diff --git a/src/DrawingRendererTest/DrawingRendererTest.csproj b/src/DrawingRendererTest/DrawingRendererTest.csproj deleted file mode 100644 index 0c0a0f3c18..0000000000 --- a/src/DrawingRendererTest/DrawingRendererTest.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - net8.0 - latest - enable - enable - True - EPPlus.Export.ImageRenderer.Tests.snk - - - - - - - - - - - - - - - - - - diff --git a/src/DrawingRendererTest/Test1.cs b/src/DrawingRendererTest/Test1.cs deleted file mode 100644 index 031d99e52a..0000000000 --- a/src/DrawingRendererTest/Test1.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace DrawingRendererTest -{ - [TestClass] - public sealed class Test1 - { - [TestMethod] - public void TestMethod1() - { - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer.Test/Chart/BarChartTests.cs b/src/EPPlus.DrawingRenderer.Tests/Chart/BarChartTests.cs similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/Chart/BarChartTests.cs rename to src/EPPlus.DrawingRenderer.Tests/Chart/BarChartTests.cs diff --git a/src/EPPlus.Export.ImageRenderer.Test/Chart/ColumnChartTests.cs b/src/EPPlus.DrawingRenderer.Tests/Chart/ColumnChartTests.cs similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/Chart/ColumnChartTests.cs rename to src/EPPlus.DrawingRenderer.Tests/Chart/ColumnChartTests.cs diff --git a/src/EPPlus.Export.ImageRenderer.Test/Chart/ComboChartTests.cs b/src/EPPlus.DrawingRenderer.Tests/Chart/ComboChartTests.cs similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/Chart/ComboChartTests.cs rename to src/EPPlus.DrawingRenderer.Tests/Chart/ComboChartTests.cs diff --git a/src/EPPlus.Export.ImageRenderer.Test/Chart/LineChartToSvgTests.cs b/src/EPPlus.DrawingRenderer.Tests/Chart/LineChartToSvgTests.cs similarity index 96% rename from src/EPPlus.Export.ImageRenderer.Test/Chart/LineChartToSvgTests.cs rename to src/EPPlus.DrawingRenderer.Tests/Chart/LineChartToSvgTests.cs index 88da23291c..52eab80bd2 100644 --- a/src/EPPlus.Export.ImageRenderer.Test/Chart/LineChartToSvgTests.cs +++ b/src/EPPlus.DrawingRenderer.Tests/Chart/LineChartToSvgTests.cs @@ -78,16 +78,16 @@ public void GenerateSvgForComboCharts_sheet4() using (var p = OpenTemplatePackage("ChartForSvg.xlsx")) { var ws = p.Workbook.Worksheets[3]; - //var ix = 1; - //var c = ws.Drawings[ix]; - //var svg = renderer.RenderDrawingToSvg(c); - //SaveTextFileToWorkbook($"svg\\ChartForSvg_sheet2_{ix++}.svg", svg); var ix = 1; - foreach (ExcelChart c in ws.Drawings) - { - var svg = c.ToSvg(); - SaveTextFileToWorkbook($"svg\\ChartForSvg_Combo_Sheet4{ix++}.svg", svg); - } + var c = ws.Drawings[ix]; + var svg = c.ToSvg(); + SaveTextFileToWorkbook($"svg\\ChartForSvg_sheet2_{ix++}.svg", svg); + //var ix = 1; + //foreach (ExcelChart c in ws.Drawings) + //{ + // var svg = c.ToSvg(); + // SaveTextFileToWorkbook($"svg\\ChartForSvg_Combo_Sheet4{ix++}.svg", svg); + //} } } [TestMethod] diff --git a/src/EPPlus.Export.ImageRenderer.Test/Chart/PieChartTests.cs b/src/EPPlus.DrawingRenderer.Tests/Chart/PieChartTests.cs similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/Chart/PieChartTests.cs rename to src/EPPlus.DrawingRenderer.Tests/Chart/PieChartTests.cs diff --git a/src/EPPlus.Export.ImageRenderer.Test/Chart/TrendlineTests.cs b/src/EPPlus.DrawingRenderer.Tests/Chart/TrendlineTests.cs similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/Chart/TrendlineTests.cs rename to src/EPPlus.DrawingRenderer.Tests/Chart/TrendlineTests.cs diff --git a/src/EPPlus.Export.ImageRenderer.Test/DrawingShapeRenderer/SvgStandAloneTests.cs b/src/EPPlus.DrawingRenderer.Tests/DrawingShapeRenderer/SvgStandAloneTests.cs similarity index 52% rename from src/EPPlus.Export.ImageRenderer.Test/DrawingShapeRenderer/SvgStandAloneTests.cs rename to src/EPPlus.DrawingRenderer.Tests/DrawingShapeRenderer/SvgStandAloneTests.cs index 11bc767dc9..f4dbcfd416 100644 --- a/src/EPPlus.Export.ImageRenderer.Test/DrawingShapeRenderer/SvgStandAloneTests.cs +++ b/src/EPPlus.DrawingRenderer.Tests/DrawingShapeRenderer/SvgStandAloneTests.cs @@ -10,6 +10,9 @@ using System.Text; using System.Threading.Tasks; using EPPlus.DrawingRenderer.RenderItems.SvgItem; +using EPPlus.Fonts.OpenType.Integration.RichText; +using EPPlus.Fonts.OpenType.Integration.DataHolders; +using System.Drawing; namespace EPPlus.Export.ImageRenderer.Tests.DrawingShapeRenderer { @@ -52,12 +55,12 @@ public void SvgRectTest() } [TestMethod] - public void SvgTextBoxTest() + public void SvgTextRun() { BoundingBox bounds = new BoundingBox(0, 0, 500, 500); StringBuilder sb = new StringBuilder(); var svgShapeRenderer = new SvgShapeRenderer(bounds, sb); - + var baseGroup = new GroupRenderItem(bounds); @@ -67,31 +70,72 @@ public void SvgTextBoxTest() background.Height = bounds.Height; background.FillColor = "aliceBlue"; - //var textBody = new RenderTextBody(baseGroup.Bounds, true); + baseGroup.AddChildItem(background); + + var rt = new RichTextFormatSimple(); + rt.Text = "My text"; + rt.UnderlineType = 1; + rt.FontColor = System.Drawing.Color.Black; + rt.Family = "Archivo Narrow"; + rt.SubFamily = OfficeOpenXml.Interfaces.Fonts.FontSubFamily.Regular; + rt.Size = 12f; + + //var paragraph = new SvgParagraphRenderItem() + + var textRun = new SvgTextRunRenderItem(baseGroup.Bounds, rt, rt.Text); + baseGroup.AddChildItem(textRun); + + + List items = new List() { baseGroup }; + svgShapeRenderer.Render(items); + + var svg = sb.ToString(); + + + SaveTextFileToWorkbook("svg\\textRunStandAlone.svg", svg); + } + + [TestMethod] + public void SvgTextBodyTest() + { + BoundingBox bounds = new BoundingBox(0, 0, 500, 500); + StringBuilder sb = new StringBuilder(); + var svgShapeRenderer = new SvgShapeRenderer(bounds, sb); - //textBody. - //textBody.Text = "Hello"; - //var para = new SvgParagraphRenderItem(textBody, textBody.Bounds); - - //var para2 = new DrawingParagraphRenderItem(textBody, textBody.Bounds); - //textBody.Paragraphs.Add + var baseGroup = new GroupRenderItem(bounds); + var background = new RectRenderItem(baseGroup.Bounds); - //para.Runs.Add() + background.Width = bounds.Width; + background.Height = bounds.Height; + background.FillColor = "aliceBlue"; - //baseGroup.AddChildItem(background); - //baseGroup.AddChildItem(textBody); + baseGroup.Bounds.Width = bounds.Width; + baseGroup.Bounds.Height = bounds.Height; + + var textBody = new SvgTextBodyRenderItem(baseGroup.Bounds, true); + var paragraph = textBody.AddParagraph("Hello"); + + paragraph.AddText(" There"); + + var rtItem = new RichTextFormatSimple("Second paragraph", "Archivo Narrow", 16f, true); + rtItem.FontColor = Color.DarkGreen; + var para2 = textBody.AddParagraph(rtItem); + + baseGroup.AddChildItem(textBody); + baseGroup.AddChildItem(background); List items = new List() { baseGroup }; + textBody.AppendRenderItems(items); svgShapeRenderer.Render(items); var svg = sb.ToString(); - SaveTextFileToWorkbook("svg\\textBoxStandAlone.svg", svg); + SaveTextFileToWorkbook("svg\\textBodyStandAlone.svg", svg); } } } diff --git a/src/EPPlus.Export.ImageRenderer.Test/EPPlus.Export.ImageRenderer.Tests.csproj b/src/EPPlus.DrawingRenderer.Tests/EPPlus.DrawingRenderer.Tests.csproj similarity index 90% rename from src/EPPlus.Export.ImageRenderer.Test/EPPlus.Export.ImageRenderer.Tests.csproj rename to src/EPPlus.DrawingRenderer.Tests/EPPlus.DrawingRenderer.Tests.csproj index 234439b818..613f6b4ecc 100644 --- a/src/EPPlus.Export.ImageRenderer.Test/EPPlus.Export.ImageRenderer.Tests.csproj +++ b/src/EPPlus.DrawingRenderer.Tests/EPPlus.DrawingRenderer.Tests.csproj @@ -6,7 +6,7 @@ enable enable True - EPPlus.Export.ImageRenderer.Tests.snk + EPPlus.DrawingRenderer.Tests.snk diff --git a/src/EPPlus.Export.ImageRenderer.Test/EPPlus.Export.ImageRenderer.Tests.snk b/src/EPPlus.DrawingRenderer.Tests/EPPlus.DrawingRenderer.Tests.snk similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/EPPlus.Export.ImageRenderer.Tests.snk rename to src/EPPlus.DrawingRenderer.Tests/EPPlus.DrawingRenderer.Tests.snk diff --git a/src/EPPlus.Export.ImageRenderer.Test/GroupItemNewTest.cs b/src/EPPlus.DrawingRenderer.Tests/GroupItemNewTest.cs similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/GroupItemNewTest.cs rename to src/EPPlus.DrawingRenderer.Tests/GroupItemNewTest.cs diff --git a/src/DrawingRendererTest/MSTestSettings.cs b/src/EPPlus.DrawingRenderer.Tests/MSTestSettings.cs similarity index 100% rename from src/DrawingRendererTest/MSTestSettings.cs rename to src/EPPlus.DrawingRenderer.Tests/MSTestSettings.cs diff --git a/src/EPPlus.Export.ImageRenderer.Test/PresetShapeDefinitionTests.cs b/src/EPPlus.DrawingRenderer.Tests/PresetShapeDefinitionTests.cs similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/PresetShapeDefinitionTests.cs rename to src/EPPlus.DrawingRenderer.Tests/PresetShapeDefinitionTests.cs diff --git a/src/EPPlus.Export.ImageRenderer.Test/Shape/ShapeToSvgTests.cs b/src/EPPlus.DrawingRenderer.Tests/Shape/ShapeToSvgTests.cs similarity index 98% rename from src/EPPlus.Export.ImageRenderer.Test/Shape/ShapeToSvgTests.cs rename to src/EPPlus.DrawingRenderer.Tests/Shape/ShapeToSvgTests.cs index 995ba255ba..48c1479b8e 100644 --- a/src/EPPlus.Export.ImageRenderer.Test/Shape/ShapeToSvgTests.cs +++ b/src/EPPlus.DrawingRenderer.Tests/Shape/ShapeToSvgTests.cs @@ -637,5 +637,20 @@ public void CreateChartsWithDifferentSize() SaveAndCleanup(p); } } + + [TestMethod] + public void SuperAndSubScript() + { + ExcelPackage.License.SetNonCommercialOrganization("EPPlus Project"); + using (var p = OpenTemplatePackage("SuperAndSubScript.xlsx")) + { + var ws = p.Workbook.Worksheets[0]; + + var currShape = ws.Drawings[0]; + + var svg = currShape.ToSvg(); + SaveTextFileToWorkbook("svg\\SuperAndSubScript.svg", svg); + } + } } } diff --git a/src/EPPlus.Export.ImageRenderer.Test/StyleTests.cs b/src/EPPlus.DrawingRenderer.Tests/StyleTests.cs similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/StyleTests.cs rename to src/EPPlus.DrawingRenderer.Tests/StyleTests.cs diff --git a/src/EPPlus.Export.ImageRenderer.Test/TestBase.cs b/src/EPPlus.DrawingRenderer.Tests/TestBase.cs similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/TestBase.cs rename to src/EPPlus.DrawingRenderer.Tests/TestBase.cs diff --git a/src/EPPlus.Export.ImageRenderer.Test/TestFontMeasurer.cs b/src/EPPlus.DrawingRenderer.Tests/TestFontMeasurer.cs similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/TestFontMeasurer.cs rename to src/EPPlus.DrawingRenderer.Tests/TestFontMeasurer.cs diff --git a/src/EPPlus.Export.ImageRenderer.Test/TextRenderTests.cs b/src/EPPlus.DrawingRenderer.Tests/TextRenderTests.cs similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/TextRenderTests.cs rename to src/EPPlus.DrawingRenderer.Tests/TextRenderTests.cs diff --git a/src/EPPlus.Export.ImageRenderer.Test/TextboxTest.cs b/src/EPPlus.DrawingRenderer.Tests/TextboxTest.cs similarity index 100% rename from src/EPPlus.Export.ImageRenderer.Test/TextboxTest.cs rename to src/EPPlus.DrawingRenderer.Tests/TextboxTest.cs diff --git a/src/EPPlus.DrawingRenderer/RenderItems/SvgItem/SvgParagraphRenderItem.cs b/src/EPPlus.DrawingRenderer/RenderItems/SvgItem/SvgParagraphRenderItem.cs index cd382a1761..72081685e5 100644 --- a/src/EPPlus.DrawingRenderer/RenderItems/SvgItem/SvgParagraphRenderItem.cs +++ b/src/EPPlus.DrawingRenderer/RenderItems/SvgItem/SvgParagraphRenderItem.cs @@ -1,17 +1,38 @@ using EPPlus.Export.ImageRenderer.RenderItems.Shared; +using EPPlus.Fonts.OpenType.Integration.DataHolders; using EPPlus.Graphics; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; + namespace EPPlus.DrawingRenderer.RenderItems.SvgItem { public class SvgParagraphRenderItem : ParagraphRenderItem { - public SvgParagraphRenderItem(RenderTextBody body, BoundingBox parent) : base(parent) + public SvgParagraphRenderItem(RenderTextBody body, BoundingBox parent, string text, bool setDefaultFont = true) : base(parent, body, text, setDefaultFont) + { + ImportStyles(); + } + public SvgParagraphRenderItem(RenderTextBody textBody, BoundingBox parent, IRichTextFormatSimple rtFormat): base(parent, textBody, rtFormat) { + ImportStyles(); + } + private void ImportStyles() + { + //Import RichText data to each run + foreach (var run in Runs) + { + var textRun = (SvgTextRunRenderItem)run; + var rtOptions = _layoutSystem.InputFragments[run.OriginalRtIdx].RichTextOptions; + if (_layoutSystem.InputFragments.Count != 0 && run.OriginalRtIdx != -1 && rtOptions is IRichTextFormatSimple) + { + textRun.ImportRichTextData((IRichTextFormatSimple)rtOptions); + } + else + { + //If not use the default for the whole paragraph (potentially user specified) + run.ImportFontData(DefaultParagraphFont); + } + } } public override RenderItemType Type => RenderItemType.Paragraph; diff --git a/src/EPPlus.DrawingRenderer/RenderItems/SvgItem/SvgTextBodyRenderItem.cs b/src/EPPlus.DrawingRenderer/RenderItems/SvgItem/SvgTextBodyRenderItem.cs new file mode 100644 index 0000000000..70ef9e714e --- /dev/null +++ b/src/EPPlus.DrawingRenderer/RenderItems/SvgItem/SvgTextBodyRenderItem.cs @@ -0,0 +1,29 @@ +using EPPlus.Export.ImageRenderer.RenderItems.Shared; +using EPPlus.Fonts.OpenType.Integration.DataHolders; +using EPPlus.Graphics; + + +namespace EPPlus.DrawingRenderer.RenderItems.SvgItem +{ + public class SvgTextBodyRenderItem : RenderTextBody + { + public SvgTextBodyRenderItem(BoundingBox parent, bool autoSize) : base(parent, autoSize) + { + } + + public SvgTextBodyRenderItem(BoundingBox parent, double left, double top, double maxWidth, double maxHeight, bool clampedToParent = false, bool autoSize = false) : base(parent, left, top, maxWidth, maxHeight, clampedToParent, autoSize) + { + + } + + protected override ParagraphRenderItem CreateParagraph(BoundingBox parent, string textIfEmpty = "") + { + return new SvgParagraphRenderItem(this, parent, textIfEmpty); + } + + protected override ParagraphRenderItem CreateParagraph(BoundingBox parent, IRichTextFormatSimple richText) + { + return new SvgParagraphRenderItem(this, parent, richText); + } + } +} diff --git a/src/EPPlus.DrawingRenderer/RenderItems/SvgItem/SvgTextRunRenderItem.cs b/src/EPPlus.DrawingRenderer/RenderItems/SvgItem/SvgTextRunRenderItem.cs index cbd24475fe..e681ac1eef 100644 --- a/src/EPPlus.DrawingRenderer/RenderItems/SvgItem/SvgTextRunRenderItem.cs +++ b/src/EPPlus.DrawingRenderer/RenderItems/SvgItem/SvgTextRunRenderItem.cs @@ -9,7 +9,7 @@ namespace EPPlus.DrawingRenderer.RenderItems.SvgItem { - internal class SvgTextRunRenderItem : TextRunRenderItem + public class SvgTextRunRenderItem : TextRunRenderItem { public SvgTextRunRenderItem(BoundingBox parent) : base(parent) { diff --git a/src/EPPlus.DrawingRenderer/RenderItems/Textbox/ParagraphRenderItem.cs b/src/EPPlus.DrawingRenderer/RenderItems/Textbox/ParagraphRenderItem.cs index f2a773515b..2211eeb9ab 100644 --- a/src/EPPlus.DrawingRenderer/RenderItems/Textbox/ParagraphRenderItem.cs +++ b/src/EPPlus.DrawingRenderer/RenderItems/Textbox/ParagraphRenderItem.cs @@ -79,19 +79,19 @@ public abstract class ParagraphRenderItem : RenderItem protected LayoutSystem _layoutSystem; - protected List _textFragments; + protected RichTextCollectionBase _textFragments = new RichTextCollectionBase(); public double ParagraphLineSpacing { get; protected set; } public TextAlignment HorizontalAlignment { get; protected set; } public List Runs { get; set; } = new List(); public TextLineCollection Lines { get; protected set; } public bool DisplayBounds { get; set; } = false; - public MeasurementFont ParagraphFont; + public override RenderItemType Type => RenderItemType.Paragraph; public bool AutoSize = false; - public OpenTypeFontInfoBase DefaultParagraphFont; + public FontFormatBase DefaultParagraphFont; protected double ParentMaxWidth; protected double ParentMaxHeight; @@ -100,6 +100,8 @@ public abstract class ParagraphRenderItem : RenderItem protected double? _lsMultiplier = null; + protected bool TextIfEmptyIsNull { get; set; } + protected bool LinespacingIsExact { get @@ -109,26 +111,36 @@ protected bool LinespacingIsExact } protected TextLineSpacing _lsType; - protected double? _centerAdjustment; - - protected ParagraphRenderItem(BoundingBox parent) : base(parent) + protected ParagraphRenderItem(BoundingBox parent, bool setFallbackDefaultFont = true) : base(parent) { Bounds.Name = "Paragraph"; + if (setFallbackDefaultFont) + { + var defaultFont = new MeasurementFont { FontFamily = "Aptos Narrow", Size = 11, Style = MeasurementFontStyles.Regular }; + DefaultParagraphFont = new FontFormatBase(defaultFont); + FillColor = "black"; + } } - protected ParagraphRenderItem(BoundingBox parent, RenderTextBody textBody, bool setFallbackDefaultFont = true) : base(parent) + protected ParagraphRenderItem(BoundingBox parent, RenderTextBody textBody, bool setFallbackDefaultFont = true) : this(parent, setFallbackDefaultFont) { - SetParentProps(textBody); + InitBasedOnParent(textBody); Bounds.Name = "Paragraph"; + } - if(setFallbackDefaultFont) - { - var defaultFont = new MeasurementFont { FontFamily = "Aptos Narrow", Size = 11, Style = MeasurementFontStyles.Regular }; - ParagraphFont = defaultFont; - DefaultParagraphFont = new OpenTypeFontInfoBase(ParagraphFont); - } + protected ParagraphRenderItem(BoundingBox parent, RenderTextBody textBody, string text, bool setFallbackDefaultFont = true) : this(parent, textBody, setFallbackDefaultFont) + { + _lsMultiplier = 1d; + ImportLinesAndTextRunsBase(text); + } + + protected ParagraphRenderItem(BoundingBox parent, RenderTextBody textBody, IRichTextFormatSimple rtFormat) : this(parent, textBody, false) + { + _lsMultiplier = 1d; + DefaultParagraphFont = new FontFormatBase(rtFormat.Family, rtFormat.SubFamily, rtFormat.Size); + AddRichText(rtFormat); } protected double GetAlignmentHorizontal(TextAlignment txAlignment) @@ -152,7 +164,7 @@ protected double GetAlignmentHorizontal(TextAlignment txAlignment) } - void SetParentProps(RenderTextBody textBody) + void InitBasedOnParent(RenderTextBody textBody) { ParentTextBody = textBody; ParentMaxWidth = textBody.MaxWidth; @@ -162,18 +174,76 @@ void SetParentProps(RenderTextBody textBody) TextLineCollection WrapFragmentsToLines(List? fragments = null) { - if (fragments == null && _layoutSystem == null) + //This is highly innefficent. Really, LayoutSystem should be + //Holding the fragments from the start/wrapping should only be done when textFragments are fully complete + _layoutSystem = new LayoutSystem(_textFragments); + + //if (fragments == null && _layoutSystem == null) + //{ + // _layoutSystem = new LayoutSystem(_textFragments); + //} + + double maxWidthInPoints; + if(AutoSize) { - fragments = _textFragments; - _layoutSystem = new LayoutSystem(fragments); + maxWidthInPoints = Math.Round(ParentMaxWidth - RightMargin - LeftMargin, 0, MidpointRounding.AwayFromZero); } - var maxWidthPoints = Math.Round(ParentMaxWidth, 0, MidpointRounding.AwayFromZero); - return _layoutSystem.Wrap(maxWidthPoints); + else + { + maxWidthInPoints = Bounds.Width; + } + return _layoutSystem.Wrap(maxWidthInPoints); } - protected void AddTextLinesAndSpacing(string textIfEmpty) + private void AddRichTextBase(IRichTextFormatSimple rt) + { + if (_textFragments == null) + { + _textFragments = new RichTextCollectionBase(); + } + + if (string.IsNullOrEmpty(rt.Text) == false) + { + _textFragments.Add(rt); + } + } + + protected void AddDefaultTextFragment(string text) + { + var defaults = new RichTextFormatSimple(); + defaults.Text = text; + defaults.SetFont(DefaultParagraphFont); + + AddRichTextBase(defaults); + } + + protected void ImportLinesAndTextRunsBase(string textIfEmpty) + { + if(string.IsNullOrEmpty(textIfEmpty)) + { + TextIfEmptyIsNull = true; + } + else + { + TextIfEmptyIsNull = false; + } + + AddDefaultTextFragment(textIfEmpty); + + Bounds.Left = GetAlignmentHorizontal(HorizontalAlignment); + if (HorizontalAlignment == TextAlignment.Center) + { + _centerAdjustment = GetAlignmentHorizontal(HorizontalAlignment); + } + + WrapTextFragmentsAndGenerateTextRuns(); + + } + + protected void WrapTextFragmentsAndGenerateTextRuns() { Lines = WrapFragmentsToLines(); + Runs.Clear(); //In points double widthOfLargestLine = 0; @@ -192,7 +262,7 @@ protected void AddTextLinesAndSpacing(string textIfEmpty) widthOfLargestLine = Lines.LargestWidthWithoutSpace; combinedHeight = Lines.GetHeightOfCollection(_lsMultiplier, lineSpacingResult); - SetHorizontalAlignment(widthOfLargestLine, string.IsNullOrEmpty(textIfEmpty)); + SetHorizontalAlignment(widthOfLargestLine); int lineIdx = 0; foreach (var line in Lines) @@ -246,9 +316,9 @@ protected double CalculatePrevWidthBasedOnAlignment(double lineDist) return prevWidth; } - protected void SetHorizontalAlignment(double widthOfLargestLine, bool textIfEmptyIsNull) + protected void SetHorizontalAlignment(double widthOfLargestLine) { - if (HorizontalAlignment == TextAlignment.Center && AutoSize && _centerAdjustment != null && textIfEmptyIsNull) + if (HorizontalAlignment == TextAlignment.Center && AutoSize && _centerAdjustment != null && TextIfEmptyIsNull) { //Bounds of the paragraph should be bounds of the text itself. //Therefore we must know the starting point to set accurate left and offset from left. @@ -262,6 +332,17 @@ protected void SetHorizontalAlignment(double widthOfLargestLine, bool textIfEmpt } } + public void AddRichText(IRichTextFormatSimple richText) + { + AddRichTextBase(richText); + WrapTextFragmentsAndGenerateTextRuns(); + } + + public void AddText(string text) + { + ImportLinesAndTextRunsBase(text); + } + protected abstract TextRunRenderItem CreateTextRun(BoundingBox parent, string displayText, int origRtIdx); } } diff --git a/src/EPPlus.DrawingRenderer/RenderItems/Textbox/RenderTextBody.cs b/src/EPPlus.DrawingRenderer/RenderItems/Textbox/RenderTextBody.cs index f91437e3d1..204b101fbe 100644 --- a/src/EPPlus.DrawingRenderer/RenderItems/Textbox/RenderTextBody.cs +++ b/src/EPPlus.DrawingRenderer/RenderItems/Textbox/RenderTextBody.cs @@ -1,4 +1,5 @@ using EPPlus.Export.ImageRenderer.RenderItems.Shared; +using EPPlus.Fonts.OpenType.Integration.DataHolders; using EPPlus.Graphics; namespace EPPlus.DrawingRenderer.RenderItems @@ -110,12 +111,24 @@ public void AppendRenderItems(List renderItems) } } - public void AddParagraph(double startingY, string text = null) + public ParagraphRenderItem AddParagraph(IRichTextFormatSimple rtFormat) + { + var paragraph = CreateParagraph(Bounds, rtFormat); + AdjustAndAddParagraph(paragraph); + return paragraph; + } + + public ParagraphRenderItem AddParagraph(string text = null) { var paragraph = CreateParagraph(Bounds, text); + AdjustAndAddParagraph(paragraph); + return paragraph; + } + + private void AdjustAndAddParagraph(ParagraphRenderItem paragraph) + { paragraph.Bounds.Name = $"Container{Paragraphs.Count}"; - paragraph.Bounds.Top = startingY; - Text = text; + paragraph.Bounds.Top = GetTopToAddNextParagraphAt(); if (AutoSize) { @@ -136,6 +149,46 @@ public void AddParagraph(double startingY, string text = null) Paragraphs.Add(paragraph); } + private double GetTopToAddNextParagraphAt() + { + double paragraphTop = 0; + + if (Paragraphs.Count != 0) + { + paragraphTop = Paragraphs.Last().Bounds.Bottom; + } + return paragraphTop; + } + + + /// + /// Get the start of text space vertically + /// + /// + protected double GetAlignmentVertical() + { + double alignmentY = 0; + + switch (VerticalAlignment) + { + case TextAnchoringType.Top: + alignmentY = Bounds.Top; + break; + //Center means center of a Shape's ENTIRE bounding box height. + //Not center of the Inset GetRectangle + case TextAnchoringType.Center: + alignmentY = (MaxHeight - Bounds.Height) / 2 + Bounds.Top; + break; + case TextAnchoringType.Bottom: + alignmentY = MaxHeight - Bounds.Height; + break; + } + + return alignmentY; + } + protected abstract ParagraphRenderItem CreateParagraph(BoundingBox parent, string textIfEmpty = ""); + + protected abstract ParagraphRenderItem CreateParagraph(BoundingBox parent, IRichTextFormatSimple richText); } } diff --git a/src/EPPlus.DrawingRenderer/RenderItems/Textbox/RichTextFormatDrawing/IRichTextFormatDrawing.cs b/src/EPPlus.DrawingRenderer/RenderItems/Textbox/RichTextFormatDrawing/IRichTextFormatDrawing.cs new file mode 100644 index 0000000000..548a9ab0ec --- /dev/null +++ b/src/EPPlus.DrawingRenderer/RenderItems/Textbox/RichTextFormatDrawing/IRichTextFormatDrawing.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using EPPlus.Fonts.OpenType.Integration.DataHolders; +using EPPlus.Export.ImageRenderer.RenderItems.Shared; +using System.Drawing; + +namespace EPPlus.DrawingRenderer.RenderItems.Textbox +{ + /// + /// TODO: Move this to interfaces. Only here in order to not break existing references in PDF + /// (This should be moved when IRichTextFormatSimple is moved) + /// + /// Rich text data for drawings + /// + public interface IRichTextFormatDrawing : IRichTextFormatSimple + { + public new eDrawingStrikeType StrikeType { get; set; } /*{ get { return (DrawingStrikeType)StrikeType; } set { StrikeType = (int)value; } }*/ + public new eDrawingUnderLineType UnderlineType { get; set; } + Color? HighLightColor { get; set; } + /// + /// The spacing between characters within a text run. + /// + double Spacing { get; set; } + + /// + /// +Superscript or -Subscript offset in percent + /// (default 30% Super and -25% subscript) + /// + public double Baseline { get; set; } + + //TODO: Advanced fills/Textoutline + //TODO: Effects once implemented in Epplus + } +} diff --git a/src/EPPlus.DrawingRenderer/RenderItems/Textbox/RichTextFormatDrawing/RichTextFormatDrawing.cs b/src/EPPlus.DrawingRenderer/RenderItems/Textbox/RichTextFormatDrawing/RichTextFormatDrawing.cs new file mode 100644 index 0000000000..07c5e253e9 --- /dev/null +++ b/src/EPPlus.DrawingRenderer/RenderItems/Textbox/RichTextFormatDrawing/RichTextFormatDrawing.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using EPPlus.Export.ImageRenderer.RenderItems.Shared; +using EPPlus.Fonts.OpenType.Integration.DataHolders; + +namespace EPPlus.DrawingRenderer.RenderItems.Textbox +{ + /// + /// Default richText data class for drawings + /// + public class RichTextFormatDrawing : RichTextFormatSimple, IRichTextFormatDrawing + { + public new eDrawingStrikeType StrikeType { get => (eDrawingStrikeType)base.StrikeType; set => base.StrikeType = (int)value; } + public new eDrawingUnderLineType UnderlineType { get => (eDrawingUnderLineType)base.UnderlineType; set => base.UnderlineType = (int)value; } + + public Color? HighLightColor { get; set; } + public double Spacing { get; set; } = 0d; + + double _baseLine = 0d; + + private bool _subScript = false; + private bool _superScript = false; + + /// + /// +Superscript or -Subscript offset in percent + /// (default 30% Super and -25% subscript) + /// + public double Baseline + { + get + { + return _baseLine; + } + set + { + if (value > 0d) + { + _superScript = true; + _subScript = false; + } + else if (value < 0d) + { + _superScript = false; + _subScript = true; + } + else + { + //When offset is 0 it is neither a sub or super script + _superScript = false; + _subScript = false; + } + + _baseLine = value; + } + } + public new bool SubScript + { + get + { + return _subScript; + } + set + { + if (value == true) + { + _superScript = false; + Baseline = -25d; + } + _subScript = value; + } + } + + public new bool SuperScript + { + get + { + return _superScript; + } + set + { + if (value == true) + { + _subScript = false; + Baseline = 30d; + } + _superScript = value; + } + } + + public RichTextFormatDrawing() : base() + { + StrikeType = eDrawingStrikeType.No; + UnderlineType = eDrawingUnderLineType.None; + } + + public RichTextFormatDrawing(string text, string fontFamily, float size, bool bold = false, bool italic = false) : base(text, fontFamily, size, bold, italic) + { + StrikeType = eDrawingStrikeType.No; + UnderlineType = eDrawingUnderLineType.None; + } + } +} diff --git a/src/EPPlus.DrawingRenderer/RenderItems/Textbox/TextRunRenderItem.cs b/src/EPPlus.DrawingRenderer/RenderItems/Textbox/TextRunRenderItem.cs index 8a9cc4ba86..f9e0793632 100644 --- a/src/EPPlus.DrawingRenderer/RenderItems/Textbox/TextRunRenderItem.cs +++ b/src/EPPlus.DrawingRenderer/RenderItems/Textbox/TextRunRenderItem.cs @@ -7,13 +7,14 @@ using OfficeOpenXml.Interfaces.RichText; using System.Drawing; using System.Text.RegularExpressions; +using EPPlusImageRenderer.Utils; namespace EPPlus.Export.ImageRenderer.RenderItems.Shared { /// /// Linestyle /// - public enum UnderLineType + public enum eDrawingUnderLineType { /// /// Dashed @@ -91,7 +92,7 @@ public enum UnderLineType /// /// BulletType of font strike /// - public enum StrikeType + public enum eDrawingStrikeType { /// /// Double-lined font strike @@ -118,14 +119,14 @@ public abstract class TextRunRenderItem : RenderItem public IFontFormatBase _measurementFont { get; internal protected set; } protected bool _isFirstInParagraph; - public double FontSizeInPixels { get; protected set; } + public double FontSizeInPixels { get; protected set; } public List Lines { get; set; } protected internal bool _isItalic = false; protected internal bool _isBold = false; - protected internal UnderLineType _underLineType = UnderLineType.None; - protected internal StrikeType _strikeType; + protected internal eDrawingUnderLineType _underLineType = eDrawingUnderLineType.None; + protected internal eDrawingStrikeType _strikeType; protected internal Color _underlineColor; protected internal double _baseline; @@ -148,6 +149,12 @@ public void ImportFontData(IFontFormatBase font) InitializeBase(font); } + public void ImportRichTextData(IRichTextFormatSimple rt) + { + InitializeBase(rt); + FillColor = "#" + rt.FontColor.To6CharHexStringImage(); + } + internal protected void InitializeBase(IFontFormatBase font) { //Should be ascent-only? diff --git a/src/EPPlus.DrawingRenderer/Svg/SvgBaseRenderer.cs b/src/EPPlus.DrawingRenderer/Svg/SvgBaseRenderer.cs index c27b9f1876..3bf5da9712 100644 --- a/src/EPPlus.DrawingRenderer/Svg/SvgBaseRenderer.cs +++ b/src/EPPlus.DrawingRenderer/Svg/SvgBaseRenderer.cs @@ -10,9 +10,14 @@ protected SvgBaseRenderer(StringBuilder outputStream) : base(outputStream) { } - protected void RenderBase(T item) + + /// + /// Used if you wish to render base to a different string builder first + /// + /// + /// + protected void RenderBaseToSpecified(T item, StringBuilder sb) { - var sb = OutputStream; if (item.Bounds.Name != null) { sb.Append($" id=\"{item.Bounds.Name}\" "); @@ -69,6 +74,12 @@ protected void RenderBase(T item) sb.Append($"stroke-miterlimit =\"{item.StrokeMiterLimit}\" "); } } + + protected void RenderBase(T item) + { + var sb = OutputStream; + RenderBaseToSpecified(item, sb); + } protected void RenderCompoundItems(T li, double? borderWidth, string color, string filter) { var tmpBorderWidth = li.BorderWidth; diff --git a/src/EPPlus.DrawingRenderer/Svg/SvgParagraphRenderer.cs b/src/EPPlus.DrawingRenderer/Svg/SvgParagraphRenderer.cs index 1f79dbe911..f4944d5223 100644 --- a/src/EPPlus.DrawingRenderer/Svg/SvgParagraphRenderer.cs +++ b/src/EPPlus.DrawingRenderer/Svg/SvgParagraphRenderer.cs @@ -19,7 +19,7 @@ public SvgParagraphRenderer(IBasicIShapesRenderer shapeRenderer, public override void Render(ParagraphRenderItem item) { var sb = OutputStream; - var fontSize = item.ParagraphFont.Size.PointToPixel().ToString(CultureInfo.InvariantCulture); + var fontSize = item.DefaultParagraphFont.Size.PointToPixel().ToString(CultureInfo.InvariantCulture); sb.AppendLine($""); @@ -100,7 +100,7 @@ public override void Render(ParagraphRenderItem item) RenderBase(item); sb.Append(/*$"{GetHorizontalAlignmentAttribute(Bounds.X)} y=\"{Bounds.Y}\" " +*/ - $"font-family=\"{item.ParagraphFont.FontFamily},{item.ParagraphFont.FontFamily}_MSFontService,sans-serif\" " + + $"font-family=\"{item.DefaultParagraphFont.Family},{item.DefaultParagraphFont.Family}_MSFontService,sans-serif\" " + $"font-size=\"{fontSize.ToString(CultureInfo.InvariantCulture)}px\" >"); if (item.Runs != null && item.Runs.Count > 0) diff --git a/src/EPPlus.DrawingRenderer/Svg/SvgTextRunRenderer.cs b/src/EPPlus.DrawingRenderer/Svg/SvgTextRunRenderer.cs index 02f810a740..9078ceddc2 100644 --- a/src/EPPlus.DrawingRenderer/Svg/SvgTextRunRenderer.cs +++ b/src/EPPlus.DrawingRenderer/Svg/SvgTextRunRenderer.cs @@ -4,6 +4,7 @@ using EPPlus.Fonts.OpenType.Utils; using EPPlusImageRenderer.RenderItems; using OfficeOpenXml.Interfaces.Drawing.Text; +using OfficeOpenXml.Utils; using System.Globalization; using System.Text; @@ -11,6 +12,8 @@ namespace EPPlus.DrawingRenderer.Svg { internal class SvgTextRunRenderer : SvgBaseRenderer { + string UnderlineColorString = string.Empty; + public SvgTextRunRenderer(StringBuilder outputStream) : base(outputStream) { @@ -27,16 +30,18 @@ string GetFontStyleAttributes(TextRunRenderItem textRun) { fontStyleAttributes += "font-weight=\"bold\" "; } - if (textRun._underLineType != UnderLineType.None | textRun._strikeType != StrikeType.No) - { - fontStyleAttributes += "text-decoration=\" "; - if (textRun._underLineType != UnderLineType.None) + string content = ""; + + if (textRun._underLineType != eDrawingUnderLineType.None | textRun._strikeType != eDrawingStrikeType.No) + { + string underlineContent = ""; + if (textRun._underLineType != eDrawingUnderLineType.None) { switch (textRun._underLineType) { - case UnderLineType.Single: - fontStyleAttributes += "underline"; + case eDrawingUnderLineType.Single: + underlineContent += "underline"; break; //These are all css only apparently //case eUnderLineType.Double: @@ -52,30 +57,49 @@ string GetFontStyleAttributes(TextRunRenderItem textRun) // fontStyleAttributes += "wavy"; // break; default: - fontStyleAttributes += "underline"; + underlineContent += "underline"; break; //throw new NotImplementedException("Not implemented yet"); } + if(textRun._underlineColor != System.Drawing.Color.Empty) + { + UnderlineColorString = $"{{1}}"; + } + content += underlineContent; + //Underline color + //We can change the underline color using double tspans + // SVG with a colored underlinesection. } - if (textRun._strikeType == StrikeType.Single) + if (textRun._strikeType == eDrawingStrikeType.Single) { //Has to check if Both underline and strike - if (textRun._underLineType != UnderLineType.None) + if (textRun._underLineType != eDrawingUnderLineType.None) { - fontStyleAttributes += ","; + content += ","; } - fontStyleAttributes += "line-through"; + content += "line-through"; } - fontStyleAttributes += "\" "; + if(string.IsNullOrEmpty(content) == false) + { + if (string.IsNullOrEmpty(UnderlineColorString)) + { + fontStyleAttributes += $" text-decoration=\"{content}\" "; + } + else + { + UnderlineColorString = string.Format(UnderlineColorString, $"text-decoration: {content};", "{0}"); + } + } } - return fontStyleAttributes; } public override void Render(TextRunRenderItem textRun) { + var sbStartidx = OutputStream.Length -1; + string finalString = ""; var xString = $"x =\"{(textRun.Bounds.Left.PointToPixelString())}\" "; @@ -84,9 +108,12 @@ public override void Render(TextRunRenderItem textRun) string visibility = ""; double fontSize = textRun.FontSizeInPixels; + //var baseLine = -textRun._baseline / 100D; if (textRun._baseline != 0) { - finalString += $" dy=\"{(fontSize.PointToPixel() * -textRun._baseline / 100D).ToString(CultureInfo.InvariantCulture)}px\" "; //For sub/superscript, move the text up/down by baseline% of font size. Negative value moves up, positive moves down. + //For sub/superscript, move the text up/down by baseline% of font size. Negative value moves up, positive moves down. + finalString += $" dy=\"{(fontSize.PointToPixel() * -textRun._baseline / 100D).ToString(CultureInfo.InvariantCulture)}px\" "; + //fontSize *= (1 - Math.Abs(baseLine)); } finalString += xString; var yString = $" y=\"{textRun.YPosition.PointToPixelString()}px\" "; @@ -101,6 +128,7 @@ public override void Render(TextRunRenderItem textRun) finalString += visibility; finalString += $"{GetFontStyleAttributes(textRun)}"; + if (textRun._measurementFont != null) { finalString += $"font-family=\"{textRun._measurementFont.Family}," @@ -108,19 +136,43 @@ public override void Render(TextRunRenderItem textRun) + $"font-size=\"{fontSize.ToString(CultureInfo.InvariantCulture)}px\" "; } - var sb = OutputStream; + if(string.IsNullOrEmpty(textRun.FillColor) == false) + { + finalString += $" style=\"fill: {textRun.FillColor};\" "; + } + + //Avoid rendering fill color as we do so via style + var temp = textRun.FillColor; + textRun.FillColor = null; + + var sb = new StringBuilder(); sb.Append(finalString); + //Get color etc. - //Renders up until this point - RenderBase(textRun); + //Renders up until this point (must be done to end the attribute addings so that text content can then be added) + RenderBaseToSpecified(textRun, sb); + + textRun.FillColor = temp; + //Since final string has been written in base.render erase it. finalString = ""; finalString += ">"; + //Add actual text content finalString += textRun._currentText; finalString += ""; - sb.Append(finalString); + + var textRunString = sb.ToString(); + + //Wrap in another tspan to apply underline color if neccesary + if(string.IsNullOrEmpty(UnderlineColorString) == false) + { + textRunString = string.Format(UnderlineColorString, textRunString); + } + + OutputStream.Append(textRunString); + UnderlineColorString = string.Empty; } } } diff --git a/src/EPPlus.DrawingRenderer/Utils/ColorUtils.cs b/src/EPPlus.DrawingRenderer/Utils/ColorUtils.cs index 9254048e40..ee432122e7 100644 --- a/src/EPPlus.DrawingRenderer/Utils/ColorUtils.cs +++ b/src/EPPlus.DrawingRenderer/Utils/ColorUtils.cs @@ -17,6 +17,11 @@ namespace EPPlusImageRenderer.Utils { internal static class ColorUtils { + internal static string To6CharHexStringImage(this Color color) + { + return (color.ToArgb() & 0xFFFFFF).ToString("x6"); + } + internal static Color GetAdjustedColor(PathFillMode fillColorSource, Color fc) { switch (fillColorSource) diff --git a/src/EPPlus.Export.ImageRenderer.Test/MSTestSettings.cs b/src/EPPlus.Export.ImageRenderer.Test/MSTestSettings.cs deleted file mode 100644 index aaf278c844..0000000000 --- a/src/EPPlus.Export.ImageRenderer.Test/MSTestSettings.cs +++ /dev/null @@ -1 +0,0 @@ -[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/src/EPPlus.Export.ImageRenderer/AdjustmentPointType.cs b/src/EPPlus.Export.ImageRenderer/AdjustmentPointType.cs deleted file mode 100644 index 1a30b41bd8..0000000000 --- a/src/EPPlus.Export.ImageRenderer/AdjustmentPointType.cs +++ /dev/null @@ -1,22 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -namespace EPPlusImageRenderer -{ - internal enum AdjustmentPointType - { - None = 0, - Linear = 1, - Radial = 2, - Angle = 3 - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/AdjustmentType.cs b/src/EPPlus.Export.ImageRenderer/AdjustmentType.cs deleted file mode 100644 index e79291dde6..0000000000 --- a/src/EPPlus.Export.ImageRenderer/AdjustmentType.cs +++ /dev/null @@ -1,23 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -namespace EPPlusImageRenderer -{ - internal enum AdjustmentType - { - AdjustToWidthHeight = 0, - LockToAdjustMin = 1, - LockToAdjustMax = 2, - Adjust = 255, - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Adjustments/ActionButtonBackPrevious.xml b/src/EPPlus.Export.ImageRenderer/Adjustments/ActionButtonBackPrevious.xml deleted file mode 100644 index 2338a37312..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Adjustments/ActionButtonBackPrevious.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/src/EPPlus.Export.ImageRenderer/Adjustments/ActionButtonBeginning.xml b/src/EPPlus.Export.ImageRenderer/Adjustments/ActionButtonBeginning.xml deleted file mode 100644 index 7c0c5e654d..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Adjustments/ActionButtonBeginning.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/EPPlus.Export.ImageRenderer/Adjustments/ActionButtonDocument.xml b/src/EPPlus.Export.ImageRenderer/Adjustments/ActionButtonDocument.xml deleted file mode 100644 index 95c4900fa9..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Adjustments/ActionButtonDocument.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/src/EPPlus.Export.ImageRenderer/Adjustments/ActionButtonHelp.xml b/src/EPPlus.Export.ImageRenderer/Adjustments/ActionButtonHelp.xml deleted file mode 100644 index 6151d199f4..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Adjustments/ActionButtonHelp.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/EPPlus.Export.ImageRenderer/Adjustments/Donut.xml b/src/EPPlus.Export.ImageRenderer/Adjustments/Donut.xml deleted file mode 100644 index f732c7cc72..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Adjustments/Donut.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Adjustments/DownArrow.xml b/src/EPPlus.Export.ImageRenderer/Adjustments/DownArrow.xml deleted file mode 100644 index 05947b7a8b..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Adjustments/DownArrow.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Adjustments/LeftArrow.xml b/src/EPPlus.Export.ImageRenderer/Adjustments/LeftArrow.xml deleted file mode 100644 index 931ab260a5..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Adjustments/LeftArrow.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Adjustments/RightArrow.xml b/src/EPPlus.Export.ImageRenderer/Adjustments/RightArrow.xml deleted file mode 100644 index c06e41dcf4..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Adjustments/RightArrow.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Adjustments/Trapezoid.xml b/src/EPPlus.Export.ImageRenderer/Adjustments/Trapezoid.xml deleted file mode 100644 index ae43f26b10..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Adjustments/Trapezoid.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Adjustments/Triangle.xml b/src/EPPlus.Export.ImageRenderer/Adjustments/Triangle.xml deleted file mode 100644 index 3f4eb045ae..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Adjustments/Triangle.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Adjustments/UpArrow.xml b/src/EPPlus.Export.ImageRenderer/Adjustments/UpArrow.xml deleted file mode 100644 index cc7053524a..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Adjustments/UpArrow.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Constants/PatternArrays.cs b/src/EPPlus.Export.ImageRenderer/Constants/PatternArrays.cs deleted file mode 100644 index 673c9a0959..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Constants/PatternArrays.cs +++ /dev/null @@ -1,403 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -namespace EPPlusImageRenderer.Constants -{ - internal static class PatternArrays - { - - internal static readonly short[][] Pct30 = new short[][] - { - new short[] { 0, 0, 0, 1 }, - new short[] { 1, 0, 1, 0 }, - new short[] { 0, 1, 0, 0 }, - new short[] { 1, 0, 1, 0 } - }; - - internal static readonly short[][] Pct40 = new short[][] - { - new short[] { 0, 0, 0, 1, 0, 1, 0, 1 }, - new short[] { 1, 0, 1, 0, 1, 0, 1, 0 }, - new short[] { 0, 1, 0, 1, 0, 1, 0, 1 }, - new short[] { 1, 0, 1, 0, 1, 0, 1, 0 }, - new short[] { 0, 1, 0, 1, 0, 0, 0, 1 }, - new short[] { 1, 0, 1, 0, 1, 0, 1, 0 }, - new short[] { 0, 1, 0, 1, 0, 1, 0, 1 }, - new short[] { 1, 0, 1, 0, 1, 0, 1, 0 } - }; - - internal static readonly short[][] Pct50 = new short[][] - { - new short[] { 1, 1, 1, 0 }, - new short[] { 0, 1, 0, 1 }, - new short[] { 1, 0, 1, 1 }, - new short[] { 0, 1, 0, 1 } - }; - - internal static readonly short[][] Pct60 = new short[][] - { - new short[] { 1, 1, 0, 1 }, - new short[] { 0, 1, 1, 1 } - }; - - internal static readonly short[][] Pct70 = new short[][] - { - new short[] { 1, 1, 0, 1 }, - new short[] { 1, 1, 1, 1 }, - new short[] { 0, 1, 1, 1 }, - new short[] { 1, 1, 1, 1 } - }; - - - internal static readonly short[][] LtHorz = new short[][] { - new short[] { 1 }, - new short[] { 0 }, - new short[] { 0 }, - new short[] { 0 } - }; - - internal static readonly short[][] LtVert = new short[][] { - new short[] { 1, 0, 0, 0 } - }; - - internal static readonly short[][] LtUpDiag = new short[][] { - new short[] { 0, 0, 0, 1 }, - new short[] { 0, 0, 1, 0 }, - new short[] { 0, 1, 0, 0 }, - new short[] { 1, 0, 0, 0 } - }; - - internal static readonly short[][] LtDnDiag = new short[][] { - new short[] { 1, 0, 0, 0 }, - new short[] { 0, 1, 0, 0 }, - new short[] { 0, 0, 1, 0 }, - new short[] { 0, 0, 0, 1 } - }; - - internal static readonly short[][] DkVert = new short[][] { - new short[] { 1, 1, 0, 0 } - }; - - internal static readonly short[][] DkHorz = new short[][] { - new short[] { 1 }, - new short[] { 1 }, - new short[] { 0 }, - new short[] { 0 } - }; - - - internal static readonly short[][] DkUpDiag = new short[][] { - new short[] { 1, 0, 0, 1 }, - new short[] { 0, 0, 1, 1 }, - new short[] { 0, 1, 1, 0 }, - new short[] { 1, 1, 0, 0 } - }; - - internal static readonly short[][] DkDnDiag = new short[][] { - new short[] { 1, 0, 0, 1 }, - new short[] { 1, 1, 0, 0 }, - new short[] { 0, 1, 1, 0 }, - new short[] { 0, 0, 1, 1 } - }; - - internal static readonly short[][] WdUpDiag = new short[][] { - new short[] { 1, 1, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 0, 0, 0, 0, 0, 0, 1, 1 }, - new short[] { 0, 0, 0, 0, 0, 1, 1, 0 }, - new short[] { 0, 0, 0, 0, 1, 1, 0, 0 }, - new short[] { 0, 0, 0, 1, 1, 0, 0, 0 }, - new short[] { 0, 0, 1, 1, 0, 0, 0, 0 }, - new short[] { 0, 1, 1, 0, 0, 0, 0, 0 } - }; - - internal static readonly short[][] WdDnDiag = new short[][] { - new short[] { 1, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 1, 1, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 1, 1, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 1, 1, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 1, 1, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 1, 1, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 1, 1, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 1, 1 } - }; - - internal static readonly short[][] NarVert = new short[][] { - new short[] { 1, 0 } - }; - - internal static readonly short[][] NarHorz = new short[][] { - new short[] { 1 }, - new short[] { 0 } - }; - - - internal static readonly short[][] Vert = new short[][] { - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 } - }; - - internal static readonly short[][] Horz = new short[][] { - new short[] { 1 }, - new short[] { 0 }, - new short[] { 0 }, - new short[] { 0 }, - new short[] { 0 }, - new short[] { 0 }, - new short[] { 0 }, - new short[] { 0 } - }; - - internal static readonly short[][] DashDnDiag = new short[][] { - new short[] { 1, 0, 0, 0, 1, 0, 0, 0 }, - new short[] { 0, 1, 0, 0, 0, 1, 0, 0 }, - new short[] { 0, 0, 1, 0, 0, 0, 1, 0 }, - new short[] { 0, 0, 0, 1, 0, 0, 0, 1 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 } - }; - - internal static readonly short[][] DashUpDiag = new short[][] { - new short[] { 0, 1, 0, 0, 0, 1, 0, 0 }, - new short[] { 1, 0, 0, 0, 1, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 1, 0, 0, 0, 1 }, - new short[] { 0, 0, 1, 0, 0, 0, 1, 0 } - }; - - internal static readonly short[][] DashHorz = new short[][] { - new short[] { 0, 0, 0, 0, 1, 1, 1, 1 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 1, 1, 1, 1 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 } - }; - - internal static readonly short[][] DashVert = new short[][] { - new short[] { 0, 0, 0, 0, 1, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 1, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 1, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 1, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 } - }; - - - internal static readonly short[][] SmConfetti = new short[][] { - new short[] { 0, 0, 0, 1, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 0, 0, 1, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 1, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 1, 0, 0, 0 }, - new short[] { 0, 1, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 1, 0 } - }; - - internal static readonly short[][] LgConfetti = new short[][] { - new short[] { 0, 0, 0, 0, 0, 0, 1, 1 }, - new short[] { 0, 0, 0, 1, 1, 0, 1, 1 }, - new short[] { 1, 1, 0, 1, 1, 0, 0, 0 }, - new short[] { 1, 1, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 1, 1, 0, 0 }, - new short[] { 1, 0, 0, 0, 1, 1, 0, 1 }, - new short[] { 1, 0, 1, 1, 0, 0, 0, 1 }, - new short[] { 0, 0, 1, 1, 0, 0, 0, 0 } - }; - - internal static readonly short[][] ZigZag = new short[][] { - new short[] { 1, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 0, 1, 0, 0, 0, 0, 1, 0 }, - new short[] { 0, 0, 1, 0, 0, 1, 0, 0 }, - new short[] { 0, 0, 0, 1, 1, 0, 0, 0 } - }; - - internal static readonly short[][] Wave = new short[][] { - new short[] { 0, 0, 1, 0, 0, 1, 0, 1 }, - new short[] { 1, 1, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 1, 1, 0, 0, 0 } - }; - - internal static readonly short[][] DiagBrick = new short[][] { - new short[] { 0, 0, 1, 0, 0, 1, 0, 0 }, - new short[] { 0, 1, 0, 0, 0, 0, 1, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 0, 0, 0, 0, 0, 0, 1, 0 }, - new short[] { 0, 0, 0, 0, 0, 1, 0, 0 }, - new short[] { 0, 0, 0, 0, 1, 0, 0, 0 }, - new short[] { 0, 0, 0, 1, 1, 0, 0, 0 } - }; - - internal static readonly short[][] HorzBrick = new short[][] { - new short[] { 1, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 1, 1, 1, 1, 1, 1, 1, 1 } - }; - - internal static readonly short[][] Weave = new short[][] { - new short[] { 1, 0, 0, 0, 1, 0, 0, 0 }, - new short[] { 0, 1, 0, 1, 0, 1, 0, 0 }, - new short[] { 0, 0, 1, 0, 0, 0, 1, 0 }, - new short[] { 0, 1, 0, 0, 0, 1, 0, 1 }, - new short[] { 1, 0, 0, 0, 1, 0, 0, 0 }, - new short[] { 0, 0, 0, 1, 0, 1, 0, 0 }, - new short[] { 0, 0, 1, 0, 0, 0, 1, 0 }, - new short[] { 0, 1, 0, 1, 0, 0, 0, 1 } - }; - - internal static readonly short[][] Plaid = new short[][] { - new short[] { 1, 0, 1, 0, 1, 0, 1, 0 }, - new short[] { 0, 1, 0, 1, 0, 1, 0, 1 }, - new short[] { 1, 1, 1, 1, 0, 0, 0, 0 }, - new short[] { 1, 1, 1, 1, 0, 0, 0, 0 }, - new short[] { 1, 1, 1, 1, 0, 0, 0, 0 }, - new short[] { 1, 1, 1, 1, 0, 0, 0, 0 }, - new short[] { 1, 0, 1, 0, 1, 0, 1, 0 }, - new short[] { 0, 1, 0, 1, 0, 1, 0, 1 } - }; - - - internal static readonly short[][] Divot = new short[][] { - new short[] { 0, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 1, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 1, 0, 0, 0 }, - new short[] { 0, 0, 0, 1, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 } - }; - - internal static readonly short[][] DotGrid = new short[][] { - new short[] { 1, 0, 1, 0, 1, 0, 1, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 } - }; - - internal static readonly short[][] DotDmnd = new short[][] { - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 1, 0, 0, 0, 1, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 1, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 1, 0, 0, 0, 1, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 } - }; - - internal static readonly short[][] Shingle = new short[][] { - new short[] { 0, 0, 1, 1, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 1, 1, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 1, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 0, 0, 0, 0, 0, 0, 1, 1 }, - new short[] { 1, 0, 0, 0, 0, 1, 0, 0 }, - new short[] { 0, 1, 0, 0, 1, 0, 0, 0 } - }; - - internal static readonly short[][] Trellis = new short[][] { - new short[] { 0, 1, 1, 0 }, - new short[] { 1, 1, 1, 1 }, - new short[] { 1, 0, 0, 1 }, - new short[] { 1, 1, 1, 1 } - }; - - internal static readonly short[][] Sphere = new short[][] { - new short[] { 1, 0, 0, 1, 1, 0, 0, 0 }, - new short[] { 1, 1, 1, 1, 1, 0, 0, 0 }, - new short[] { 1, 1, 1, 1, 1, 0, 0, 0 }, - new short[] { 0, 1, 1, 1, 0, 1, 1, 1 }, - new short[] { 1, 0, 0, 0, 1, 0, 0, 1 }, - new short[] { 1, 0, 0, 0, 1, 1, 1, 1 }, - new short[] { 1, 0, 0, 0, 1, 1, 1, 1 }, - new short[] { 0, 1, 1, 1, 0, 1, 1, 1 } - }; - - internal static readonly short[][] SmGrid = new short[][] { - new short[] { 1, 1, 1, 1 }, - new short[] { 1, 0, 0, 0 }, - new short[] { 1, 0, 0 }, - new short[] { 1, 0, 0, 0 } - }; - - internal static readonly short[][] LgGrid = new short[][] { - new short[] { 1, 1, 1, 1, 1, 1, 1, 1 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 0, 0 } - }; - - internal static readonly short[][] SmCheck = new short[][] { - new short[] { 1, 0, 0, 1 }, - new short[] { 1, 0, 0, 1 }, - new short[] { 0, 1, 1, 0 }, - new short[] { 0, 1, 1, 0 } - }; - - internal static readonly short[][] LgCheck = new short[][] { - new short[] { 1, 1, 0, 0, 0, 0, 1, 1 }, - new short[] { 1, 1, 0, 0, 0, 0, 1, 1 }, - new short[] { 0, 0, 1, 1, 1, 1, 0, 0 }, - new short[] { 0, 0, 1, 1, 1, 1, 0, 0 }, - new short[] { 0, 0, 1, 1, 1, 1, 0, 0 }, - new short[] { 0, 0, 1, 1, 1, 1, 0, 0 }, - new short[] { 1, 1, 0, 0, 0, 0, 1, 1 }, - new short[] { 1, 1, 0, 0, 0, 0, 1, 1 } - }; - - internal static readonly short[][] OpenDmnd = new short[][] { - new short[] { 0, 1, 0, 0, 0, 1, 0, 0 }, - new short[] { 1, 0, 0, 0, 0, 0, 1, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 1 }, - new short[] { 1, 0, 0, 0, 0, 0, 1, 0 }, - new short[] { 0, 1, 0, 0, 0, 1, 0, 0 }, - new short[] { 0, 0, 1, 0, 1, 0, 0, 0 }, - new short[] { 0, 0, 0, 1, 0, 0, 0, 0 }, - new short[] { 0, 0, 1, 0, 1, 0, 0, 0 } - }; - - internal static readonly short[][] SolidDmnd = new short[][] { - new short[] { 0, 1, 1, 1, 1, 1, 0, 0 }, - new short[] { 0, 0, 1, 1, 1, 0, 0, 0 }, - new short[] { 0, 0, 0, 1, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 0, 0, 0, 0, 0 }, - new short[] { 0, 0, 0, 1, 0, 0, 0, 0 }, - new short[] { 0, 0, 1, 1, 1, 0, 0, 0 }, - new short[] { 0, 1, 1, 1, 1, 1, 0, 0 }, - new short[] { 1, 1, 1, 1, 1, 1, 1, 0 } - }; - - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Coordinate.cs b/src/EPPlus.Export.ImageRenderer/Coordinate.cs deleted file mode 100644 index 62e366dddf..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Coordinate.cs +++ /dev/null @@ -1,26 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -namespace EPPlusImageRenderer -{ - internal class Coordinate - { - public Coordinate(double x, double y) - { - X = x; - Y = y; - } - public double X { get; set; } - public double Y { get; set; } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/DrawingBase.cs b/src/EPPlus.Export.ImageRenderer/DrawingBase.cs deleted file mode 100644 index 9cfdc4ec47..0000000000 --- a/src/EPPlus.Export.ImageRenderer/DrawingBase.cs +++ /dev/null @@ -1,62 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -using EPPlus.Export.ImageRenderer; -using EPPlus.Export.ImageRenderer.Utils; -using EPPlus.Fonts.OpenType; -using EPPlus.Fonts.OpenType.Integration; -using EPPlus.Graphics; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using OfficeOpenXml.Export.HtmlExport; -using OfficeOpenXml.Interfaces.Drawing.Text; -using System.Collections.Generic; -using System.Text; - -namespace EPPlusImageRenderer -{ - internal abstract class DrawingBase - { - internal DrawingBase(ExcelDrawing drawing) - { - Drawing = drawing; - Bounds = drawing.GetBoundingBox(); - - var wb = drawing._drawings.Worksheet.Workbook; - Theme = wb.ThemeManager.GetOrCreateTheme(); - - var shaper = OpenTypeFonts.GetTextShaper(Theme.FontScheme.MajorFont[0].Typeface); - TextMeasurer = new OpenTypeFontTextMeasurer(shaper); - } - - - internal DrawingBase() - { - //Drawing = drawing; - //Bounds = drawing.GetBoundingBox(); - - //var wb = drawing._drawings.Worksheet.Workbook; - //Theme = wb.ThemeManager.GetOrCreateTheme(); - } - - internal readonly StyleCache _styleCache = new StyleCache(); - public ExcelDrawing Drawing { get; } - public ExcelTheme Theme { get;} - public ExcelWorkbook Workbook => Drawing._drawings.Worksheet.Workbook; - internal ITextMeasurer TextMeasurer { get; } - public List RenderItems { get; } = new List(); - internal BoundingBox Bounds = new BoundingBox(); - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/DrawingChart.cs b/src/EPPlus.Export.ImageRenderer/DrawingChart.cs deleted file mode 100644 index 9908c7db57..0000000000 --- a/src/EPPlus.Export.ImageRenderer/DrawingChart.cs +++ /dev/null @@ -1,28 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -using OfficeOpenXml.Drawing.Chart; - -namespace EPPlusImageRenderer -{ - internal abstract class DrawingChart : DrawingBase - { - public DrawingChart(ExcelChart chart) : base(chart) - { - } - public ExcelChart Chart - { - get =>(ExcelChart)Drawing; - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/DrawingShape.cs b/src/EPPlus.Export.ImageRenderer/DrawingShape.cs deleted file mode 100644 index 102b6499cb..0000000000 --- a/src/EPPlus.Export.ImageRenderer/DrawingShape.cs +++ /dev/null @@ -1,133 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Export.ImageRenderer.Utils; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using System; -using System.Collections.Generic; - -namespace EPPlusImageRenderer -{ - internal abstract class DrawingShape : DrawingBase - { - protected ExcelShape _shape; - protected DrawingShape(ExcelShape shape) : base(shape) - { - var style = shape.Style; - - _shape = shape; - } - protected static void AddCmd(SvgRenderPathItem pi, DrawingPath path, List coordinates, ref PathCommands cmd, PathsBase pp, PathsBase p, PathCommandType commandType) - { - if (pp == null || pp.Type != p.Type) - { - SetCmdCoordinats(cmd, p, coordinates); - cmd = new PathCommands(commandType, pi); - pi.Commands.Add(cmd); - } - AddToCoordinates(path, coordinates, p); - } - protected static void AddArc(SvgRenderPathItem pi, DrawingPath path, List coordinates, PathsBase pCmd, out double startPointX, out double startPointY, PathsBase p) - { - //var width = ((double)path.Width.Value / ExcelDrawing.EMU_PER_PIXEL); - //var height = ((double)path.Height.Value / ExcelDrawing.EMU_PER_PIXEL); - var arc = (ArcTo)p; - PathCommands c = null; - startPointX = pCmd.EndX; - startPointY = pCmd.EndY; - if (startPointX != 0) startPointX /= ExcelDrawing.EMU_PER_POINT; - if (startPointY != 0) startPointY /= ExcelDrawing.EMU_PER_POINT; - var wR = arc.WidthRadius.Value / ExcelDrawing.EMU_PER_POINT; - var hR = arc.HeightRadius.Value / ExcelDrawing.EMU_PER_POINT; - if (wR == 0 && hR == 0) - { - return; - } - var stA = arc.StartAngle.Value / 60000d; - var swA = arc.SwingAngle.Value / 60000d; - - while (swA != 0) - { - var aAdd = swA < 0 ? Math.Max(swA, -180) : Math.Min(swA, 180); - var endAngle = AngleToRadians(stA + aAdd); - - var stA_Adj = stA < 0 ? (stA + 360) % 360 : stA; - var adjRads = AngleToRadians(stA_Adj); - - //Start and End angles are NOT the 't' angle of the equations we use. - //The angles we are given are DIRECTLY against the ellipse. Or point 'P' in a parametric form - //Therefore we have to use the angle we have to calculate the angles needed for our formulas. - var angleT = Math.Atan((wR * Math.Tan(adjRads)) / hR); - var angleTEnd = Math.Atan((wR * Math.Tan(endAngle)) / hR); - - //Atan can only return values on positive x 90° to -90° - //So we must adjust by adding Pi (180°) if x of the angle is negative - if (Math.Cos(adjRads) < 0) - { - angleT += (Math.Round((double)System.Math.PI, 14)); - } - if (Math.Cos(endAngle) < 0) - { - angleTEnd += (Math.Round((double)System.Math.PI, 14)); - } - - var centerX = startPointX - (wR * Math.Cos(angleT)); - var centerY = startPointY - (hR * Math.Sin(angleT)); - var endX = (double)centerX + (wR * Math.Cos(angleTEnd)); - var endY = (double)centerY + (hR * Math.Sin(angleTEnd)); - c = new PathCommands(PathCommandType.Arc, pi, wR, hR , 0, 0, swA < 0 ? 0 : 1, endX, endY); - pi.Commands.Add(c); - stA += aAdd; - swA -= aAdd; - if (wR != 0) - { - startPointX = endX; - } - if (hR != 0) - { - startPointY = endY; - } - ((ArcTo)p).SetEndCoordinates(endX* ExcelDrawing.EMU_PER_POINT, endY* ExcelDrawing.EMU_PER_POINT); - } - } - - protected static double AngleToRadians(double angle) - { - return MConverter.DegreesToRadians(angle); - } - protected static void SetCmdCoordinats(PathCommands cmd, PathsBase p, List coordinates) - { - if (cmd != null) - { - cmd.Coordinates = coordinates.ToArray(); - if (cmd.Coordinates.Length > 0) - { - coordinates.Clear(); - } - } - } - private static void AddToCoordinates(DrawingPath path, List coordinates, PathsBase p) - { - var mt = (PathWithCoordinates)p; - foreach (var c in mt.Coordinates) - { - coordinates.Add(c.X.Value / ExcelDrawing.EMU_PER_POINT); - coordinates.Add(c.Y.Value / ExcelDrawing.EMU_PER_POINT); - } - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/DrawingSize.cs b/src/EPPlus.Export.ImageRenderer/DrawingSize.cs deleted file mode 100644 index 7d8b1b5797..0000000000 --- a/src/EPPlus.Export.ImageRenderer/DrawingSize.cs +++ /dev/null @@ -1,26 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -namespace EPPlus.Export.ImageRenderer -{ - internal class DrawingSize - { - public DrawingSize(int width, int height) - { - Width = width; - Height = height; - } - public int Width { get; private set; } - public int Height { get; private set; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/EPPlus.Export.ImageRenderer.csproj b/src/EPPlus.Export.ImageRenderer/EPPlus.Export.ImageRenderer.csproj deleted file mode 100644 index a0803a19c0..0000000000 --- a/src/EPPlus.Export.ImageRenderer/EPPlus.Export.ImageRenderer.csproj +++ /dev/null @@ -1,116 +0,0 @@ - - - - net8.0;net9.0;netstandard2.1;netstandard2.0;net462 - True - ImageRenderer.snk - - latest - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - diff --git a/src/EPPlus.Export.ImageRenderer/IRender.cs b/src/EPPlus.Export.ImageRenderer/IRender.cs deleted file mode 100644 index 04a8d51afd..0000000000 --- a/src/EPPlus.Export.ImageRenderer/IRender.cs +++ /dev/null @@ -1,17 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using System.Text; - -namespace EPPlusImageRenderer -{ -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/ImageRenderer.cs b/src/EPPlus.Export.ImageRenderer/ImageRenderer.cs deleted file mode 100644 index 3b29db1d42..0000000000 --- a/src/EPPlus.Export.ImageRenderer/ImageRenderer.cs +++ /dev/null @@ -1,742 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -using EPPlus.Export.ImageRenderer.RenderItems; -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlus.Export.ImageRenderer.Svg; -using EPPlus.Export.ImageRenderer.Svg.DefinitionUtils; -using EPPlus.Export.ImageRenderer.Svg.DefinitionUtils.UtillNodes; -using EPPlus.Export.ImageRenderer.Svg.NodeAttributes; -using EPPlus.Export.ImageRenderer.Svg.Writer; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlus.Graphics.Math; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using OfficeOpenXml.Utils; -using OfficeOpenXml.Utils.EnumUtils; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using System.Text; - -namespace EPPlusImageRenderer -{ - public class ImageRenderer - { - public string RenderDrawingToSvg(ExcelDrawing drawing) - { - var sb = new StringBuilder(); - if (drawing is ExcelShape shape) - { - var svg = new SvgShape(shape); - svg.Render(sb); - return sb.ToString(); - } - else if(drawing is ExcelChart chart) - { - var svg = new SvgChart(chart); - svg.Render(sb); - return sb.ToString(); - } - - throw new NotImplementedException("Image rendering for drawing type not implemented."); - } - - public enum RenderItemClasses - { - Rect, - TextBox, - Shape, - CircleSegment - } - - public Dictionary GetItemProperties(RenderItemClasses item) - { - switch (item) - { - case RenderItemClasses.Rect: - return new Dictionary { { "Top", 10d }, { "Left", 10d }, { "Width", 10d }, { "Height", 10d }, {"Opacity", 0.8 }, {"Fill", Color.Goldenrod } }; - case RenderItemClasses.CircleSegment: - return new Dictionary { { "angle", 90d }, { "radius", 144d }, { "cx", 144d }, { "cy", 144d } }; - case RenderItemClasses.TextBox: - default: - throw new NotImplementedException("This class has not been implemented as an option yet"); - - } - } - - private string RenderCircleSegment(DrawingBase baseItem, Dictionary itemProperties) - { - return RenderCircleSegment((double)itemProperties["angle"], (double)itemProperties["radius"], (double)itemProperties["cx"], (double)itemProperties["cy"]); - } - - string RenderCircleSegment(double degree, double radius, double cx, double cy) - { - degree = degree % 360; - - if(degree < 0) - { - degree = 360 - degree; - } - - //Adjust by -90 so it starts from the top - var angleRadians = (degree-90d) * (Math.PI / 180.0d); - - //radius = radius.PixelToPoint(); - //cx = radius.PixelToPoint(); - //cy = radius.PixelToPoint(); - - var halfAngle = degree / 2; - - var halfAngleRadians = (halfAngle - 90d) * (Math.PI / 180.0d); - - var xPoint = cx + (radius * Math.Cos(angleRadians)); - var yPoint = cy + (radius * Math.Sin(angleRadians)); - - Coordinate endPoint = new Coordinate(xPoint, yPoint); - - var xPointHalf = cx + (radius * Math.Cos(halfAngleRadians)); - var yPointHalf = cy + (radius * Math.Sin(halfAngleRadians)); - - Coordinate halfPoint = new Coordinate(xPointHalf, yPointHalf); - - var baseBB = new BoundingBox(); - - //96x96 px - baseBB.Width = 72*4; - baseBB.Height = 72*4; - - var baseItem = new DrawingItemForTesting(baseBB); - - BoundingBox parent = new BoundingBox(); - - //Transform rotationPoint = new Transform(); - //rotationPoint.SetLocalPositionWithWorldCoordinates(new Vector2(cx, cy)); - //var item = new SvgGroupItemNew(baseItem, parent, -45d, rotationPoint); - - var slice = new SvgRenderPathItem(baseItem, baseItem.Bounds); - - //item.AddChildItem(slice); - - var startPoint = new Coordinate(cx, cy-radius); - - var moveCenter = new PathCommands(PathCommandType.Move, slice, cx / baseItem.Bounds.Width, cy / baseItem.Bounds.Height); - var lineToStart = new PathCommands(PathCommandType.Line, slice, startPoint.X / baseItem.Bounds.Width, startPoint.Y / baseItem.Bounds.Height); - - var lineToMidPoint = new PathCommands(PathCommandType.Line, slice, halfPoint.X / baseItem.Bounds.Width, halfPoint.Y / baseItem.Bounds.Height); - - - - var w = baseItem.Bounds.Width.PointToPixel(); - var h = baseItem.Bounds.Height.PointToPixel(); - - var radX = radius.PointToPixel() / w; - var radY = radius.PointToPixel() / h; - - var arcCommand = new PathCommands(PathCommandType.Arc, slice, new double[] { radX, radY, 0, degree > 180 ? 1 : 0, 1, endPoint.X / baseItem.Bounds.Width, endPoint.Y / baseItem.Bounds.Height }); - - slice.Commands.Add(moveCenter); - slice.Commands.Add(lineToStart); - slice.Commands.Add(arcCommand); - slice.Commands.Add(moveCenter); - slice.Commands.Add(lineToMidPoint); - - slice.FillColor = "red"; - slice.BorderColor = "green"; - slice.BorderWidth = 5; - - baseItem.RenderItems.Add(slice); - - var sb = new StringBuilder(); - - baseItem.Render(sb); - - return sb.ToString(); - - //return baseItem; - } - - /// - /// For Testing the specific renderItem class - /// - /// - /// - /// - /// - public string RenderIndividualClass(RenderItemClasses item, Dictionary itemProperties, double width, double height) - { - var svgCanvas = new DrawingItemForTesting(new BoundingBox(width.PixelToPoint(), height.PixelToPoint())); - - RenderItem renderItem; - - if (item == RenderItemClasses.CircleSegment) - { - return GenerateFromCircle(item, svgCanvas, itemProperties); - } - else - { - renderItem = GenerateFromClasses(item, svgCanvas, itemProperties); - } - - svgCanvas.RenderItems.Add(renderItem); - - var sb = new StringBuilder(); - - svgCanvas.Render(sb); - - return sb.ToString(); - } - - public enum RenderPresets - { - ContainerMargins, - RotatingContainer, - PatternFill, - } - - public string RenderTest(RenderPresets preset) - { - return GenerateFromPreset(preset); - } - - - private string rotatingContainer() - { - var baseBB = new BoundingBox(); - - //96x96 px - baseBB.Width = 72; - baseBB.Height = 72; - - var baseItem = new DrawingItemForTesting(baseBB); - - BoundingBox parent = new BoundingBox(); - - var groupItem = new SvgGroupItemNew(baseItem, parent, 45); - - groupItem.TranslationOffset.Left = 10; - groupItem.TranslationOffset.Top = 10; - - SvgRenderRectItem rectItem = new SvgRenderRectItem(baseItem, groupItem.Bounds); - - rectItem.FillColor = "red"; - rectItem.FillOpacity = 0.2d; - - rectItem.Width = 20; - rectItem.Height = 20; - - groupItem.AddChildItem(rectItem); - - - SvgRenderRectItem siblingItem = new SvgRenderRectItem(baseItem, groupItem.Bounds); - siblingItem.FillColor = "blue"; - siblingItem.FillOpacity = 0.2d; - - siblingItem.Width = 20; - siblingItem.Height = 20; - - siblingItem.Bounds.Left = 20; - siblingItem.Bounds.Top = 20; - - groupItem.AddChildItem(siblingItem); - - groupItem.SetRotationPointToCenterOfGroup(); - - SvgRenderRectItem centerOfGroupMarker = new SvgRenderRectItem(baseItem, baseItem.Bounds); - centerOfGroupMarker.FillColor = "green"; - centerOfGroupMarker.FillOpacity = 0.8d; - - centerOfGroupMarker.Width = 6; - centerOfGroupMarker.Height = 6; - - centerOfGroupMarker.Left = 30 - (centerOfGroupMarker.Width / 2); - centerOfGroupMarker.Top = 30 - (centerOfGroupMarker.Height / 2); - - baseItem.RenderItems.Add(centerOfGroupMarker); - - var sb = new StringBuilder(); - - baseItem.RenderItems.Add(groupItem); - - baseItem.Render(sb); - - return sb.ToString(); - } - - private string containerMargins() - { - var baseBB = new BoundingBox(); - - baseBB.Width = 400; - baseBB.Height = 400; - - var baseItem = new DrawingItemForTesting(baseBB); - - SvgRenderRectItem myBgItem = new SvgRenderRectItem(baseItem, baseItem.Bounds); - myBgItem.FillColor = "purple"; - myBgItem.FillOpacity = 0.2d; - - SvgRenderRectItem myInnerItem = new SvgRenderRectItem(baseItem, myBgItem.Bounds); - - myInnerItem.FillColor = "green"; - myInnerItem.FillOpacity = 0.8d; - - myInnerItem.Width = 50; - myInnerItem.Height = 50; - - var container = new SvgContainerItem(myInnerItem, myBgItem); - - container.MarginLeft = 5; - container.MarginRight = 5; - container.MarginTop = 5; - container.MarginBottom = 5; - - container.ApplyMargins(); - - baseItem.RenderItems.Add(container); - - var sb = new StringBuilder(); - - baseItem.Render(sb); - - return sb.ToString(); - } - - internal string pattern() - { - var baseBB = new BoundingBox(); - - baseBB.Width = 400; - baseBB.Height = 400; - - var baseItem = new DrawingItemForTesting(baseBB); - - - var linePattern = new LinePattern(baseItem, "testLines", LinePatternType.Vertical); - linePattern.SetNumberOfLines(3); - - var defItem = new DefinitionGroup(baseItem); - defItem.Items.Add(linePattern); - - var rectItem = new SvgRenderRectItem(baseItem, baseItem.Bounds); - - rectItem.FillColor = $"url(#{"testLines"})"; - - rectItem.Width = 200; - rectItem.Height = 200; - - baseItem.RenderItems.Add(defItem); - baseItem.RenderItems.Add(rectItem); - var useItem = new SvgUseRefItem(baseItem,baseItem.Bounds,"testLines"); - - baseItem.RenderItems.Add(useItem); - - var sb = new StringBuilder(); - - baseItem.Render(sb); - - return sb.ToString(); - } - - internal string pattern2() - { - var baseBB = new BoundingBox(); - - baseBB.Width = 400; - baseBB.Height = 400; - - var baseItem = new DrawingItemForTesting(baseBB); - - string refId = "grid"; - - var defItem = new DefinitionGroup(baseItem); - - var dynaGrid = new DynamicGridDefGroup(baseItem, refId, 7, 5); - defItem.Items.Add(dynaGrid); - - baseItem.RenderItems.Add(defItem); - - var useItem = new SvgUseRefItem(baseItem, baseItem.Bounds, refId); - useItem.Bounds.Width = 300; - useItem.Bounds.Height = 200; - - baseItem.RenderItems.Add(useItem); - - var sb = new StringBuilder(); - - baseItem.Render(sb); - - return sb.ToString(); - } - - private string GenerateFromPreset(RenderPresets preset) - { - switch (preset) - { - case RenderPresets.ContainerMargins: - return containerMargins(); - case RenderPresets.RotatingContainer: - return rotatingContainer(); - case RenderPresets.PatternFill: - return pattern2(); - } - return ""; - } - - private RenderItem GenerateRect(DrawingBase baseItem, Dictionary itemProperties) - { - var rectItem = new SvgRenderRectItem(baseItem, baseItem.Bounds); - - if (itemProperties.ContainsKey("Top")) - { - rectItem.Top = (double)itemProperties["Top"]; - } - if (itemProperties.ContainsKey("Left")) - { - rectItem.Left = (double)itemProperties["Left"]; - } - if (itemProperties.ContainsKey("Width")) - { - rectItem.Width = (double)itemProperties["Width"]; - } - if (itemProperties.ContainsKey("Height")) - { - rectItem.Height = (double)itemProperties["Height"]; - } - if (itemProperties.ContainsKey("Opacity")) - { - rectItem.FillOpacity = (double)itemProperties["Opacity"]; - } - if (itemProperties.ContainsKey("Fill")) - { - rectItem.FillColor = "#" + ((Color)itemProperties["Fill"]).ToColorString(); - } - - return rectItem; - } - - private string GenerateFromCircle(RenderItemClasses preset, DrawingBase baseItem, Dictionary itemProperties) - { - return RenderCircleSegment(baseItem, itemProperties); - } - - private RenderItem GenerateFromClasses(RenderItemClasses preset, DrawingBase baseItem, Dictionary itemProperties) - { - switch (preset) - { - case RenderItemClasses.Rect: - return GenerateRect(baseItem, itemProperties); - case RenderItemClasses.CircleSegment: - //return RenderCircleSegment(baseItem, itemProperties); - case RenderItemClasses.TextBox: - - - default: - throw new NotImplementedException("This class has not been implemented as an option yet"); - } - } - - //public string RenderTestCanvas(double widthPixel, double heightPixel, Color bgColor) - //{ - - //} - - //public string RenderBaseItemToSvg(DrawingBase drawing) - //{ - - //} - - ////Attempt at ensuring features can be created/tested individually by the system - ////Without relying on having a whole workbook or epplus project. - ////Simply: Does our positioning, sizing and parent hierarchy logic work as expected or not. - //public string RenderIndependentCanvas(double widthPixel, double heightPixel, Color bgColor) - //{ - // var widthPoint = widthPixel.PixelToPoint(); - // var heightPoint = heightPixel.PixelToPoint(); - - // var CanvasBounds = new BoundingBox(widthPoint, heightPoint); - - // var sb = new StringBuilder(); - // var canvas = new SvgIndependentCanvas(CanvasBounds, bgColor); - - // var rect = new SvgIndependentRect(canvas.Bounds, widthPoint/2, heightPoint/2); - // rect.FillColor = "red"; - // rect.Left = 10; - - // //BoundingBox boundsTextBox = new BoundingBox(rect.Bounds.Left, rect.Bounds.Top, rect.Bounds.Width, rect.Bounds.Height); - // //var independentTxtBox = new SvgIndependentTextBox(canvas, boundsTextBox); - - // //var textBox = new svgin - - // canvas.AddRenderItem(rect); - - // canvas.Render(sb); - // return sb.ToString(); - //} - - - //public string RenderTextBox(ExcelDrawing someDrawing, BoundingBox parent, double maxHeight, double maxWidth) - //{ - // var sb = new StringBuilder(); - // var svgTextBox = new DrawingTextBox(someDrawing, parent, maxWidth, maxHeight); - //} - - - //public string RenderRangeToSvg(ExcelRange range) - //{ - // var ws = range.Worksheet; - - // //ws.Workbook.Styles.CellXfs. - - // double totalWidth = 0; - // foreach (var col in range.EntireColumn) - // { - // totalWidth += ExcelColumn.ColumnWidthToPixels(col.Width, range.Worksheet.Workbook.MaxFontWidth); - // } - // double totalHeight = 0; - // foreach (var row in range.EntireRow) - // { - // totalHeight = row.Height; - // } - - // var sRange = new SvgRange(range, totalWidth, totalHeight); - - // var sb = new StringBuilder(); - // sb.Append($""); - // sRange.Render(sb); - // sb.Append($""); - // return sb.ToString(); - //} - - //public string RenderBox(string boxText) - //{ - // string retStr = ""; - // var container = new TextContainerBase(boxText); - // var element = GenerateSvg(container); - //public string RenderBox(string boxText) - //{ - // string retStr = ""; - // var container = new TextContainerBase(boxText); - // var element = GenerateSvg(container); - - // using (var ms = EPPlusMemoryManager.GetStream()) - // { - // SvgWriter writer = new SvgWriter(ms, Encoding.UTF8); - // writer.RenderSvgElement(element, true); - // ms.Position = 0; - // using (var sr = new StreamReader(ms)) - // { - // retStr = sr.ReadToEnd(); - // return retStr; - // } - // } - // using (var ms = EPPlusMemoryManager.GetStream()) - // { - // SvgWriter writer = new SvgWriter(ms, Encoding.UTF8); - // writer.RenderSvgElement(element, true); - // ms.Position = 0; - // using (var sr = new StreamReader(ms)) - // { - // retStr = sr.ReadToEnd(); - // return retStr; - // } - // } - - // //writer.RenderSvgElement(element, true); - // //writer.RenderSvgElement(element, true); - - // //StreamReader reader = new StreamReader(ms); - // //retStr = reader.ReadToEnd(); - - // ////SvgParagraph para = new SvgParagraph(container.GetContent(),); - // ////var doc = new SvgEpplusDocument(); - // //return retStr; - //} - - //public string RenderTextBody(ExcelTextBody body, double shapeWidth, double shapeHeight) - //{ - // var sb = new StringBuilder(); - - // BoundingBox worldBounds = new BoundingBox(); - // worldBounds.Width = shapeWidth; - // worldBounds.Height = shapeHeight; - - // var doc = new SvgEpplusDocument((int)worldBounds.Width, (int)worldBounds.Height); - // doc.Render(sb); - - // FontMeasurerTrueType measurer = new FontMeasurerTrueType(11, "Aptos Narrow", FontSubFamily.Regular); - - // var svgBody = new DrawingTextbody(doc, worldBounds, null); - // svgBody.ImportTextBody(body); - - // svgBody.Render(sb); - // sb.AppendLine(""); - - // return sb.ToString(); - //} - - internal string RenderSvgElement(SvgElement element) - { - string retStr = string.Empty; - - using (var ms = EPPlusMemoryManager.GetStream()) - { - SvgWriter writer = new SvgWriter(ms, Encoding.UTF8); - writer.RenderSvgElement(element, true); - ms.Position = 0; - using (var sr = new StreamReader(ms)) - { - retStr = sr.ReadToEnd(); - return retStr; - } - } - } - - - internal SvgElement GetDefinitions(BoundingBox boundingBox, out string nameId, bool AllowOverflow = false) - { - nameId = "boundingBox"; - var def = new SvgElement("defs"); - - string defaultName = "defaultRect"; - - if (AllowOverflow == false) - { - var bb = new SvgElement("rect"); - bb.AddAttribute("width", boundingBox.Width); - bb.AddAttribute("height", boundingBox.Height); - bb.AddAttribute("id", defaultName); - - def.AddChildElement(bb); - - var clipPath = new SvgElement("clipPath"); - clipPath.AddAttribute("id", nameId); - - def.AddChildElement(clipPath); - - var useElement = new SvgElement("use"); - useElement.AddAttribute("href", $"#{defaultName}"); - - clipPath.AddChildElement(useElement); - } - - return def; - } - - //internal SvgElement GenerateSvg(TextContainerBase container) - //{ - // var fullString = container.GetContent(); - //internal SvgElement GenerateSvg(TextContainerBase container) - //{ - // var fullString = container.GetContent(); - - // var doc = new SvgEpplusDocument(500, 500); - // var doc = new SvgEpplusDocument(500, 500); - - // var bg = new SvgElement("rect"); - // bg.AddAttribute("width", "100%"); - // bg.AddAttribute("height", "100%"); - // bg.AddAttribute("fill", "red"); - // bg.AddAttribute("opacity", "0.1"); - // var bg = new SvgElement("rect"); - // bg.AddAttribute("width", "100%"); - // bg.AddAttribute("height", "100%"); - // bg.AddAttribute("fill", "red"); - // bg.AddAttribute("opacity", "0.1"); - - // var nameId = "boundingBox"; - // var def = new SvgElement("defs"); - // var clipPath = new SvgElement("clipPath"); - // clipPath.AddAttribute("id", nameId); - // var nameId = "boundingBox"; - // var def = new SvgElement("defs"); - // var clipPath = new SvgElement("clipPath"); - // clipPath.AddAttribute("id", nameId); - - // def.AddChildElement(clipPath); - // def.AddChildElement(clipPath); - - // var bb = new SvgElement("rect"); - // bb.AddAttribute("x", container.Position.X); - // bb.AddAttribute("y", container.Position.Y); - // bb.AddAttribute("width", container.Width); - // bb.AddAttribute("height", container.Height); - // //bb.AddAttribute("fill", "blue"); - // //bb.AddAttribute("opacity", "0.5"); - // var bb = new SvgElement("rect"); - // bb.AddAttribute("x", container.Position.X); - // bb.AddAttribute("y", container.Position.Y); - // bb.AddAttribute("width", container.Width); - // bb.AddAttribute("height", container.Height); - // //bb.AddAttribute("fill", "blue"); - // //bb.AddAttribute("opacity", "0.5"); - - // clipPath.AddChildElement(bb); - // clipPath.AddChildElement(bb); - - // var fontSizePx = 16d; - // var fontSizePx = 16d; - - // var renderElement = new SvgElement("text"); - // renderElement.AddAttribute("x", container.Position.X); - // renderElement.AddAttribute("y", container.Position.Y + fontSizePx); - // renderElement.AddAttribute("_measurementFont-size", $"{fontSizePx}px"); - // renderElement.AddAttribute("clip-path", $"url(#{nameId})"); - // var renderElement = new SvgElement("text"); - // renderElement.AddAttribute("x", container.Position.X); - // renderElement.AddAttribute("y", container.Position.Y + fontSizePx); - // renderElement.AddAttribute("_measurementFont-size", $"{fontSizePx}px"); - // renderElement.AddAttribute("clip-path", $"url(#{nameId})"); - - // renderElement.Content = fullString; - // renderElement.Content = fullString; - - // var bbVisual = new SvgElement("rect"); - // bbVisual.AddAttribute("x", container.Position.X); - // bbVisual.AddAttribute("y", container.Position.Y); - // bbVisual.AddAttribute("width", container.Width); - // bbVisual.AddAttribute("height", container.Height); - // bbVisual.AddAttribute("fill", "blue"); - // bbVisual.AddAttribute("opacity", "0.5"); - // var bbVisual = new SvgElement("rect"); - // bbVisual.AddAttribute("x", container.Position.X); - // bbVisual.AddAttribute("y", container.Position.Y); - // bbVisual.AddAttribute("width", container.Width); - // bbVisual.AddAttribute("height", container.Height); - // bbVisual.AddAttribute("fill", "blue"); - // bbVisual.AddAttribute("opacity", "0.5"); - - // doc.AddChildElement(def); - // doc.AddChildElement(bg); - // doc.AddChildElement(bbVisual); - // doc.AddChildElement(renderElement); - // doc.AddChildElement(def); - // doc.AddChildElement(bg); - // doc.AddChildElement(bbVisual); - // doc.AddChildElement(renderElement); - - // doc.AddAttributes(); - // doc.AddAttributes(); - - // return doc; - //} - // return doc; - //} - } -} diff --git a/src/EPPlus.Export.ImageRenderer/ImageRenderer.snk b/src/EPPlus.Export.ImageRenderer/ImageRenderer.snk deleted file mode 100644 index 92ed14d1c0..0000000000 Binary files a/src/EPPlus.Export.ImageRenderer/ImageRenderer.snk and /dev/null differ diff --git a/src/EPPlus.Export.ImageRenderer/PathCommands.cs b/src/EPPlus.Export.ImageRenderer/PathCommands.cs deleted file mode 100644 index b27c5c30be..0000000000 --- a/src/EPPlus.Export.ImageRenderer/PathCommands.cs +++ /dev/null @@ -1,71 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -using EPPlus.Fonts.OpenType.Utils; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml; -using System; -using System.Globalization; -using System.Text; - -namespace EPPlusImageRenderer -{ - internal class PathCommands - { - public PathCommands(PathCommandType type, SvgRenderItem item, params double[] coordinates) - { - Type = type; - RenderItem = item; - Coordinates = coordinates; - } - public SvgRenderItem RenderItem{ get; set;} - public PathCommandType Type { get; } - public double[] Coordinates { get; set; } - public SvgAdjustmentPoint AdjustmentPoint { get; set; } - public int CommandIndex { get; set; } - - public void Render(/*double width, double height, */StringBuilder sb) - { - sb.Append(Type.AsCommandChar()); - for (int i = 0; i < Coordinates.Length; i++) - { - string s; - if (Type==PathCommandType.Arc && ((i & 7)==2 || (i & 7) == 3 || (i & 7) == 4)) // Arc flags are not coordinates, but should be rendered as integers - { - s = Coordinates[i].ToString(CultureInfo.InvariantCulture); - } - else - { - s = Coordinates[i].PointToPixelString(); - } - sb.AppendFormat("{0} ", s); - } - if (Coordinates.Length > 0) - { - sb.Remove(sb.Length - 1, 1); - } - } - internal virtual bool InPoints(double x) - { - return true; - } - internal PathCommands Clone() - { - return new PathCommands(Type, RenderItem) - { - Coordinates = (double[])Coordinates.Clone(), - }; - } - } - -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Properties/AssemblyInfo.cs b/src/EPPlus.Export.ImageRenderer/Properties/AssemblyInfo.cs deleted file mode 100644 index 88652c64cf..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 01/27/2020 EPPlus Software AB Initial release EPPlus 5 - *************************************************************************************************/ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -//[assembly: InternalsVisibleTo("EPPlusTest, PublicKey=00240000048000009400000006020000002400005253413100040000010001001dd11308ec93a6ebcec727e183a8972dc6f95c23ecc34aa04f40cbfc9c17b08b4a0ea5c00dcd203bace44d15a30ce8796e38176ae88e960ceff9cc439ab938738ba0e603e3d155fc298799b391c004fc0eb4393dd254ce25db341eb43303e4c488c9500e126f1288594f0710ec7d642e9c72e76dd860649f1c48249c00e31fba")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] -//[assembly: InternalsVisibleTo("EPPlusImageRenderer, PublicKey=002400000480000094000000060200000024000052534131000400000100010045e5d8275ad7edb388eb9c60ac6e1dd30161aea53f5334c45751217df06feb6ae799a1866fc0671f9ec0d6dbec23807ad71012617cb8d471979216c63baa607754a5d5f20b45e1ce5c256685552a2a09e19a35b554d554dcbbfecdd331bf15cb2b5c4fbd76f7424f30493635c0234ee1ea782ebfdfdc0dec3dbbc167c4c061cc")] -[assembly: InternalsVisibleTo("EPPlus.Export.ImageRenderer.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010065aa00a9b0dcfa0e8debefb14c3b6c12ef658ce1cfbfafcb6eb7dbdc0c49e4f70ea144ba29d827453f0716ffc8c1d87450ce0cf255bda1def174915caaa78f373f291ce1a7edf91ba6f9dc961d937b19d46dd5d7d70a6e2097c749d43780f0d00b29a6f5aec5c1d191bee69de0c889a6d2566bef3cb235612351eae7015382d0")] -[assembly: InternalsVisibleTo("SvgRenderer, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e5059c702e8e710d3450fe0c455a46f9ddd46f7f87451ecb5160d3a23fd6d63f6cd723abd60f7536098598d10e18d42accf48ce0856a01b04e0249cdd63dabdbb13a33c007738ebbe1b947e4af346854c58ffdacff04c5ae09c4cd2879a1af3354ca477632b07595fa0bf04b9a552356f8ad08bfe70c39d0d03ce45df7c7e3be")] -// The following GUID is for the ID of the typelib if this project is exposed to COM - -// Version information for an assembly consists of the following four values: -// -// MajorInterval Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: -#if (!Core) -//[assembly: AssemblyTitle("EPPlus")] -//[assembly: AssemblyDescription("A spreadsheet library for .NET framework and .NET core")] -//[assembly: AssemblyConfiguration("")] -//[assembly: AssemblyCompany("EPPlus Software AB")] -//[assembly: AssemblyProduct("EPPlus")] -//[assembly: AssemblyCopyright("EPPlus Software AB")] -//[assembly: AssemblyTrademark("")] -//[assembly: AssemblyCulture("")] -//[assembly: ComVisible(false)] - -//[assembly: AssemblyVersion("5.5.0.0")] -//[assembly: AssemblyFileVersion("5.5.0.0")] -#endif -[assembly: AllowPartiallyTrustedCallers] diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/GradientFill.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/GradientFill.cs deleted file mode 100644 index b3adc22c44..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/GradientFill.cs +++ /dev/null @@ -1,48 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.RenderItems; -using OfficeOpenXml.Drawing.Style.Fill; -using OfficeOpenXml.Drawing.Theme; -using System.Collections.Generic; -using System.Drawing; -using EPPlusColorConverter = OfficeOpenXml.Utils.TypeConversion.ColorConverter; -using System; - -namespace EPPlusImageRenderer.RenderItems -{ - internal class DrawGradientFill - { - public DrawGradientFill(ExcelTheme theme, ExcelDrawingGradientFill gradientFill) - { - this.Settings = gradientFill; - for (int i = 0; i < gradientFill.Colors.Count; i++) - { - var c = new GradientFillColor(gradientFill.Colors[i].Position, EPPlusColorConverter.GetThemeColor(theme, gradientFill.Colors[i].Color)); - Colors.Add(c); - } - - } - - public DrawGradientFill(List colors, List stops) - { - for (int i = 0; i < stops.Count; i++) - { - var c = new GradientFillColor(stops[i], colors[i]); - Colors.Add(c); - } - } - - public ExcelDrawingGradientFill Settings { get; set; } - public List Colors { get; set; } = new List(); - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/GradientFillColor.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/GradientFillColor.cs deleted file mode 100644 index affbc8207b..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/GradientFillColor.cs +++ /dev/null @@ -1,29 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using System.Drawing; - -namespace EPPlus.Export.ImageRenderer.RenderItems -{ - internal class GradientFillColor - { - public GradientFillColor(double position, Color color) - { - Position = position; - Color = color; - } - - public double Position { get; private set; } - - public Color Color { get; private set; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IBorder.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IBorder.cs deleted file mode 100644 index 4f70d4441a..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IBorder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Interfaces -{ - public interface IBorder : IFill - { - double? BorderWidth { get; set; } - double[] BorderDashArray { get; set; } - double? BorderDashOffset { get; set; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IFill.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IFill.cs deleted file mode 100644 index 7bdc1b33a6..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IFill.cs +++ /dev/null @@ -1,15 +0,0 @@ -using OfficeOpenXml.Drawing; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Interfaces -{ - public interface IFill - { - string Color { get; set; } - double? Opacity { get; set; } - PathFillMode FillMode { get; set; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IRectItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IRectItem.cs deleted file mode 100644 index ef091d7641..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IRectItem.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Interfaces -{ - internal interface IRectItem - { - double Top { get; set; } - double Left { get; set; } - double Height { get; set; } - double Width { get; set; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IStylingInfo.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IStylingInfo.cs deleted file mode 100644 index 940dff2f03..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IStylingInfo.cs +++ /dev/null @@ -1,25 +0,0 @@ -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Style.Effect; -using OfficeOpenXml.Drawing.Style.Fill; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Interfaces -{ - public interface StylingInfo - { - IBorder Border { get; set; } - IFill Fill { get; set; } - string FilterName { get; set; } - //public RenderGradientFill GradientFill { get; set; } - //FillType FillType { get; set; } - //RenderGradientFill BorderGradientFill { get; set; } - ExcelDrawingPatternFill PatternFill { get; } - ExcelDrawingBlipFill BlipFill { get; } - int StrokeMiterLimit { get; set; } - eCompoundLineStyle CompoundLineStyle { get; set; } - eLineCap LineCap { get; set; } - //eLineJoin LineJoin { get; set; } - double? GlowRadius { get; } - string GlowColor { get; } - ExcelDrawingOuterShadowEffect OuterShadowEffect { get; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IStylingInfoBase.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IStylingInfoBase.cs deleted file mode 100644 index de110ec790..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Interfaces/IStylingInfoBase.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Interfaces -{ - internal interface IStylingInfoBase - { - string FillColor { get; set; } - string BorderColor { get; set; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/PathCommandType.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/PathCommandType.cs deleted file mode 100644 index bb844e0429..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/PathCommandType.cs +++ /dev/null @@ -1,27 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -namespace EPPlusImageRenderer.RenderItems -{ - internal enum PathCommandType : byte - { - Move = 0, - Line = 1, - HorizontalLine = 2, - VerticalLine = 3, - CubicBézier = 4, - QuadraticBézier = 5, - Arc = 6, - End = 0xFF - } - -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/RenderItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/RenderItem.cs deleted file mode 100644 index 0fc0865737..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/RenderItem.cs +++ /dev/null @@ -1,346 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Graphics; -using EPPlusImageRenderer.Utils; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Chart.Style; -using OfficeOpenXml.Drawing.Style.Coloring; -using OfficeOpenXml.Drawing.Style.Effect; -using OfficeOpenXml.Drawing.Style.Fill; -using OfficeOpenXml.Drawing.Theme; -using OfficeOpenXml.Style; -using System; -using System.Drawing; -using System.Text; -using EPPlusColorConverter = OfficeOpenXml.Utils.TypeConversion.ColorConverter; -namespace EPPlusImageRenderer.RenderItems -{ - internal abstract class RenderItem : RenderItemBase - { - internal protected DrawingBase DrawingRenderer { get; } - internal RenderItem(DrawingBase renderer) - { - DrawingRenderer = renderer; - } - - internal RenderItem(DrawingBase renderer, BoundingBox parent) - { - Bounds.Parent = parent; - DrawingRenderer = renderer; - } - //internal abstract void GetBounds(out double il, out double it, out double ir, out double ib); - internal virtual void GetBounds(out double il, out double it, out double ir, out double ib) - { - il = Bounds.Left; - it = Bounds.Top; - ir = Bounds.Right; - ib = Bounds.Bottom; - } - - //internal bool IsEndOfGroup { get; set; } = false; - public string FillColor { get; set; } - public string FilterName { get; set; } - public DrawGradientFill GradientFill { get; set; } - public SvgFillType FillType { get; set; } - public double? FillOpacity { get; set; } - public string BorderColor { get; set; } - public DrawGradientFill BorderGradientFill { get; set; } - public ExcelDrawingPatternFill PatternFill { get; private set; } - public ExcelDrawingBlipFill BlipFill { get; private set; } - public double? BorderWidth { get; set; } - public double[] BorderDashArray { get; set; } - public int StrokeMiterLimit { get; set; } = 4; - public eCompoundLineStyle CompoundLineStyle { get; set; } = eCompoundLineStyle.Single; - public double? BorderDashOffset { get; set; } - public eLineCap LineCap { get; set; } = eLineCap.Flat; - public SvgLineJoin LineJoin { get; set; } = SvgLineJoin.Miter; - public double? BorderOpacity { get; set; } - public PathFillMode FillColorSource { get; set; } = PathFillMode.Norm; - public PathFillMode BorderColorSource { get; set; } = PathFillMode.Norm; - public double? GlowRadius { get; private set; } - public string GlowColor { get; private set; } - public ExcelDrawingOuterShadowEffect OuterShadowEffect { get; private set; } = null; - - /// - /// The origin point for any transform actions in svg. - /// Normally/Default 0,0 - /// - public Coordinate TransformOrigin { get; set; } = null; - - protected void CloneBase(RenderItem item) - { - item.FillColor = FillColor; - item.FillOpacity = FillOpacity; - item.BorderWidth = BorderWidth; - item.BorderColor = BorderColor; - item.BorderDashArray = BorderDashArray; - item.BorderDashOffset = BorderDashOffset; - item.BorderOpacity = BorderOpacity; - item.LineJoin = LineJoin; - item.LineCap = LineCap; - item.FillColorSource = FillColorSource; - } - - internal void SetPatternFill() - { - - } - - internal virtual void SetDrawingPropertiesFill(ExcelDrawingFill fill, ExcelDrawingColorManager color) - { - switch (fill.Style) - { - - case eFillStyle.PatternFill: - PatternFill = fill.PatternFill; - break; - case eFillStyle.BlipFill: - BlipFill = fill.BlipFill; - break; - default: - SetDrawingPropertiesFill((ExcelDrawingFillBasic)fill, color); - break; - } - } - internal virtual void SetDrawingPropertiesFill(ExcelDrawingFillBasic fill, ExcelDrawingColorManager color) - { - double? opacity=null; - switch (fill.Style) - { - case eFillStyle.NoFill: - if (fill.IsEmpty) - { - FillColor = GetFillColor(fill, color, FillColorSource, out opacity); - } - else - { - FillColor = "none"; - } - break; - case eFillStyle.SolidFill: - FillColor = GetFillColor(fill, color, FillColorSource, out opacity); - break; - case eFillStyle.GradientFill: - GradientFill = new DrawGradientFill(DrawingRenderer.Theme, fill.GradientFill); - FillColor = null; - break; - } - if (opacity.HasValue) - { - FillOpacity = opacity; - } - } - internal virtual void SetDrawingPropertiesBorder(ExcelDrawingBorder border, ExcelChartStyleColorManager color, bool hasBorder, double defaultWidth=1.5) - { - double? opacity = null; - switch (border.Fill.Style) - { - case eFillStyle.NoFill: - if(border.Fill.IsEmpty) - { - BorderColor = GetFillColor(border.Fill, color, BorderColorSource, out opacity); - } - else - { - BorderColor = "none"; - } - break; - case eFillStyle.SolidFill: - BorderColor = GetFillColor(border.Fill, color, BorderColorSource, out opacity); - BorderGradientFill = null; - break; - case eFillStyle.GradientFill: - BorderGradientFill = new DrawGradientFill(DrawingRenderer.Theme, border.Fill.GradientFill); - BorderColor = null; - break; - } - - if (opacity.HasValue) - { - BorderOpacity = opacity; - } - - if (hasBorder && BorderColorSource != PathFillMode.None) - { - BorderWidth = border.Width == 0 ? defaultWidth : border.Width; - if(border.LineStyle.HasValue && border.LineStyle!=eLineStyle.Solid) - { - BorderDashArray = GetDashArray(border); - } - if(border.CompoundLineStyle!=eCompoundLineStyle.Single) - { - CompoundLineStyle = border.CompoundLineStyle; - //TODO:Add support double compound borders. - } - } - } - internal void SetDrawingPropertiesEffects(ExcelDrawingEffectStyle effect) - { - if (effect.HasGlow) - { - GlowRadius = effect.Glow.Radius; - var gc = EPPlusColorConverter.GetThemeColor(DrawingRenderer.Theme, effect.Glow.Color); - GlowColor = "#" + gc.ToArgb().ToString("x8").Substring(2); - } - if(effect.HasOuterShadow) - { - OuterShadowEffect = effect.OuterShadow; - } - } - - private double[] GetDashArray(ExcelDrawingBorder border) - { - var lw = (int)Math.Round(border.Width * ExcelDrawing.EMU_PER_POINT / ExcelDrawing.EMU_PER_PIXEL); - switch (border.LineStyle) - { - case eLineStyle.Dot: - return new double[]{ lw, 4 * lw }; - case eLineStyle.DashDot: - return new double[] { 4 * lw, 3 * lw, lw, 3 * lw }; - case eLineStyle.Dash: - return new double[] { 4 * lw, 3 * lw }; - case eLineStyle.LongDash: - return new double[] { 8 * lw, 3 * lw }; - case eLineStyle.LongDashDot: - return new double[] { 8 * lw, 3 * lw, lw, 3 * lw }; - case eLineStyle.LongDashDotDot: - return new double[] { 8 * lw, 3 * lw, lw, 3 * lw, lw, 3 * lw }; - case eLineStyle.SystemDash: - return new double[] { 3 * lw, lw }; - case eLineStyle.SystemDot: - return new double[] { lw, lw }; - case eLineStyle.SystemDashDot: - return new double[] { 3 * lw, lw, lw, lw }; - case eLineStyle.SystemDashDotDot: - return new double[] { 3 * lw, lw, lw, lw, lw, lw }; - } - return null; - } - - private string GetFillColor(ExcelDrawingFillBasic fill, ExcelDrawingColorManager styleFillColor, PathFillMode fillColorSource, out double? opacity) - { - opacity = null; - if (fillColorSource == PathFillMode.None) - { - return "none"; - } - - Color fc; - if (fill == null || fill.Style == eFillStyle.NoFill) - { - if (styleFillColor == null) - { - fc = EPPlusColorConverter.GetThemeColor(DrawingRenderer.Theme.ColorScheme.Accent1); - } - else - { - fc = EPPlusColorConverter.GetThemeColor(DrawingRenderer.Theme, styleFillColor); - } - } - else if (fill.Style == eFillStyle.SolidFill) - { - fc = EPPlusColorConverter.GetThemeColor(DrawingRenderer.Theme, fill.SolidFill.Color); - } - else - { - return string.Empty; - } - - fc = ColorUtils.GetAdjustedColor(fillColorSource, fc); - if(fc.A<255 && fc!=Color.Empty) - { - opacity = fc.A/255D; - } - return "#" + fc.ToArgb().ToString("x8").Substring(2); - } - - internal void GetOuterShadowColor(out string shadowColor, out double opacity) - { - if (OuterShadowEffect == null) - { - shadowColor = null; - opacity = 0; - - } - else - { - var tc=EPPlusColorConverter.GetThemeColor(DrawingRenderer.Theme, OuterShadowEffect.Color); - if (tc.A < 255 && tc != Color.Empty) - { - opacity = tc.A / 255D; - } - else - { - opacity = 1; - } - shadowColor = "#" + tc.ToArgb().ToString("x8").Substring(2); - } - } - } - - internal abstract class RenderItemIndependent : RenderItemBase - { - public string FillColor { get; set; } - public string FilterName { get; set; } - public SvgFillType FillType { get; set; } - public double? FillOpacity { get; set; } - public string BorderColor { get; set; } - public double? BorderWidth { get; set; } - public double[] BorderDashArray { get; set; } - public double? BorderDashOffset { get; set; } - public eLineCap LineCap { get; set; } = eLineCap.Flat; - public SvgLineJoin LineJoin { get; set; } = SvgLineJoin.Miter; - public double? BorderOpacity { get; set; } - public PathFillMode FillColorSource { get; set; } = PathFillMode.Norm; - public PathFillMode BorderColorSource { get; set; } = PathFillMode.Norm; - public double? GlowRadius { get; private set; } - public string GlowColor { get; private set; } - - protected void CloneBase(RenderItem item) - { - item.FillColor = FillColor; - item.FillOpacity = FillOpacity; - item.BorderWidth = BorderWidth; - item.BorderColor = BorderColor; - item.BorderDashArray = BorderDashArray; - item.BorderDashOffset = BorderDashOffset; - item.BorderOpacity = BorderOpacity; - item.LineJoin = LineJoin; - item.LineCap = LineCap; - item.FillColorSource = FillColorSource; - } - - - internal string SetFillColor(Color color) - { - FillColor = GetAdjustedColor(color); - return FillColor; - } - - private string GetAdjustedColor(Color color) - { - var fc = ColorUtils.GetAdjustedColor(FillColorSource, color); - return "#" + fc.ToArgb().ToString("x8").Substring(2); - } - } - - /// - /// Base class for any item rendered. - /// - internal abstract class RenderItemBase - { - internal BoundingBox Bounds = new BoundingBox(); - public abstract RenderItemType Type { get; } - public abstract void Render(StringBuilder sb); - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/RenderItemType.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/RenderItemType.cs deleted file mode 100644 index aa3412ab30..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/RenderItemType.cs +++ /dev/null @@ -1,28 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -namespace EPPlusImageRenderer.RenderItems -{ - internal enum RenderItemType - { - Path = 0, - Rect = 1, - Group = 2, - Line = 3, - Ellipse = 4, - Text = 5, - TSpan = 6, - Paragraph = 7, - CommentTitle = 8, - Reference = 9, - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/ContainerItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/ContainerItem.cs deleted file mode 100644 index 355a0df8bb..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/ContainerItem.cs +++ /dev/null @@ -1,141 +0,0 @@ -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Shared -{ - internal abstract class ContainerItem : RenderItem - { - internal RenderItem InnerItem { get; private set; } - internal RenderItem OuterItem { get; private set; } - - double _marginLeft; - double _marginTop; - - internal double MarginLeft - { - get - { - return _marginLeft; - } - set - { - VerifyMarginInput(value); - _marginLeft = value; - //InnerItem.Bounds.Left = value; - } - } - - internal double MarginTop - { - get - { - return _marginTop; - } - set - { - VerifyMarginInput(value); - _marginTop = value; - //InnerItem.Bounds.Top = value; - } - } - - double _marginRight; - - internal double MarginRight - { - get - { - return _marginRight; - } - set - { - VerifyMarginInput(value); - _marginRight = value; - //InnerItem.Bounds.Width = Bounds.Width - value; - } - } - - double _marginBottom; - - internal double MarginBottom - { - get - { - return _marginBottom; - } - set - { - VerifyMarginInput(value); - _marginBottom = value; - } - } - - bool SizeToContents = true; - public ContainerItem(RenderItem innerItem, RenderItem outerItem) : base(innerItem.DrawingRenderer) - { - OuterItem = outerItem; - InnerItem = innerItem; - - OuterItem.Bounds.Parent = Bounds; - InnerItem.Bounds.Parent = Bounds; - } - - internal void ApplyMargins() - { - //Origin-Point: Set. Set in stone. All text moves from there - //Here it is Bounds.Top and Bounds.Left. Nothing in the content can change that - - OuterItem.Bounds.Width = InnerItem.Bounds.Width + MarginRight + MarginLeft; - OuterItem.Bounds.Height = InnerItem.Bounds.Height + MarginBottom + MarginTop; - - Bounds.Width = OuterItem.Bounds.Width; - Bounds.Height = OuterItem.Bounds.Height; - } - - public override RenderItemType Type => RenderItemType.Group; - - internal double GetInnerLeft() - { - return Bounds.Left + MarginLeft; - } - - internal double GetInnerTop() - { - return Bounds.Top + MarginTop; - } - - internal double GetInnerBottom() - { - return Bounds.Bottom - MarginBottom; - } - - internal double GetInnerRight() - { - return Bounds.Right - MarginRight; - } - - public double GetInnerWidth() - { - return GetInnerRight() - GetInnerLeft(); - } - - public double GetInnerHeight() - { - return GetInnerBottom() - GetInnerTop(); - } - - private void VerifyMarginInput(double value) - { - if (value < 0) - { - throw new ArgumentException("Margins cannot be set to a negative value! If you wish to set starting position, please set Left or Top for the ContainerItem"); - } - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/FlexBaseContainer.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/FlexBaseContainer.cs deleted file mode 100644 index 532679f706..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/FlexBaseContainer.cs +++ /dev/null @@ -1,18 +0,0 @@ -using EPPlusImageRenderer.RenderItems; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Shared -{ - //internal class FlexBaseContainer : RenderItem - //{ - // public override RenderItemType Type => throw new NotImplementedException(); - - // public override void Render(StringBuilder sb) - // { - // throw new NotImplementedException(); - // } - //} -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/GroupItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/GroupItem.cs deleted file mode 100644 index f725fd22a1..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/GroupItem.cs +++ /dev/null @@ -1,102 +0,0 @@ -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using System.Collections.Generic; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Shared -{ - internal abstract class PieGroupItemBase : RenderItem - { - /// - /// In degrees - /// - internal double Rotation = double.NaN; - /// - /// The translated position of this item in points - /// Also the parent position of the group item - /// (This may seem strange but it ensures the the translation is seen - /// immediately in the global position of GroupItem without affecting local position) - /// - internal Point TranslationOffset = new Point(0,0); - - Point _altRotationPoint = null; - - internal Point RotationPoint - { - get - { - if (_altRotationPoint == null) - { - return TranslationOffset; - } - return _altRotationPoint; - } - set - { - _altRotationPoint = value; - } - } - - internal Coordinate Scale = null; - - - //Transform _rotationPoint; - - /// - /// Items contained in this group - /// - internal protected List _childItems = new List(); - - public PieGroupItemBase(DrawingBase renderer) : base(renderer) - { - Bounds.Parent = TranslationOffset; - //_rotationPoint = Bounds; - } - - public PieGroupItemBase(DrawingBase renderer, double localXPos, double localYPos) : this(renderer) - { - Bounds.Left = localXPos; - Bounds.Top = localYPos; - } - - - public PieGroupItemBase(DrawingBase renderer, BoundingBox parent, double rotation, Transform rotationPoint = null) : this(renderer, 0, 0) - { - TranslationOffset.Parent = parent; - Rotation = rotation; - if (rotationPoint != null) - { - RotationPoint = new Point(rotationPoint.LocalPosition.X, rotationPoint.LocalPosition.Y); - } - } - - internal void SetRotationPointToCenterOfGroup(double rotation = double.NaN) - { - RotationPoint = new Point(Bounds.Width/2, Bounds.Height/2); - - if (double.IsNaN(rotation) == false) - { - Rotation = rotation; - } - } - - internal void AddChildItem(RenderItem item) - { - if (item is PieGroupItemBase) - { - var subGroup = (PieGroupItemBase)item; - subGroup.TranslationOffset.Parent = Bounds; - } - else - { - item.Bounds.Parent = Bounds; - } - _childItems.Add(item); - - Bounds.Width = item.Bounds.Right > Bounds.Width ? item.Bounds.Right : Bounds.Width; - Bounds.Height = item.Bounds.Bottom > Bounds.Height ? item.Bounds.Bottom : Bounds.Height; - } - - public override RenderItemType Type => RenderItemType.Group; - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/InnerGroup.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/InnerGroup.cs deleted file mode 100644 index 0bd1958efd..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/InnerGroup.cs +++ /dev/null @@ -1,31 +0,0 @@ -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using System.Collections.Generic; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Shared -{ - internal abstract class InnerGroup : RenderItem - { - /// - /// Items contained in this group - /// - internal protected List _childItems = new List(); - - public InnerGroup(DrawingBase renderer) : base(renderer) - { - } - - internal void AddChildItem(RenderItem item) - { - item.Bounds.Parent = Bounds; - - _childItems.Add(item); - - Bounds.Width = item.Bounds.Right > Bounds.Width ? item.Bounds.Right : Bounds.Width; - Bounds.Height = item.Bounds.Bottom > Bounds.Height ? item.Bounds.Bottom : Bounds.Height; - } - - public override RenderItemType Type => RenderItemType.Group; - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/ParagraphItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/ParagraphItem.cs deleted file mode 100644 index 6043768391..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/ParagraphItem.cs +++ /dev/null @@ -1,537 +0,0 @@ -using EPPlus.Fonts.OpenType; -using EPPlus.Fonts.OpenType.Integration; -using EPPlus.Fonts.OpenType.TextShaping; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Utils; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Statistical; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -using OfficeOpenXml.Interfaces.Drawing.Text; -using OfficeOpenXml.Style; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using EPPlusColorConverter = OfficeOpenXml.Utils.TypeConversion.ColorConverter; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Shared -{ - internal abstract class ParagraphItem : RenderItem - { - TextLayoutEngine _layout; - - double _leftMargin; - double _rightMargin; - - eDrawingTextLineSpacing _lsType; - double _lineSpacingAscendantOnly; - double? _lsMultiplier = null; - internal bool IsFirstParagraph { get; private set; } - List _paragraphLines = new List(); - protected List _textRunDisplayText = new List(); - - List _newTextFragments; - int _manualFragmentsStartIndex = -1; - List _manualFragments; - internal protected MeasurementFont _paragraphFont; - internal TextBodyItem ParentTextBody { get; set; } - internal double ParagraphLineSpacing { get; private set; } - internal eTextAlignment HorizontalAlignment { get; private set; } - internal List Runs { get; set; } = new List(); - - internal bool DisplayBounds { get; set; } = false; - - private List _lines; - - //Start temp workaround vars - string _textIfEmpty = null; - ExcelDrawingParagraph _p = null; - //end temp workaround vars - - private double? _centerAdjustment = null; - - internal List SpaceWidthsPerLine = new List(); - - bool LinespacingIsExact - { - get - { - return _lsMultiplier.HasValue == false; - } - } - - public ParagraphItem(TextBodyItem textBody, DrawingBase renderer, BoundingBox parent) : base(renderer, parent) - { - ParentTextBody = textBody; - Bounds.Name = "Paragraph"; - var defaultFont = new MeasurementFont { FontFamily = "Aptos Narrow", Size = 11, Style = MeasurementFontStyles.Regular }; - _paragraphFont = defaultFont; - - _layout = OpenTypeFonts.GetTextLayoutEngineForFont(defaultFont); - ParagraphLineSpacing = GetParagraphLineSpacingInPoints(100, (TextShaper)OpenTypeFonts.GetShaperForFont(defaultFont), defaultFont.Size); - } - - public ParagraphItem(TextBodyItem textBody, DrawingBase renderer, BoundingBox parent, ExcelDrawingParagraph p, string textIfEmpty = null) : base(renderer, parent) - { - ParentTextBody = textBody; - IsFirstParagraph = p == p._paragraphs[0]; - - if (p.DefaultRunProperties.Fill != null && p.DefaultRunProperties.Fill.IsEmpty == false) - { - if (IsFirstParagraph) - { - if (p.DefaultRunProperties.Fill != null) - { - SetDrawingPropertiesFill(p.DefaultRunProperties.Fill, null); - } - } - else - { - //Drawingproperties has fallback to firstDefault but excel does not display it so we should not either. - if (p.DefaultRunProperties != p._paragraphs.FirstDefaultRunProperties) - { - SetDrawingPropertiesFill(p.DefaultRunProperties.Fill, null); - } - else - { - var fc = EPPlusColorConverter.GetThemeColor(DrawingRenderer.Theme.ColorScheme.Light1); - fc = ColorUtils.GetAdjustedColor(PathFillMode.Norm, fc); - FillColor = "#" + fc.ToArgb().ToString("x8").Substring(2); - //Use shape fill somehow - //Maybe use a name property for fallback theme accent1 color? - } - } - } - else - { - if (p._paragraphs.FirstDefaultRunProperties != null && p._paragraphs.FirstDefaultRunProperties.Fill != null && p._paragraphs.FirstDefaultRunProperties.Fill.IsEmpty == false) - { - var fill = p._paragraphs.FirstDefaultRunProperties.Fill; - SetDrawingPropertiesFill(fill, null); - } - } - - //---Initialize Bounds / Margins-- - - Bounds.Name = "Paragraph"; - - var indent = 48 * p.IndentLevel; - _leftMargin = p.LeftMargin + p.Indent + indent; - _rightMargin = p.RightMargin; - - _leftMargin = _leftMargin.PixelToPoint(); - _rightMargin = _rightMargin.PixelToPoint(); - - HorizontalAlignment = p.HorizontalAlignment; - _leftMargin = _leftMargin.PixelToPoint(); - _rightMargin = _rightMargin.PixelToPoint(); - - HorizontalAlignment = p.HorizontalAlignment; - - if (ParentTextBody.AutoSize == false) - { - Bounds.Left = 0; - Bounds.Width = ParentTextBody.MaxWidth; - - //Left is equal to left Paragraph margin - //Textbody or Textbox are assumed to handle shape/chart margins - //Paragraph handles only indentations/margins that is applied ON TOP of those margins - //Paragraph left is the exact position where the text itself starts on the left - if (HorizontalAlignment != eTextAlignment.Center) - { - Bounds.Left = GetAlignmentHorizontal(HorizontalAlignment); - } - else - { - //Center is a bit strange the bounds really are the same as left or right aligned - //It doesn't truly matter as only left min and right max play a role - Bounds.Left = GetAlignmentHorizontal(eTextAlignment.Left); - _centerAdjustment = GetAlignmentHorizontal(HorizontalAlignment); - - } - Bounds.Width = parent.Width - _rightMargin - _leftMargin; - } - - //---Initialize / calculate lines and runs--- - //measurer must be set before AddLinesAndRichText - _paragraphFont = p.DefaultRunProperties.GetMeasureFont(); - - //---Get measurer--- - _layout = OpenTypeFonts.GetTextLayoutEngineForFont(_paragraphFont); - - //---Calculate linespacing--- - int numLines = _paragraphLines.Count; - _lsType = p.LineSpacing.LineSpacingType; - ParagraphLineSpacing = GetParagraphLineSpacingInPoints(p.LineSpacing.Value, - (TextShaper) OpenTypeFonts.GetShaperForFont(_paragraphFont), - _paragraphFont.Size); - - - ImportLinesAndTextRuns(p, textIfEmpty); - } - - private double GetParagraphLineSpacingInPoints(double spacingValue, TextShaper fmExact, float fontSize) - { - if (_lsType == eDrawingTextLineSpacing.Exactly) - { - if (IsFirstParagraph) - { - _lineSpacingAscendantOnly = spacingValue; - } - return spacingValue; - } - else - { - var multiplier = (spacingValue / 100); - _lsMultiplier = multiplier; - if (IsFirstParagraph) - { - _lineSpacingAscendantOnly = multiplier * fmExact.GetAscentInPoints(fontSize); - } - return multiplier * fmExact.GetLineHeightInPoints(fontSize); - } - } - - //public void AddOwnText(string text) - //{ - // var fragment = new TextFragment(); - // fragment.Text = text; - // fragment.Font = ParagraphFont; - // _manualFragments.Add(fragment); - - // //if(_newTextFragments == null) - // //{ - // // //This should probably never happen - // // throw new InvalidOperationException("Must GENERATE textfragments first in the constructor"); - // // //GenerateTextFragments(text); - // //} - // //else - // //{ - // // var fragment = new TextFragment(); - // // fragment.Text = text; - // // fragment.Font = ParagraphFont; - // // //_newTextFragments.Add(fragment); - // // _manualFragments.Add(fragment); - // //} - - // //Redo whole thing for now. - // //Import and wrapping really should be completely seperated but can't refactor all of it yet - // //AddTextLinesAndSpacing(_p, _textIfEmpty); - //} - - public void AddOwnText(TextFragment fragment) - { - //_manualFragments.Add(fragment); - - if (_newTextFragments == null) - { - //This should probably never happen - throw new InvalidOperationException("Must GENERATE textfragments first in the constructor"); - //GenerateTextFragments(text); - } - else - { - if(_manualFragmentsStartIndex == -1) - { - _manualFragmentsStartIndex = _newTextFragments.Count; - } - //_newTextFragments.Add(fragment); - _newTextFragments.Add(fragment); - } - - - //Redo whole thing for now. - //Import and wrapping really should be completely seperated but can't refactor all of it yet - AddTextLinesAndSpacing(_p, _textIfEmpty); - } - - - internal protected TextRunItem AddRenderItemTextRun(ExcelParagraphTextRunBase origTxtRun, string displayText) - { - var targetTxtRun = CreateTextRun(origTxtRun, Bounds, displayText); - - Runs.Add(targetTxtRun); - return targetTxtRun; - } - - private void AddText(string text, MeasurementFont font) - { - var container = CreateTextRun(font, Bounds, text); - Runs.Add(container); - - container.Bounds.Name = $"Container{Runs.Count}"; - } - - private void AddText(string text, ExcelTextFont font) - { - var mf = font.GetMeasureFont(); - var measurer = OpenTypeFonts.GetTextLayoutEngineForFont(mf); - - var container = CreateTextRun(text, font, Bounds, text); - Runs.Add(container); - //Bounds.Width = container.Bounds.Width + 0.001; //TODO: fix for equal width issue - container.Bounds.Name = $"Container{Runs.Count}"; - } - - void GenerateTextFragments(string text) - { - _newTextFragments = new List(); - - if (string.IsNullOrEmpty(text) == false) - { - var currentFrag = new TextFragment() { Text = text, Font = _paragraphFont}; - _newTextFragments.Add(currentFrag); - } - } - - /// - /// Log linebreak positions and sizes of the runs - /// So that we can easily know what textfragment is on what line and what size it has later - /// - /// - void GenerateTextFragments(ExcelDrawingTextRunCollection runs/*, string textIfEmpty*/) - { - List runContents = new List(); - List fonts = new List(); - - for (int i = 0; i < runs.Count(); i++) - { - var txtRun = runs[i]; - var runFont = txtRun.GetMeasurementFont(); - - fonts.Add(runFont); - runContents.Add(txtRun.Text); - } - - _newTextFragments = new List(); - - for (int i = 0; i < runContents.Count(); i++) - { - if (string.IsNullOrEmpty(runContents[i]) == false) - { - var currentFrag = new TextFragment() { Text = runContents[i], Font = fonts[i] }; - _newTextFragments.Add(currentFrag); - } - } - } - - internal void ImportLinesAndTextRunsDefault(string textIfEmpty) - { - GenerateTextFragments(textIfEmpty); - - if (HorizontalAlignment != eTextAlignment.Center) - { - Bounds.Left = GetAlignmentHorizontal(HorizontalAlignment); - } - else - { - Bounds.Left = GetAlignmentHorizontal(eTextAlignment.Left); - _centerAdjustment = GetAlignmentHorizontal(HorizontalAlignment); - } - - AddTextLinesAndSpacing(null, textIfEmpty); - } - private void ImportLinesAndTextRuns(ExcelDrawingParagraph p, string textIfEmpty) - { - if (p.TextRuns.Count == 0 && string.IsNullOrEmpty(textIfEmpty) == false) - { - ImportLinesAndTextRunsDefault(textIfEmpty); - } - else - { - //Log line positions and run sizes - GenerateTextFragments(p.TextRuns); - AddTextLinesAndSpacing(p, textIfEmpty); - } - } - - private void AddTextLinesAndSpacing(ExcelDrawingParagraph p, string textIfEmpty) - { - //Temp workaround - if (_p == null) - { - _p = p; - } - if (_textIfEmpty == null) - { - _textIfEmpty = textIfEmpty; - } - - _lines = WrapFragmentsToLines(); - - //In points - double lastDescent = 0; - double lineTop = 0; - double greatestWidth = 0; - - if (_lines != null && _lines.Count != 0) - { - //This could be moved into a textLines collection class - //START - var idxOfLargestLine = 0; - double widthOfLargestLine = _lines[0].GetWidthWithoutTrailingSpaces(); - - for (int i = 1; i < _lines.Count; i++) - { - if (_lines[i].Width > widthOfLargestLine) - { - var ctrLineWidth = _lines[i].GetWidthWithoutTrailingSpaces(); - SpaceWidthsPerLine.Add(_lines[i].lastFontSpaceWidth); - widthOfLargestLine = ctrLineWidth; - idxOfLargestLine = i; - } - } - //END - - - if (HorizontalAlignment == eTextAlignment.Center && ParentTextBody.AutoSize && _centerAdjustment != null && string.IsNullOrEmpty(textIfEmpty)) - { - //Bounds of the paragraph should be bounds of the text itself. - //Therefore we must know the starting point to set accurate left and offset from left. - Bounds.Left = _centerAdjustment.Value - (widthOfLargestLine / 2); - } - else - { - Bounds.Left = 0; - } - //if (ParentTextBody.AutoSize) - //{ - // //Bounds of the paragraph should be bounds of the text itself. - // //Therefore we must know the starting point to set accurate left and offset from left. - // Bounds.Left = 0; - //} - - foreach (var line in _lines) - { - double prevWidth = 0; - - if (HorizontalAlignment == eTextAlignment.Center) - { - var ctrLineWidth = line.GetWidthWithoutTrailingSpaces(); - //Calculate difference in widths and split to get offset between leftmost position and current line - prevWidth = (widthOfLargestLine - ctrLineWidth) / 2; - } - else if (HorizontalAlignment == eTextAlignment.Right) - { - //Note that the actual bounds with the space will be outside max bounds. - //This appears to be how excel does it - var ctrLineWidth = line.GetWidthWithoutTrailingSpaces(); - prevWidth = widthOfLargestLine - ctrLineWidth; - } - - if (LinespacingIsExact == false) - { - lineTop += line.LargestAscent + lastDescent; - } - else - { - lineTop += ParagraphLineSpacing; - } - if (line.GetWidthWithoutTrailingSpaces() > greatestWidth) - { - greatestWidth = line.GetWidthWithoutTrailingSpaces(); - } - - foreach (var lineFragment in line.LineFragments) - { - var displayText = lineFragment.Text; - - - if (p != null && p.TextRuns.Count == 0 && string.IsNullOrEmpty(textIfEmpty) == false) - { - //Import fallback text with paragraph settings - AddText(displayText, p.DefaultRunProperties); - } - else if (p != null && p.TextRuns.Count != 0) - { - var rtIdx = _newTextFragments.IndexOf(lineFragment.OriginalTextFragment); - if (rtIdx > p.TextRuns.Count - 1) - { - AddText(displayText, _newTextFragments[rtIdx].Font); - } - else - { - //Import Paragraph text run - var idx = _newTextFragments.IndexOf(lineFragment.OriginalTextFragment); - AddRenderItemTextRun(p.TextRuns[idx], displayText); - } - } - else - { - //Import fallback with default settings from constructor - AddText(displayText, _paragraphFont); - } - TextRunItem runItem = Runs.Last(); - runItem.Bounds.Left = prevWidth; - runItem.YPosition = lineTop; - - runItem.Bounds.Width = lineFragment.Width; - prevWidth += lineFragment.Width; - } - lastDescent = line.LargestDescent; - } - } - Bounds.Height = lineTop + lastDescent; - Bounds.Width = greatestWidth; - } - - List WrapFragmentsToLines(List fragments = null) - { - if(fragments == null ) - { - fragments = _newTextFragments; - } - - if (fragments.Count > 0) - { - if(_layout == null) - { - _layout = OpenTypeFonts.GetTextLayoutEngineForFont((fragments[0].Font)); - } - - var maxWidthPoints = Math.Round(ParentTextBody.MaxWidth, 0, MidpointRounding.AwayFromZero); - - _lines = _layout.WrapRichTextLines(fragments, maxWidthPoints); - return _lines; - } - return new List(); - } - - internal double GetAlignmentHorizontal(eTextAlignment txAlignment) - { - var area = Bounds; - double x = 0; - switch (txAlignment) - { - case eTextAlignment.Left: - default: - x = area.Left + _leftMargin; - break; - case eTextAlignment.Center: - x = (area.Right / 2) + _leftMargin - _rightMargin; - break; - case eTextAlignment.Right: - x = area.Right - _rightMargin; - break; - } - - return x; - } - - /// - /// Type of textrun defined by child type - /// - /// - /// - /// - /// - internal abstract TextRunItem CreateTextRun(ExcelParagraphTextRunBase run, BoundingBox parent, string displayText); - internal abstract TextRunItem CreateTextRun(string text, ExcelTextFont font, BoundingBox parent, string displayText); - internal abstract TextRunItem CreateTextRun(MeasurementFont font, BoundingBox parent, string displayText); - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/TextBodyItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/TextBodyItem.cs deleted file mode 100644 index 394e21a54b..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/TextBodyItem.cs +++ /dev/null @@ -1,274 +0,0 @@ -using EPPlus.Fonts.OpenType; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using OfficeOpenXml.Interfaces.Drawing.Text; -using OfficeOpenXml.Interfaces.Fonts; -using OfficeOpenXml.Style; -using OfficeOpenXml.Utils; -using System; -using System.Collections.Generic; -using System.Linq; - - -namespace EPPlus.Export.ImageRenderer.RenderItems.Shared -{ - /// - /// Margin left = X - /// Margin right = Y - /// - internal abstract class TextBodyItem : DrawingObject - { - public TextBodyItem(DrawingBase renderer, BoundingBox parent, bool autoSize) : base(renderer, parent) - { - Bounds.Name = "TxtBody"; - Bounds.Parent = parent; - AutoSize = autoSize; - } - public TextBodyItem(DrawingBase renderer, BoundingBox parent, double maxWidth, double maxHeight) : base(renderer, parent) - { - Bounds.Name = "TxtBody"; - Bounds.Parent = parent; - MaxWidth = maxWidth; - MaxHeight = maxHeight; - AutoSize = false; - } - internal bool AutoSize { get; set; } - internal double MaxWidth { get; set; } - internal double MaxHeight { get; set; } - - /// - /// Shorthand for Bounds.Width - /// - internal double Width { get { return Bounds.Width; } set { Bounds.Width = value; } } - - /// - /// Shorthand for Bounds.Height - /// - internal double Height { get { return Bounds.Height; } set { Bounds.Height = value; } } - - internal string FontColorString { get; set; } - - internal eTextAnchoringType VerticalAlignment = eTextAnchoringType.Top; - - internal abstract List Paragraphs { get; set; } - - public bool AllowOverflow; - - internal bool WrapText = true; - - internal string _text; - - public void ImportParagraph(ExcelDrawingParagraph item, double startingY, string text=null) - { - var measureFont = item.DefaultRunProperties.GetMeasureFont(); - bool isFirst = Paragraphs.Count == 0; - - var paragraph = CreateParagraph(this, item, Bounds, text); - paragraph.Bounds.Name = $"Container{Paragraphs.Count}"; - paragraph.Bounds.Top = startingY; - _text = text; - - if(AutoSize) - { - if (Paragraphs.Count == 0) - { - Bounds.Height = paragraph.Bounds.Height; - } - else - { - Bounds.Height += paragraph.Bounds.Height; - } - - if (Bounds.Width < paragraph.Bounds.Width || (Bounds.Width == MaxWidth && Paragraphs.Count == 0)) - { - Bounds.Width = paragraph.Bounds.Width; - } - } - Paragraphs.Add(paragraph); - } - - public void AddParagraph(double startingY, string text = null) - { - var paragraph = CreateParagraph(this, Bounds, text); - paragraph.Bounds.Name = $"Container{Paragraphs.Count}"; - paragraph.Bounds.Top = startingY; - _text = text; - - if (AutoSize) - { - if (Paragraphs.Count == 0) - { - Bounds.Height = paragraph.Bounds.Height; - } - else - { - Bounds.Height += paragraph.Bounds.Height; - } - - if (Bounds.Width < paragraph.Bounds.Width || (Bounds.Width == MaxWidth && Paragraphs.Count == 0)) - { - Bounds.Width = paragraph.Bounds.Width; - } - } - Paragraphs.Add(paragraph); - } - - internal void SetHorizontalAlignmentPosition() - { - //if (AutoSize) - //{ - foreach (var p in Paragraphs) - { - switch (p.HorizontalAlignment) - { - case eTextAlignment.Left: - p.Bounds.Left = 0; - break; - case eTextAlignment.Center: - p.Bounds.Left = (Bounds.Width / 2) - (p.Bounds.Width / 2); - break; - case eTextAlignment.Right: - p.Bounds.Left = Bounds.Right - p.Bounds.Width; - break; - case eTextAlignment.Distributed: - case eTextAlignment.Justified: - case eTextAlignment.JustifiedLow: - case eTextAlignment.ThaiDistributed: - p.Bounds.Left = 0; //TODO: Set left for now as we do not support distributed spacing yet - break; - } - } - //} - } - - internal virtual void ImportTextBody(ExcelTextBody body, ExcelHorizontalAlignment horizontalDefault = ExcelHorizontalAlignment.Left) - { - _text = null; - VerticalAlignment = body.Anchor; - - //We already apply bounds top via the parent Transform - double currentHeight = 0; - double largestWidth = double.MinValue; - - foreach (var paragraph in body.Paragraphs) - { - ImportParagraph(paragraph, currentHeight); - var addedPara = Paragraphs.Last(); - currentHeight = addedPara.Bounds.Bottom; - largestWidth = Math.Max(largestWidth, addedPara.Bounds.Width); - } - - foreach (var paragraph in body.Paragraphs) - { - SetHorizontalAlignmentPosition(); - } - - if (Paragraphs != null && Paragraphs.Count() > 0) - { - Bounds.Height = currentHeight; - } - - Bounds.Top = GetAlignmentVertical(); - } - - private double GetParagraphAscendantSpacingInPixels(eDrawingTextLineSpacing lineSpacingType, double spacingValue, ITextShaper fmExact, float fontSize, out double multiplier) - { - if (lineSpacingType == eDrawingTextLineSpacing.Exactly) - { - multiplier = -1; - return spacingValue.PointToPixel(); - } - else - { - multiplier = (spacingValue / 100); - return multiplier * fmExact.GetAscentInPoints(fontSize).PointToPixel(); - } - } - - - //public void AddText(string text, FontMeasurerTrueType measurer) - //{ - // if (Paragraphs.Count == 0) - // { - // AddParagraph(text, measurer); - // } - // else - // { - // Paragraphs.Last().AddText(text, measurer); - // } - //} - //internal void AddText(string text, ExcelTextFont font) - //{ - // //Document Top position for the paragraph text based on vertical alignment - // var posY = GetAlignmentVertical(); - // //var vertAlignAttribute = GetVerticalAlignAttribute(posY); - - // //var measurer = font.PictureRelationDocument.Package.Settings.TextSettings.GenericTextMeasurerTrueType; - // //Limit bounding area with the space taken by previous paragraphs - // //Note that this is ONLY identical to PosY if the vertical alignment is top - - // //The first run in the first paragraph must apply different line-spacing - // //var svgParagraph = new SvgParagraph(text, font, area, vertAlignAttribute, posY); - // var paragraph = CreateParagraph(Bounds); - // paragraph.AddText(text, font); - // paragraph.FillColor = font.Fill.Color.To6CharHexString(); - - // Paragraphs.Add(paragraph); - //} - //public void SetMeasurer(FontMeasurerTrueType fontMeasurer) - //{ - // _measurer = fontMeasurer; - //} - - double? _alignmentY = null; - - /// - /// Get the start of text space vertically - /// - /// - /// - private double GetAlignmentVertical() - { - double alignmentY = 0; - - switch (VerticalAlignment) - { - case eTextAnchoringType.Top: - alignmentY = Bounds.Top; - break; - //Center means center of a Shape's ENTIRE bounding box height. - //Not center of the Inset GetRectangle - case eTextAnchoringType.Center: - alignmentY = (MaxHeight - Bounds.Height)/2 + Bounds.Top; - break; - case eTextAnchoringType.Bottom: - alignmentY = MaxHeight - Bounds.Height; - break; - } - - _alignmentY = alignmentY; - - return _alignmentY.Value; - } - - - /// - /// Each file format defines its own paragraph - /// - /// - internal abstract ParagraphItem CreateParagraph(TextBodyItem textBody, ExcelDrawingParagraph paragraph, BoundingBox parent, string textIfEmpty=""); - - internal abstract ParagraphItem CreateParagraph(TextBodyItem textBody, BoundingBox parent, string textIfEmpty = ""); - - /// - /// Each file format defines its own paragraph - /// - /// - internal abstract ParagraphItem CreateParagraph(TextBodyItem textBody, BoundingBox parent); - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/TextRunCollection.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/TextRunCollection.cs deleted file mode 100644 index 8f631f2633..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/TextRunCollection.cs +++ /dev/null @@ -1,50 +0,0 @@ -using OfficeOpenXml.Drawing.Chart; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Shared -{ - internal class TextRunCollection : IEnumerable - { - private List _textRunItems; - - public void Add(TextRunItem item) - { - _textRunItems.Add(item); - } - - /// - /// Number of items in the collection - /// - public int Count - { - get - { - return _textRunItems.Count; - } - } - IEnumerator IEnumerable.GetEnumerator() - { - return _textRunItems.GetEnumerator(); - } - IEnumerator IEnumerable.GetEnumerator() - { - return _textRunItems.GetEnumerator(); - } - /// - /// Returns a textrun at position - /// - /// The position of the chart. 0-base - /// - public TextRunItem this[int PositionID] - { - get - { - return (_textRunItems[PositionID]); - } - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/TextRunItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/TextRunItem.cs deleted file mode 100644 index 4b3f1d544d..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/TextRunItem.cs +++ /dev/null @@ -1,204 +0,0 @@ -using EPPlus.Fonts.OpenType; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Interfaces.Drawing.Text; -using OfficeOpenXml.Style; -using OfficeOpenXml.Utils; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text.RegularExpressions; -using static System.Net.Mime.MediaTypeNames; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Shared -{ - internal abstract class TextRunItem : RenderItem - { - public override RenderItemType Type => RenderItemType.Text; - - internal readonly string _originalText; - protected string _currentText; - - internal protected MeasurementFont _measurementFont; - internal protected bool _isFirstInParagraph; - - internal double FontSizeInPixels { get; private set; } - - public List Lines { get; private set; } - - protected internal bool _isItalic = false; - protected internal bool _isBold = false; - protected internal eUnderLineType _underLineType; - protected internal eStrikeType _strikeType; - protected internal Color _underlineColor; - protected internal double _baseline; - - internal double YPosition { get; set; } - internal double ClippingHeight = double.NaN; - - internal TextRunItem(DrawingBase renderer, BoundingBox parent, MeasurementFont font, string displayText) : base(renderer, parent) - { - _originalText = displayText; - - Bounds.Name = "TextRun"; - _currentText = displayText; - - Lines = Regex.Split(_currentText, "\r\n|\r|\n").ToList(); - - _measurementFont = font; - _isFirstInParagraph = true; - - FontSizeInPixels = ((double)_measurementFont.Size).PointToPixel(true); - Bounds.Height = _measurementFont.Size; - if (parent.Height < _measurementFont.Size) - { - parent.Height = _measurementFont.Size; - } - - //To get clipping height we need to get the textbody bounds - if (parent != null && parent.Parent != null && parent.Parent.Parent != null) - { - ClippingHeight = parent.Parent.Parent.Position.Y + parent.Parent.Parent.Size.Y; - } - if (Lines.Count == 1) - { - //Bounds.Width = parent.Width; - GetBounds(out double il, out double it, out double ir, out double ib); //TODO: remove when calc works - } - else - { - //Measure text. - GetBounds(out double il, out double it, out double ir, out double ib); //TODO: remove when calc works - } - _underLineType = eUnderLineType.None; - } - - internal TextRunItem(DrawingBase renderer, BoundingBox parent, string text, ExcelTextFont font, string displayText) : base(renderer, parent) - { - _originalText = text; - - Bounds.Name = "TextRun"; - _currentText = string.IsNullOrEmpty(displayText) ? _originalText : displayText; - - Lines = Regex.Split(_currentText, "\r\n|\r|\n").ToList(); - - //_measurer = new FontMeasurerTrueType(); - - _measurementFont = font.GetMeasureFont(); - //_measurer.SetFont(_measurementFont); - - _isFirstInParagraph = true; - - //_fontStyles = _measurementFont.Style; - - _baseline = font.Baseline; - if (_baseline != 0) - { - _measurementFont.Size *= (float)(1 - (Math.Abs(_baseline) / 100)); - } - FontSizeInPixels = ((double)_measurementFont.Size).PointToPixel(true); - - Bounds.Height = _measurementFont.Size; - if (parent.Height < _measurementFont.Size) - { - parent.Height = _measurementFont.Size; - } - //_horizontalTextAlignment = TextAlignment.Center; - - if (font.Fill.Style == eFillStyle.SolidFill) - { - FillColor = "#" + font.Fill.Color.To6CharHexString(); - } - - //To get clipping height we need to get the textbody bounds - if (parent != null && parent.Parent != null && parent.Parent.Parent != null) - { - ClippingHeight = parent.Parent.Parent.Position.Y + parent.Parent.Parent.Size.Y; - } - if(Lines.Count==1) - { - //Bounds.Width = parent.Width; - GetBounds(out double il, out double it, out double ir, out double ib); //TODO: remove when calc works - } - else - { - //Measure text. - GetBounds(out double il, out double it, out double ir, out double ib); //TODO: remove when calc works - } - _isItalic = font.Italic; - _isBold = font.Bold; - _underLineType = font.UnderLine; - _underlineColor = font.UnderLineColor; - _strikeType = font.Strike; - } - - /// - /// If the run has been wrapped more line-breaks may have been added in displayText - /// - /// - /// - /// - internal TextRunItem(DrawingBase renderer, BoundingBox parent, ExcelParagraphTextRunBase run, string displayText = "") : base(renderer, parent) - { - _originalText = run.Text; - - Bounds.Name = "TextRun"; - _currentText = string.IsNullOrEmpty(displayText) ? _originalText : displayText; - - Lines = Regex.Split(_currentText, "\r\n|\r|\n").ToList(); - - _measurementFont = run.GetMeasurementFont(); - - - //_fontStyles = _measurementFont.Style; - - _baseline = run.Baseline; - FontSizeInPixels = ((double)_measurementFont.Size).PointToPixel(true); - - Bounds.Height = _measurementFont.Size; - - //_horizontalTextAlignment = run.Paragraph.HorizontalAlignment; - - if (run.Fill.IsEmpty == false && run.Fill.Style == eFillStyle.SolidFill) - { - FillColor = "#" + run.Fill.Color.To6CharHexString(); - } - - //To get clipping height we need to get the textbody bounds - if( parent!= null && parent.Parent != null && parent.Parent.Parent != null) - { - ClippingHeight = ((BoundingBox)parent.Parent.Parent).Bottom; - } - - if (run.Fill.Style == eFillStyle.SolidFill) - { - FillColor = "#" + run.Fill.Color.To6CharHexString(); - } - - _isItalic = run.FontItalic; - _isBold = run.FontBold; - _underLineType = run.FontUnderLine; - _underlineColor = run.UnderLineColor; - _strikeType = run.FontStrike; - } - - /// - /// Calculates right/bottom - /// - /// - /// - /// - /// - internal override void GetBounds(out double il, out double it, out double ir, out double ib) - { - il = Bounds.Left; - it = Bounds.Top; - ir = Bounds.Right; - ib = Bounds.Bottom; - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/TransformGroup.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/TransformGroup.cs deleted file mode 100644 index a7f738444a..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Shared/TransformGroup.cs +++ /dev/null @@ -1,100 +0,0 @@ -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using System.Collections.Generic; - -namespace EPPlus.Export.ImageRenderer.RenderItems.Shared -{ - internal abstract class TransformGroup : RenderItem - { - protected InnerGroup _innerGroup; - - Point PositionAfterTransform; - - /// - /// In degrees - /// - internal double Rotation = double.NaN; - - BoundingBox _altRotationPoint = null; - - internal BoundingBox RotationPoint - { - get - { - if (_altRotationPoint == null) - { - return Bounds; - } - return _altRotationPoint; - } - set - { - _altRotationPoint = value; - } - } - Coordinate _scale = new Coordinate(1,1); - - internal Coordinate Scale - { - get - { - return _scale; - } - set - { - Bounds.Parent.Scale = new Graphics.Math.Vector2(value.X, value.Y); - _scale = value; - } - } - - public TransformGroup(DrawingBase renderer) : base(renderer) - { - _innerGroup = CreateInnerGroup(); - _innerGroup.Bounds.Parent = Bounds; - } - - public TransformGroup(DrawingBase renderer, double localXPos, double localYPos) : this(renderer) - { - Bounds.Left = localXPos; - Bounds.Top = localYPos; - } - - - public TransformGroup(DrawingBase renderer, BoundingBox parent, double rotation, Transform rotationPoint = null) : this(renderer, 0, 0) - { - Bounds.Parent = parent; - Rotation = rotation; - if (rotationPoint != null) - { - RotationPoint = new BoundingBox(rotationPoint.LocalPosition.X, rotationPoint.LocalPosition.Y); - } - } - - internal void SetRotationPointToCenterOfGroup(double rotation = double.NaN) - { - RotationPoint = new BoundingBox(Bounds.Width / 2, Bounds.Height / 2); - - if (double.IsNaN(rotation) == false) - { - Rotation = rotation; - } - } - - /// - /// Adds child item to the group under this group - /// - /// - internal void AddChildItem(RenderItem item) - { - _innerGroup.AddChildItem(item); - - Bounds.Width = item.Bounds.Right > Bounds.Width ? item.Bounds.Right : Bounds.Width; - Bounds.Height = item.Bounds.Bottom > Bounds.Height ? item.Bounds.Bottom : Bounds.Height; - } - - internal abstract InnerGroup CreateInnerGroup(); - - public override RenderItemType Type => RenderItemType.Group; - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Svg/SvgParagraph.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Svg/SvgParagraph.cs deleted file mode 100644 index bf9136ae95..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Svg/SvgParagraph.cs +++ /dev/null @@ -1,161 +0,0 @@ -using EPPlusImageRenderer.FontData; -using EPPlusImageRenderer.RenderItems.Shared; -using EPPlusImageRenderer.Svg; -using EPPlusImageRenderer.Text; -using EPPlusImageRenderer.Utils; -using Microsoft.Extensions.Primitives; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Interfaces.Drawing.Text; -using OfficeOpenXml.Style; -using System.Drawing; -using System.Globalization; -using System.Text; - - -namespace EPPlusImageRenderer.RenderItems.Svg -{ - internal class SvgParagraph : SvgRenderItem - { - public override void Render(StringBuilder sb) - { - sb.Append(""); - - foreach (var textRun in TextRuns) - { - textRun.Render(sb); - } - - sb.Append(""); - } - - public override SvgItemType Type => SvgItemType.Text; - - internal override RenderItem Clone(SvgShape svgDocument) - { - throw new NotImplementedException(); - } - - internal override void GetBounds(out double il, out double it, out double ir, out double ib) - { - il = ParagraphArea.Left + LeftMargin; - it = ParagraphArea.Right - RightMargin; - ir = ParagraphArea.Top; - ib = ParagraphArea.Bottom; - } - - string vertAlignAttribute = ""; - protected List TextRuns = new List(); - - //ExcelDrawingParagraph Paragraph; - double RightMargin; - double LeftMargin; - - eTextAlignment HorizontalAlignment; - - RectBase ParagraphArea; - - ExcelDrawingParagraph Paragraph; - - /// - /// X position - /// - protected double XPos; - - string fontName; - - /// - /// Line-Spacing in pixels - /// - protected double LineSpacing; - MeasurementFont defaultMeasureFont; - - public SvgParagraph(ExcelDrawingParagraph p, RectBase paragraphArea, FontMeasurerExact fmExact, string VertAlign) - { - Paragraph = p; - defaultMeasureFont = p.DefaultRunProperties.GetMeasureFont(); - vertAlignAttribute = VertAlign; - //Seperated out in case of some final render item - //needing to adjust without changing the original values - RightMargin = p.RightMargin; - LeftMargin = p.LeftMargin; - - - - ParagraphArea = paragraphArea; - HorizontalAlignment = p.HorizontalAlignment; - - LineSpacing = GetParagraphLineSpacingInPixels(fmExact); - XPos = GetAlignmentHorizontal(HorizontalAlignment); - } - - private string GetHorizontalAlignmentAttribute(double indentX) - { - string ret = ""; - var xStr = indentX.ToString(CultureInfo.InvariantCulture); - - switch (HorizontalAlignment) - { - default: - case eTextAlignment.Left: - ret = $"text-anchor=\"start\" x=\"{xStr}\" "; - break; - case eTextAlignment.Center: - ret = $"text-anchor=\"middle\" x=\"{xStr}\" "; - break; - case eTextAlignment.Right: - - ret = $"text-anchor=\"end\" x=\"{xStr}\" "; - break; - } - - return ret; - } - - private double GetParagraphLineSpacingInPixels(FontMeasurerExact fmExact) - { - if (Paragraph.LineSpacing.LineSpacingType == eDrawingTextLineSpacing.Exactly) - { - return Paragraph.LineSpacing.Value.PointToPixel(); - } - else - { - var multiplier = (Paragraph.LineSpacing.Value / 100); - return multiplier * fmExact.GetSingleLineSpacingPX(); - } - } - - internal double GetAlignmentHorizontal(eTextAlignment txAlignment) - { - var area = ParagraphArea; - double x = 0; - switch (txAlignment) - { - case eTextAlignment.Left: - default: - x = area.Left; - break; - case eTextAlignment.Center: - x = (area.Right / 2) + LeftMargin - RightMargin; - break; - case eTextAlignment.Right: - x = area.Right - RightMargin; - break; - } - - return TextUtils.RoundToWhole(x); - } - - internal void AddTextRun(ExcelParagraphTextRunBase txtRun) - { - GetBounds(out double l, out double t, out double r, out double b); - var textMaxWidth = r - l; - var textRun = new SvgTextRun(txtRun, LineSpacing, textMaxWidth); - TextRuns.Add(textRun); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/Svg/SvgTextRun.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/Svg/SvgTextRun.cs deleted file mode 100644 index b0f448b408..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/Svg/SvgTextRun.cs +++ /dev/null @@ -1,177 +0,0 @@ -using EPPlusImageRenderer.FontData; -using EPPlusImageRenderer.Svg; -using EPPlusImageRenderer.Text; -using EPPlusImageRenderer.Utils; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Interfaces.Drawing.Text; -using System.Globalization; -using System.Text; - -namespace EPPlusImageRenderer.RenderItems.Svg -{ - internal class SvgTextRun : SvgRenderItem - { - double LineSpacingPerNewLine; - double yPosition; - double yClipHeight; - double fontSizeInPixels; - eTextAlignment horizontalTextAlignment; - string textContent; - - internal RectBase BoundingBox = new(); - internal Coordinate origin = new Coordinate(0, 0); - internal ExcelParagraphTextRunBase TextRun; - List Lines; - - MeasurementFont measurementFont; - - FontMeasurerExact fmExact; - - //Unnecesary?? - double TextLengthInPixels; - - string horizontalAttribute; - - /// - /// constructor. enter linespacing in pixels - /// - /// - /// - internal SvgTextRun(ExcelParagraphTextRunBase textRun, double lineSpacing, double textMaxWidth) : base() - { - TextRun = textRun; - Lines = SplitIntoLines(); - - measurementFont = TextRun.GetMeasureFont(); - - fmExact = new FontMeasurerExact(measurementFont); - - horizontalTextAlignment = TextRun.Paragraph.HorizontalAlignment; - - LineSpacingPerNewLine = lineSpacing; - - //No idea how to get this property now since we have no access to textbody - bool wrapText = true; - if (wrapText) - { - CalculateTextWrapping(textMaxWidth); - } - } - public RectBase textArea; - - public override SvgItemType Type => SvgItemType.TSpan; - - public override void Render(StringBuilder sb) - { - string finalString = ""; - - foreach (var line in Lines) - { - yPosition += TextUtils.RoundToWhole(LineSpacingPerNewLine); - string visibility = ""; - if (yPosition >= yClipHeight) - { - visibility = "display=\"none\""; - } - - finalString += $""; - finalString += line; - finalString += ""; - } - - sb.Append(finalString); - //throw new NotImplementedException(); - } - - internal override RenderItem Clone(SvgShape svgDocument) - { - throw new NotImplementedException(); - } - - internal double GetNumberOfLines() - { - if (TextRun != null) - { - SplitIntoLines(); - return Lines.Count; - } - else - { - return 0; - } - } - - internal List SplitIntoLines() - { - return TextRun.Text.Split("\r\n").ToList(); - } - - internal override void GetBounds(out double il, out double it, out double ir, out double ib) - { - il = origin.X; - it = origin.Y; - - ir = CalculateRightPositionInPixels(); - ib = CalculateBottomPositionInPixels(); - - BoundingBox.Left = il; - BoundingBox.Top = it; - BoundingBox.Right = ir; - BoundingBox.Bottom = ib; - } - - internal double CalculateRightPositionInPixels() - { - var numLines = GetNumberOfLines(); - double retPos = origin.X; - - if (numLines <= 0 || string.IsNullOrEmpty(TextRun.Text)) - { - TextLengthInPixels = 0; - return retPos; - } - - double longestWidth = -1; - - for (int i = 0; i < numLines; i++) - { - var text = Lines[i]; - var width = CalculateTextWidth(Lines[i]); - - if (width > longestWidth) - { - longestWidth = width; - } - } - - TextLengthInPixels = longestWidth; - - retPos += longestWidth; - - return retPos; - } - internal double CalculateBottomPositionInPixels() - { - var bottomPosition = (((double)TextRun.FontSize).PointToPixel() + origin.Y) * GetNumberOfLines(); - return bottomPosition; - } - - internal double CalculateTextWidth(string targetString) - { - var textMesurer = new FontMeasurerExact(measurementFont); - textMesurer.MeasureWrappedTextCells = true; - var width = textMesurer.MeasureTextWidth(targetString); - - return width; - } - - internal void CalculateTextWrapping(double maxWidth) - { - List NewContentLines = new List(); - var textMesurer = new FontMeasurerExact(measurementFont); - - var newLines = textMesurer.MeasureAndWrapText(TextRun.Text, measurementFont, maxWidth); - Lines = newLines; - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgBaseRenderer.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgBaseRenderer.cs deleted file mode 100644 index 3cf77c09aa..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgBaseRenderer.cs +++ /dev/null @@ -1,48 +0,0 @@ -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml.Drawing; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems -{ - internal static class SvgBaseRenderer - { - public static void BaseRender(StringBuilder sb, RenderItem item) - { - if (string.IsNullOrEmpty(item.FillColor) == false) - { - sb.Append($"fill=\"{item.FillColor}\" "); - if (item.FillOpacity != null && item.FillOpacity != 1) - { - sb.Append($"opacity=\"{item.FillOpacity.Value.ToString(CultureInfo.InvariantCulture)}\" "); - } - } - if (string.IsNullOrEmpty(item.FilterName) == false) - { - sb.Append($"filter=\"{item.FilterName}\" "); - } - - if (item.BorderWidth.HasValue) - { - if (string.IsNullOrEmpty(item.BorderColor) == false) - { - sb.Append($"stroke=\"{item.BorderColor}\" "); - } - var v = item.BorderWidth.Value * ExcelDrawing.EMU_PER_POINT / ExcelDrawing.EMU_PER_PIXEL; - sb.Append($"stroke-width=\"{v.ToString(CultureInfo.InvariantCulture)}\" "); - - if (item.BorderDashArray != null) - { - var BorderDashArrayStr = item.BorderDashArray.Select(x => - x.ToString(CultureInfo.InvariantCulture)).ToArray(); - - sb.Append($"stroke-dasharray=\"" + $"{string.Join(",", BorderDashArrayStr)}\" "); - } - } - sb.Append($"stroke-miterlimit =\"8\" "); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgDrawingCommands.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgDrawingCommands.cs deleted file mode 100644 index b5087795db..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgDrawingCommands.cs +++ /dev/null @@ -1,181 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -//using OfficeOpenXml.Drawing; -//using OfficeOpenXml.Utils.TypeConversion; -//using System; -//using System.Reflection; -//using System.Textbox.Json; -//using System.Windows; -//using System.Xml; -//namespace EPPlusImageRenderer.RenderItems -//{ -// public static class SvgDrawingCommands -// { -// public static void LoadShapes() -// { -// Shapes.Clear(); -// var br = new BinaryReader(new FileStream("c:\\temp\\Shapedrawing.bin", FileMode.Open)); -// while (br.BaseStream.Position < br.BaseStream.Length) -// { -// var shapeStyle = (eShapeStyle)br.ReadByte(); -// var list = new List(); -// var adj = ShapeAdjustments.ContainsKey(shapeStyle) ? ShapeAdjustments[shapeStyle] : null; -// Shapes.Add(shapeStyle, list); -// var ix = 0; -// do -// { -// var recType = br.ReadByte(); -// if (recType == 0) break; -// SvgAdjustmentPoint adjPoint = null; -// if (adj != null && adj.ContainsKey(ix & 0x1FF)) -// { -// adjPoint = adj[ix]; -// } - -// switch (recType) -// { -// case 1: //rect -// list.Add(ReadRect(br)); -// break; -// case 2: //path -// list.Add(ReadPath(br)); -// break; -// } -// ix++; -// } -// while (br.BaseStream.Position < br.BaseStream.Length); -// } -// } - -// private static SvgRenderPathItem ReadPath(BinaryReader br) -// { -// var fill = br.ReadByte(); -// var stroke = br.ReadByte(); -// var commandCount = br.ReadByte(); -// var item = new SvgRenderPathItem(); -// for (var i = 0; i < commandCount; i++) -// { -// var ct = (PathCommandType)br.ReadByte(); -// var itemCount = br.ReadInt16(); -// var l = new List(); -// for (int j = 0; j < itemCount; j++) -// { -// l.Add(br.ReadSingle()); -// } -// var cmd = new PathCommands(ct, item, l.ToArray()); -// //if (adj != null && (adj.Commands == null || adj.Commands.Any(x => x.Index == i))) -// //{ -// // cmd.AdjustmentPoint = adj; -// // if (adj.Commands != null) -// // { -// // cmd.CommandIndex = adj.Commands.FindIndex(x => x.Index == i); -// // } -// //} -// item.Commands.Add(cmd); -// } - -// item.FillColorSource = (PathFillMode)fill; -// item.BorderColorSource = (PathFillMode)stroke; - -// return item; -// } - -// private static SvgRenderRectItem ReadRect(BinaryReader br) -// { -// var fill = br.ReadByte(); -// var stroke = br.ReadByte(); -// var x = br.ReadSingle(); -// var y = br.ReadSingle(); -// var width = br.ReadSingle(); -// var height = br.ReadSingle(); - -// return new SvgRenderRectItem() { Left = x, Top = y, Width = width, Height = height, FillColorSource = (PathFillMode)fill, BorderColorSource = (PathFillMode)stroke }; -// } -// public static string SerializeShapeAdjustments() -// { -// var s = JsonSerializer.Serialize(ShapeAdjustments); -// return new StringReader(s).ReadToEnd(); -// } -// public static void DeSerializeShapeAdjustments(string json) -// { -// ShapeAdjustments.Clear(); -// LoadShapes(); -// //MessageBox.Show("Json Loaded"); -// } - -// public static void LoadAdjustments() -// { -// var path = new FileInfo(Assembly.GetExecutingAssembly().FullName).DirectoryName; -// ShapeAdjustments = new Dictionary>(); -// foreach (var f in Directory.GetFiles(path + "\\Adjustments\\", "*.xml")) -// { -// ReadAdjustmentXml(f, ShapeAdjustments); -// } -// } - -// private static void ReadAdjustmentXml(string f, Dictionary> shapeAdjustments) -// { -// var xml = new XmlDocument(); -// xml.Load(f); - -// var style = (eShapeStyle)Enum.Parse(typeof(eShapeStyle), xml.SelectSingleNode("adjust/@type").Value); -// var adjs = new Dictionary(); -// foreach (XmlElement objNode in xml.SelectNodes("adjust/objects/object")) -// { -// var adj = new SvgAdjustmentPoint(); -// adj.ItemIndex = int.Parse(objNode.GetAttribute("ix")); -// adj.AdjustmentType = (AdjustmentType)Enum.Parse(typeof(AdjustmentType), objNode.GetAttribute("adjustmentType")); -// foreach (XmlElement cmdNode in objNode.SelectNodes("commands/command")) -// { -// var ix = int.Parse(cmdNode.GetAttribute("ix")); -// var cmd = new SvgCommand(ix); -// if (adj.Commands == null) adj.Commands = new List(); -// adj.Commands.Add(cmd); -// foreach (XmlElement ptNode in cmdNode.SelectNodes("pt")) -// { -// var ptIx = short.Parse(ptNode.GetAttribute("ix")); -// var coordinate = new SvgCoordinate(ptIx); -// if (string.IsNullOrEmpty(ptNode.GetAttribute("adjustPointName"))==false) -// { -// coordinate.PointName = ptNode.GetAttribute("adjustPointName"); -// } -// else -// { -// coordinate.PointName = ""; -// } - -// if (ConvertUtil.TryParseIntString(ptNode.GetAttribute("origin"), out int r)) -// { -// coordinate.Origin = (short)r; -// } -// else -// { -// coordinate.Origin = 0; -// } -// if (Enum.TryParse(ptNode.GetAttribute("type"), true, out var result)) -// { -// coordinate.BulletType = result; -// } -// cmd.Coordinates.Add(ptIx, coordinate); -// } -// } -// adjs.Add(adj.ItemIndex, adj); -// } -// shapeAdjustments.Add(style, adjs); -// } - -// internal static Dictionary> Shapes { get; } = new Dictionary>(); -// internal static Dictionary> ShapeAdjustments { get; set; } = new Dictionary>(); -// } - -//} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgExtensions.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgExtensions.cs deleted file mode 100644 index 442d8a6ee2..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using System; - -namespace EPPlusImageRenderer.RenderItems -{ - internal static class SvgExtensions - { - internal static char AsCommandChar(this PathCommandType type) - { - switch (type) - { - case PathCommandType.Move: - return 'M'; - case PathCommandType.Line: - return 'L'; - case PathCommandType.HorizontalLine: - return 'H'; - case PathCommandType.VerticalLine: - return 'V'; - case PathCommandType.CubicBézier: - return 'C'; - case PathCommandType.QuadraticBézier: - return 'Q'; - case PathCommandType.Arc: - return 'A'; - case PathCommandType.End: - return 'Z'; - default: - throw new NotImplementedException("SVG path type not implemented"); - } - } - } - -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgGroupItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgGroupItem.cs deleted file mode 100644 index 0b6f047f72..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgGroupItem.cs +++ /dev/null @@ -1,113 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing.Theme; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using System.Drawing; -using System.Globalization; -using System.Text; - -namespace EPPlusImageRenderer.RenderItems -{ - internal class SvgEndGroupItem : RenderItem - { - internal SvgEndGroupItem(DrawingBase renderer, BoundingBox parent) : base(renderer, parent) - { - - } - public override RenderItemType Type => throw new System.NotImplementedException(); - public override void Render(StringBuilder sb) - { - sb.Append(""); - } - } - internal class SvgGroupItem : RenderItem - { - public override RenderItemType Type => RenderItemType.Group; - - public string TextAnchor { get; set; } - - public string GroupTransform = ""; - - //internal SvgGroupItem(DrawingBase renderer, BoundingBox bounds) - - internal SvgGroupItem(DrawingBase renderer) : base(renderer) - { - } - - internal SvgGroupItem(DrawingBase renderer, double xPos, double yPos) : base(renderer) - { - GroupTransform = $"transform=\"translate({xPos.PointToPixelString()}, {yPos.PointToPixelString()})\""; - } - - internal SvgGroupItem(DrawingBase renderer, BoundingBox bounds) : base(renderer) - { - Bounds = bounds; - if (Bounds.Left != 0 || Bounds.Top != 0) - { - GroupTransform = $"transform=\"translate({Bounds.Left.PointToPixelString()}, {Bounds.Top.PointToPixelString()})\""; - } - } - internal SvgGroupItem(DrawingBase renderer, BoundingBox parent, double rotation) : base(renderer, parent) - { - var bounds = ""; - var rot = ""; - Bounds = parent; - if (Bounds.Left!=0 || Bounds.Top!=0) - { - bounds = $"translate({Bounds.Left.PointToPixelString()}, {Bounds.Top.PointToPixelString()})"; - } - if(rotation!=0) - { - rot = $"rotate({rotation.ToString(CultureInfo.InvariantCulture)})"; - } - - if(string.IsNullOrEmpty(bounds)==false && string.IsNullOrEmpty(rot)==false) - { - GroupTransform = $"transform=\"{bounds} {rot}\""; - } - else if(string.IsNullOrEmpty(bounds)==false) - { - GroupTransform = $"transform=\"{bounds}\""; - } - else if (string.IsNullOrEmpty(rot) == false) - { - GroupTransform = $"transform=\"{rot}\""; - } - } - - public override void Render(StringBuilder sb) - { - sb.Append($""); - } - - internal override void GetBounds(out double il, out double it, out double ir, out double ib) - { - il = 0; - it = 0; - ir = 1; - ib = 1; - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/ConnectionPointsMiddle.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/ConnectionPointsMiddle.cs deleted file mode 100644 index e8150275f4..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/ConnectionPointsMiddle.cs +++ /dev/null @@ -1,37 +0,0 @@ -using EPPlusImageRenderer; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -using System.Collections.Generic; - - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - internal class ConnectionPointsMiddle - { - internal Coordinate Left; - internal Coordinate Top; - internal Coordinate Right; - internal Coordinate Bottom; - - internal Dictionary Points = new Dictionary(); - - internal ConnectionPointsMiddle(double left, double top, double width, double height) - { - var middleWidth = width / 2; - var middleHeight = height / 2; - - var middleX = left + middleWidth; - var middleY = top + middleHeight; - - Left = new Coordinate(left, middleY); - Top = new Coordinate(middleX, top); - - Right = new Coordinate(left + width, middleY); - Bottom = new Coordinate(left + middleX, top + height); - - Points.Add(0, Left); - Points.Add(1, Top); - Points.Add(2, Right); - Points.Add(3, Bottom); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/PointLines.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/PointLines.cs deleted file mode 100644 index 79bd7dd591..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/PointLines.cs +++ /dev/null @@ -1,75 +0,0 @@ -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - internal class PointLines : DrawingObject - { - internal List RenderLines = new List(); - - internal ConnectionPointsMiddle ConnectionPoints; - - private List ptColors = new List { "red", "green", "blue", "yellow" }; - - private BoundingBox parentBounds; - - private PointLines(DrawingBase renderer) : base(renderer) - { - } - - internal PointLines(DrawingBase renderer, BoundingBox parent, ConnectionPointsMiddle connectionPoints) : this(renderer) - { - parentBounds = parent; - - Bounds = new BoundingBox(); - - Bounds.Parent = parent; - //Bounds.Left = parent.Left; - //Bounds.Top = parent.Top; - //Bounds.Width = parent.Width; - //Bounds.Height = parent.Height; - ConnectionPoints = connectionPoints; - - UpdateLines(); - } - - internal void UpdateLines() - { - RenderLines.Clear(); - - for (int i = 0; i < ConnectionPoints.Points.Count; i++) - { - var cPoint = ConnectionPoints.Points[i]; - var cPointLine = new SvgRenderLineItem(DrawingRenderer, Bounds); - cPointLine.X1 = 0; - cPointLine.Y1 = 0; - cPointLine.X2 = cPoint.X; - cPointLine.Y2 = cPoint.Y; - - cPointLine.BorderWidth = 1; - cPointLine.BorderColor = ptColors[i]; - RenderLines.Add(cPointLine); - } - } - - internal override void AppendRenderItems(List renderItems) - { - SvgGroupItem gItem = new SvgGroupItem(DrawingRenderer, Bounds); - renderItems.Add(gItem); - foreach (var line in RenderLines) - { - renderItems.Add(line); - } - SvgEndGroupItem endGItem = new SvgEndGroupItem(DrawingRenderer, Bounds); - renderItems.Add(endGItem); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgChartSerieDataLabel.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgChartSerieDataLabel.cs deleted file mode 100644 index f4345b3c73..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgChartSerieDataLabel.cs +++ /dev/null @@ -1,156 +0,0 @@ -using EPPlus.Graphics; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Chart; -using System.Collections.Generic; - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - internal class SvgChartSerieDataLabel : DrawingObjectNoBounds - { - //positioning is handled by parent item via these - internal List groupItems = new List(); - private List dataLabels = new List(); - - private RenderItem seriesIcon = null; - private int _serieIndex = -1; - ExcelDrawingParagraph defaultParagraph; - BoundingBox plotAreaBounds; - BoundingBox _defaultMargins; - ExcelChartSerieDataLabel _dlblSerie; - - public SvgChartSerieDataLabel(SvgChart chart, ExcelChartSerieDataLabel dlblSerie, BoundingBox maxBounds, ExcelChartStandardSerie serie, List xValues, List yValues, int index) : base(chart) - { - _serieIndex = index; - _dlblSerie = dlblSerie; - plotAreaBounds = chart.Plotarea.Rectangle.Bounds; - - if (dlblSerie.TextBody.Paragraphs.Count != 0) - { - defaultParagraph = dlblSerie.TextBody.Paragraphs[0]; - dlblSerie.TextBody.GetInsetsInPoints(out double l, out double top, out double right, out double bottom); - _defaultMargins = new BoundingBox(l, top, right, bottom); - } - - - if (dlblSerie.DataLabels.Count == 0 && serie.NumberOfItems > 0) - { - //if (xValues != null) - //{ - for (int i = 0; i < serie.NumberOfItems; i++) - { - var yVal = yValues == null ? null : yValues[i]; - var xVal = xValues == null ? null : xValues[i]; - AddDatalabel(chart, serie, dlblSerie, xVal, yValues[i], maxBounds); - } - //} - } - else - { - //if (xValues != null) - //{ - for (int i = 0; i < dlblSerie.DataLabels.Count; i++) - { - var dataLabel = dlblSerie.DataLabels[i]; - var yVal = yValues == null ? null : yValues[i]; - var xVal = xValues == null ? null : xValues[i]; - AddDatalabel(chart, serie, dataLabel, xVal, yVal, maxBounds); - } - //} - } - } - - private void CreateSeriesIcon(SvgChart chart, ExcelChartStandardSerie serie, BoundingBox maxBounds) - { - if (chart.Legend == null) - { - seriesIcon = chart.GetSeriesIcon(serie, _serieIndex, maxBounds); - } - else - { - var legendItem = chart.Legend; - var seriesIconOrig = (SvgRenderLineItem)legendItem.SeriesIcon[_serieIndex].SeriesIcon; - var clonedIcon = seriesIconOrig.Clone(chart); - - clonedIcon.Y1 = 0; - clonedIcon.Y2 = 0; - - seriesIcon = clonedIcon; - } - } - - private RenderItem GetSeriesIcon(SvgChart chart, ExcelChartStandardSerie serie, BoundingBox maxBounds) - { - if(seriesIcon == null) - { - CreateSeriesIcon(chart, serie, maxBounds); - } - - return seriesIcon; - } - - private void AddDatalabel(SvgChart chart, ExcelChartStandardSerie serie, ExcelChartDataLabelStandard dataLabel, object xValue, object yValue, BoundingBox maxBounds) - { - var newDataLabel = new SvgDataLabelPoint(chart, dataLabel); - newDataLabel.ImportDataLabel(chart, serie, dataLabel, xValue, yValue, defaultParagraph, maxBounds, _defaultMargins); - - if(dataLabel.ShowLegendKey) - { - newDataLabel.AddSeriesIcon(GetSeriesIcon(chart, serie, maxBounds)); - } - - dataLabels.Add(newDataLabel); - } - - BoundingBox _parentShapeBounds = null; - Graphics.Math.Vector2 _startToEndDir = Graphics.Math.Vector2.Zero; - /// - /// Datapoints can have different shapes. - /// Which gives different meaning to positions like'Center' and 'Inside' and 'Outside' - /// Therefore you have the option to provide the bounds of a shape and its endpoint - /// - /// - /// - /// - internal void SetParentShape(BoundingBox parentBounds, BoundingBox shapeEndPoint, int index) - { - _parentShapeBounds = parentBounds; - SetParentPoint(shapeEndPoint, index); - } - - internal void SetParentVector(BoundingBox parentPoint, int index, Graphics.Math.Vector2 startToEndDir) - { - _startToEndDir = startToEndDir; - SetParentPoint(parentPoint, index); - } - - internal void SetParentPoint(BoundingBox parent, int index) - { - if (dataLabels.Count > index) - { - dataLabels[index].SetParentPoint(parent, _parentShapeBounds, _startToEndDir); - } - //dataLabels[index].SetParentPoint(parent); - } - - internal override void AppendRenderItems(List renderItems) - { - var plotAreaGroup = new SvgGroupItem(DrawingRenderer, plotAreaBounds); - - if(_dlblSerie.Fill.IsEmpty == false) - { - plotAreaGroup.SetDrawingPropertiesFill(_dlblSerie.Fill, null); - - plotAreaGroup.GroupTransform += $" fill=\"{plotAreaGroup.FillColor}\""; - } - - renderItems.Add(plotAreaGroup); - for(int i = 0; i< dataLabels.Count; i++) - { - dataLabels[i].AppendRenderItems(renderItems); - } - renderItems.Add(new SvgEndGroupItem(DrawingRenderer, plotAreaBounds)); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgContainerItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgContainerItem.cs deleted file mode 100644 index 26d9928373..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgContainerItem.cs +++ /dev/null @@ -1,36 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlusImageRenderer.RenderItems; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - internal class SvgContainerItem : ContainerItem - { - public SvgContainerItem(RenderItem innerItem, RenderItem outerItem) : base(innerItem, outerItem) - { - } - - public override void Render(StringBuilder sb) - { - var grpItem = new SvgGroupItem(DrawingRenderer, Bounds.Left, Bounds.Top); - grpItem.Render(sb); - - OuterItem.Render(sb); - - var grpItem2 = new SvgGroupItem(DrawingRenderer, MarginLeft, MarginTop); - grpItem2.Render(sb); - - InnerItem.Render(sb); - - var endGroupItem2 = new SvgEndGroupItem(DrawingRenderer, Bounds); - endGroupItem2.Render(sb); - - - var endGroupItem = new SvgEndGroupItem(DrawingRenderer, Bounds); - endGroupItem.Render(sb); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgDataLabelPoint.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgDataLabelPoint.cs deleted file mode 100644 index d1a7b0d2fb..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgDataLabelPoint.cs +++ /dev/null @@ -1,503 +0,0 @@ -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.Utils.EnumUtils; -using System; -using System.Collections.Generic; - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - internal class SvgDataLabelPoint : SvgChartObject - { - bool _hasManualLayout = false; - bool _hasLeaderLines = false; - bool _haveAdjustedForIcon = false; - bool _renderConnectionPointLines = false; - - private SvgTextBox _txtBox; - BoundingBox _parentPoint; - List _leaderLines = new List(); - Coordinate _manualLayoutOffset = new Coordinate (0, 0); - PointLines _connectionPointLines; - eLabelPosition _labelPosition; - - //public SvgChartDataLabelStandard(ChartRenderer chart, string dataLabelText) : base(chart) - //{ - // var txtBox = new DrawingTextBox(chart, chart.Bounds, chart.Bounds); - // txtBox.AddText(0, dataLabelText); - //} - - //public SvgChartDataLabelStandard(ChartRenderer chart, ExcelChartDataLabelStandard standard, DrawingTextBox txtBox) : base(chart) - //{ - // HasLegendKey = standard.ShowLegendKey; - // TxtBox = txtBox; - //} - - public SvgDataLabelPoint(EPPlusImageRenderer.DrawingChart chart, ExcelChartDataLabelStandard standard) : base(chart) - { - _labelPosition = standard.Position; - } - - RenderItem _seriesIcon = null; - - internal void AddSeriesIcon(RenderItem seriesIcon) - { - var iconWidth = seriesIcon.Bounds.Width; - var iconHeight = seriesIcon.Bounds.Height; - - _seriesIcon = seriesIcon; - _seriesIcon.Bounds.Parent = Bounds; - - if (_haveAdjustedForIcon == false) - { - _txtBox.Left += iconWidth; - _seriesIcon.Bounds.Left -= 0.75d; - //It seems there is a hard-coded margin in excel of about 4.5pt (6px) - Bounds.Left += 4d + 2.25d; - LeftMargin -= 2.25d + 4d; - Bounds.Width += iconWidth + 2.25d; - - _haveAdjustedForIcon = true; - } - } - - internal void ImportDataLabel(SvgChart chart, ExcelChartStandardSerie serie, ExcelChartDataLabelStandard dataLabel, object xValue, object yValue, ExcelDrawingParagraph defaultParagraph, BoundingBox maxBounds, BoundingBox defaultMargins) - { - List dlblStrings = new List(); - - if (dataLabel.ShowSeriesName) - { - dlblStrings.Add(serie.GetHeaderString()); - } - if (dataLabel.ShowCategory) - { - dlblStrings.Add(xValue.ToString()); - } - if (dataLabel.ShowValue) - { - if (yValue != null) - { - dlblStrings.Add(yValue.ToString()); - } - } - - var separator = string.IsNullOrEmpty(dataLabel.Separator) ? ", " : dataLabel.Separator; - - string finalString = ""; - for (int j = 0; j < dlblStrings.Count; j++) - { - finalString += dlblStrings[j]; - if (j != dlblStrings.Count - 1) - { - finalString += separator; - } - } - - var txtBox = new SvgTextBox(chart, Bounds, maxBounds); - - txtBox.ImportTextBody(dataLabel.TextBody, false); - - txtBox.TextBody.Bounds.Top = 0; - txtBox.TextBody.AutoSize = true; - - if (txtBox.TextBody.Paragraphs.Count == 0) - { - txtBox.TextBody.ImportParagraph(defaultParagraph, 0, finalString); - //txtBox.TextBody.AddParagraph(0, finalString); - } - else if (txtBox.TextBody.Paragraphs.Count == 1) - { - txtBox.TextBody.ImportParagraph(dataLabel.TextBody.Paragraphs[0], 0, finalString); - //Remove dummy paragraph added by ImportTextBody - txtBox.TextBody.Paragraphs.RemoveAt(0); - } - - if(txtBox.LeftMargin == 0) - { - txtBox.LeftMargin = defaultMargins.Left; - txtBox.RightMargin = defaultMargins.Width; - txtBox.TopMargin = defaultMargins.Top; - txtBox.BottomMargin = defaultMargins.Height; - } - - ////Txtbox is Broken. Workaround - //txtBox.Left += txtBox.LeftMargin; - //txtBox.Top += txtBox.TopMargin; - - //Center the textbox at the origin point - Bounds.Left -= txtBox.Rectangle.Bounds.Width / 2; - Bounds.Top -= txtBox.Rectangle.Bounds.Height / 2; - - //Set initial width and height to content - Bounds.Width = txtBox.Rectangle.Bounds.Width; - Bounds.Height = txtBox.Rectangle.Bounds.Height; - - _txtBox = txtBox; - - if (dataLabel.Fill.IsEmpty == false) - { - _txtBox.Rectangle.SetDrawingPropertiesFill(dataLabel.Fill, null); - } - if (dataLabel.Font.IsEmpty == false) - { - txtBox.TextBody.FontColorString = "#" + dataLabel.Font.Color.ToColorString(); - } - - _labelPosition = dataLabel.Position; - - if (dataLabel is ExcelChartDataLabelItem) - { - var individualLabel = dataLabel as ExcelChartDataLabelItem; - - if (individualLabel.Fill.IsEmpty == false) - { - _txtBox.Rectangle.FillColor = "#" + individualLabel.Fill.Color.ToColorString(); - } - - if (individualLabel.Layout != null && individualLabel.Layout.HasLayout) - { - _hasManualLayout = true; - var rect = GetRectFromManualLayout(chart, individualLabel.Layout); - Rectangle = rect; - - _manualLayoutOffset = new Coordinate(Rectangle.Left, Rectangle.Top); - - Bounds.Left += _manualLayoutOffset.X; - Bounds.Top += _manualLayoutOffset.Y; - - if (dataLabel.ShowLeaderLines) - { - _hasLeaderLines = true; - } - } - } - } - - private int GetClosestConnectionPointCoordinateIndex(Coordinate originPoint) - { - double smallestDist = double.MaxValue; - int i = 0; - int smallestIndex = 0; - - foreach (var line in _connectionPointLines.RenderLines) - { - line.X1 = originPoint.X; - line.Y1 = originPoint.Y; - - var w = Math.Abs(line.X2 - line.X1); - var h = Math.Abs(line.Y2 - line.Y1); - - //Use pythagoran theorem to get diagonal distance - var totalDist = Math.Sqrt(Math.Pow(w, 2) + Math.Pow(h, 2)); - - if (totalDist < smallestDist) - { - smallestDist = totalDist; - smallestIndex = i; - } - i++; - } - - return smallestIndex; - } - - BoundingBox _parentShapeBounds = null; - - - - private void SetPositionBasic(BoundingBox point, eLabelPosition basicPosition) - { - switch (basicPosition) - { - //case eLabelPosition.Center: - // Bounds.Left = dataLabelCenter.X; - // Bounds.Top = dataLabelCenter.Y; - // break; - case eLabelPosition.Left: - Bounds.Left -= _txtBox.Width + (point.Width / 2); - break; - case eLabelPosition.Right: - case eLabelPosition.BestFit: - Bounds.Left += _txtBox.Width / 2 + point.Width; - break; - case eLabelPosition.Top: - Bounds.Top -= (point.Height + _txtBox.Height) / 2; - break; - case eLabelPosition.Bottom: - Bounds.Top += (point.Height + _txtBox.Height) / 2; - break; - default: - throw new InvalidOperationException($"The datalabel position entered in SetPositionBasic: '{basicPosition}' is not a basic position"); - } - } - - internal void SetParentPoint(BoundingBox parentPoint, BoundingBox parentShape, Graphics.Math.Vector2 startToEndDir) - { - Bounds.Parent = parentPoint; - _parentPoint = parentPoint; - _parentShapeBounds = parentShape; - - var dataLabelCenter = new Graphics.Math.Vector2(Bounds.Left, Bounds.Top); - Graphics.Math.Vector2 startPointDirection = Graphics.Math.Vector2.Zero; - - if ((startToEndDir.X == 0 && startToEndDir.Y == 0) == false) - { - startPointDirection = startToEndDir / startToEndDir.Length; - } - - //if (_parentShapeBounds != null) - //{ - // //shapeCenter - // dataLabelCenter = new Graphics.Math.Vector2((_parentShapeBounds.Width / 2)+parentShape.Left, (_parentShapeBounds.Height / 2)+parentShape.Top); - - // //Get directional vector (in local coords but does not matter since we make it directional) - // startPointDirection = dataLabelCenter - parentPoint.LocalPosition; - // //Divide by length to only get direction - // var startPointDirectionOnly = startPointDirection / startPointDirection.Length; - - // //var lenX = Math.Abs() - // //////Pythagoran theorem - // var len = Math.Sqrt(Math.Pow(startPointDirection.X, 2) + Math.Pow(startPointDirection.Y, 2)); - // startPointDirection = startPointDirectionOnly * len; - //} - //else - //{ - // dataLabelCenter = new Graphics.Math.Vector2(Bounds.Left, Bounds.Top); - //} - - switch (_labelPosition) - { - case eLabelPosition.Center: - - if ((startToEndDir.X == 0 && startToEndDir.Y == 0) == false) - { - //Half and invert - dataLabelCenter = ((startToEndDir*0.5d) * -1d); - } - Bounds.Left += dataLabelCenter.X; - Bounds.Top += dataLabelCenter.Y; - break; - case eLabelPosition.Left: - SetPositionBasic(parentPoint, _labelPosition); - break; - case eLabelPosition.Right: - case eLabelPosition.BestFit: - SetPositionBasic(parentPoint, _labelPosition); - break; - case eLabelPosition.Top: - SetPositionBasic(parentPoint, _labelPosition); - break; - case eLabelPosition.Bottom: - SetPositionBasic(parentPoint, _labelPosition); - break; - case eLabelPosition.InEnd: - //if (startPointDirection.X == 0 && startPointDirection.Y == 0) - //{ - // throw new InvalidOperationException("eLabelPosition.InEnd MUST have a direction." + - // "Cannot be within End if EndPoint is undefined."); - //} - ////if(parentShape == null) - ////{ - //// throw new InvalidOperationException("eLabelPosition.InEnd MUST have a parentShape"); - ////} - //startPointDirection = startPointDirection * -1; - //if (startPointDirection.X != 0) - //{ - // //If endPoint is to the right - // if (startPointDirection.X > 0) - // { - // //We must place to the left - // SetPositionBasic(parentPoint, eLabelPosition.Left); - // } - // //if endpoint is to the left - // else - // { - // //We must place to the right - // SetPositionBasic(parentPoint, eLabelPosition.Right); - // } - //} - - //if (startPointDirection.Y != 0) - //{ - // //If endpoint is below - // if (startPointDirection.Y > 0) - // { - // //We must place on Top - // SetPositionBasic(parentPoint, eLabelPosition.Top); - // } - // else - // { - // //We must place on Bottom - // SetPositionBasic(parentPoint, eLabelPosition.Bottom); - // } - //} - break; - case eLabelPosition.OutEnd: - //if (startPointDirection.X == 0 && startPointDirection.Y == 0) - //{ - // throw new InvalidOperationException("eLabelPosition.OutEnd MUST have a direction." + - // "Cannot be within End if EndPoint is undefined."); - //} - ////if (parentShape == null) - ////{ - //// throw new InvalidOperationException("eLabelPosition.OutEnd MUST have a parentShape"); - ////} - - //if (startPointDirection.X != 0) - //{ - // //If endPoint is to the left - // if (startPointDirection.X < 0) - // { - // //We must place to the left - // SetPositionBasic(parentPoint, eLabelPosition.Left); - // } - // //if endpoint is to the right - // else - // { - // //We must place to the right - // SetPositionBasic(parentPoint, eLabelPosition.Right); - // } - //} - - //if (startPointDirection.Y != 0) - //{ - // //If endpoint is on Top - // if (startPointDirection.Y < 0) - // { - // //We must place on Top - // SetPositionBasic(parentPoint, eLabelPosition.Top); - // } - // //If endpoint is on bottom - // else - // { - // //We must place on Bottom - // SetPositionBasic(parentPoint, eLabelPosition.Bottom); - // } - //} - break; - default: - throw new InvalidOperationException($"The datalabel position {_labelPosition} has not been implemented yet"); - } - - if (_hasManualLayout) - { - if (_hasLeaderLines) - { - //With origin in top left of current bounds get the connection points - var cPoints = new ConnectionPointsMiddle(0, 0, Bounds.Width, Bounds.Height); - - //Ready to draw the lines so that we can visualize the distances to each point - _connectionPointLines = new PointLines(ChartRenderer, Bounds, cPoints); - - //Adjust if there is a margin - _connectionPointLines.Bounds.Left += LeftMargin; - _connectionPointLines.UpdateLines(); - - //Get the offset between those points and the origin point - var offsetToParentPoint = new Coordinate(-(Bounds.Left + LeftMargin), -(Bounds.Top + TopMargin)); - - //Calculate closest point - var index = GetClosestConnectionPointCoordinateIndex(offsetToParentPoint); - - _leaderLines.Clear(); - - double xOffset = 0; - if (index == 0 || index == 2) - { - //If Left or Right - //Add extra 7 px (5.25pt) line to the given side - var extraLine = new SvgRenderLineItem(ChartRenderer, ChartRenderer.Bounds); - - xOffset += index == 0 ? -5.25d : 5.25d; - - extraLine.X1 = _connectionPointLines.ConnectionPoints.Points[index].X + LeftMargin; - extraLine.Y1 = _connectionPointLines.ConnectionPoints.Points[index].Y; - extraLine.Y2 = _connectionPointLines.ConnectionPoints.Points[index].Y; - extraLine.X2 = extraLine.X1 + xOffset; - - extraLine.BorderColor = "gray"; - extraLine.BorderWidth = 0.5; - - _leaderLines.Add(extraLine); - } - var mainLine = new SvgRenderLineItem(ChartRenderer, ChartRenderer.Bounds); - mainLine.X1 = _connectionPointLines.ConnectionPoints.Points[index].X + xOffset + LeftMargin; - mainLine.Y1 = _connectionPointLines.ConnectionPoints.Points[index].Y; - mainLine.X2 = offsetToParentPoint.X + LeftMargin; - mainLine.Y2 = offsetToParentPoint.Y; - - mainLine.BorderColor = "gray"; - mainLine.BorderWidth = 0.5; - _leaderLines.Add(mainLine); - } - } - } - - private void AppendDebugBounds(List renderItems) - { - SvgRenderRectItem rect = new SvgRenderRectItem(ChartRenderer, Bounds); - rect.Bounds.Left = LeftMargin; - rect.Bounds.Top = 0; - rect.Bounds.Width = Bounds.Width; - rect.Bounds.Height = Bounds.Height; - - rect.FillColor = "red"; - rect.FillOpacity = 0.2; - renderItems.Add(rect); - } - - internal override void AppendRenderItems(List renderItems) - { - var parentPointGroup = new SvgGroupItem(ChartRenderer, _parentPoint); - renderItems.Add(parentPointGroup); - - var titleItemOrigin = new SvgTitleItem(DrawingRenderer, "DataLabel originpoint"); - renderItems.Add(titleItemOrigin); - - var group = new SvgGroupItem(ChartRenderer, Bounds); - renderItems.Add(group); - - var titleItem = new SvgTitleItem(DrawingRenderer, "DataLabel size adjustment"); - renderItems.Add(titleItem); - - //AppendDebugBounds(renderItems); - - _txtBox.AppendRenderItems(renderItems); - - if(_renderConnectionPointLines) - { - if (_connectionPointLines != null) - { - _connectionPointLines.AppendRenderItems(renderItems); - } - } - - if (_seriesIcon != null) - { - var height = Bounds.Height; - if (height == 0) - { - height = _txtBox.Height; - } - //Currently series icon always has a y1 y2 of 2 - var iconGrp = new SvgGroupItem(ChartRenderer, _seriesIcon.Bounds.Left, height / 2 - 2); - renderItems.Add(iconGrp); - renderItems.Add(_seriesIcon); - renderItems.Add(new SvgEndGroupItem(DrawingRenderer, Bounds)); - } - - if (_leaderLines != null && _leaderLines.Count > 0) - { - foreach (var line in _leaderLines) - { - renderItems.Add(line); - } - } - renderItems.Add(new SvgEndGroupItem(DrawingRenderer, Bounds)); - renderItems.Add(new SvgEndGroupItem(DrawingRenderer, Bounds)); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgGroupItemNew.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgGroupItemNew.cs deleted file mode 100644 index 401114ca01..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgGroupItemNew.cs +++ /dev/null @@ -1,117 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using System.Globalization; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - /// - /// Group item that ends itself - /// (writes its own ending node) - /// - internal class PieGroupRenderItem : PieGroupItemBase - { - const string transformTranslate = "translate({0}, {1})"; - const string transformRotate = "rotate({0})"; - const string transformScale = "scale({0}, {1})"; - - public SvgGroupItemNew(DrawingBase renderer, double localXPos, double localYPos) : base(renderer, localXPos, localYPos) - { - } - - public SvgGroupItemNew(DrawingBase renderer, BoundingBox parent, double rotation, Transform rotationPoint = null) : base(renderer, parent, rotation, rotationPoint) - { - } - - public override void Render(StringBuilder sb) - { - string combinedTransform = GetCombinedTransformString(); - string name = string.Empty; - if(Bounds.Name != null) - { - name = $"class=\"{Bounds.Name}\""; - } - - if (string.IsNullOrEmpty(combinedTransform) == false) - { - sb.Append($""); - } - else - { - sb.Append($""); - } - - foreach (var item in _childItems) - { - if (item is SvgGroupItemNew) - { - var subGroup = item as SvgGroupItemNew; - subGroup.Render(sb); - } - else - { - item.Render(sb); - } - } - - sb.Append(""); - } - - string GetTransformOrigin() - { - string tOrigin = string.Empty; - - if(TransformOrigin != null && (TransformOrigin.X == 0 && TransformOrigin.Y == 0) == false) - { - tOrigin = $"transform-origin=\"{TransformOrigin.X.PointToPixelString()} {TransformOrigin.Y.PointToPixelString()}\""; - } - return tOrigin; - } - - string GetCombinedTransformString() - { - string positionStr = ""; - string rotationStr = GetRotationStr(); - string scalingStr = GetScalingStr(); - - - if (TranslationOffset != null && (TranslationOffset.Left == 0 && TranslationOffset.Top == 0) == false) - { - positionStr = string.Format(transformTranslate, TranslationOffset.Left.PointToPixelString(), TranslationOffset.Top.PointToPixelString()) + " "; - } - - return positionStr + rotationStr + scalingStr; - } - - string GetScalingStr() - { - var scaleStr = string.Empty; - - if (Scale != null) - { - scaleStr = string.Format(transformScale, Scale.X.ToString(CultureInfo.InvariantCulture), Scale.Y.ToString(CultureInfo.InvariantCulture)) + " "; - } - - return scaleStr; - } - - string GetRotationStr() - { - if (double.IsNaN(Rotation) == false) - { - string rot = Rotation.ToString(CultureInfo.InvariantCulture); - - if(RotationPoint != null && RotationPoint != TranslationOffset) - { - rot += $", {RotationPoint.Left.PointToPixelString()}, {RotationPoint.Top.PointToPixelString()}" + " "; - } - - return string.Format(transformRotate, rot); - } - - return string.Empty; - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgInnerGroup.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgInnerGroup.cs deleted file mode 100644 index 5489bb231b..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgInnerGroup.cs +++ /dev/null @@ -1,33 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlusImageRenderer; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - internal class SvgInnerGroup : InnerGroup - { - public SvgInnerGroup(DrawingBase renderer) : base(renderer) - { - } - - public override void Render(StringBuilder sb) - { - sb.Append($""); - - foreach (var item in _childItems) - { - if (item is InnerGroup) - { - var subGroup = item as InnerGroup; - subGroup.Render(sb); - } - else - { - item.Render(sb); - } - } - - sb.Append(""); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgParagraphItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgParagraphItem.cs deleted file mode 100644 index b638c7cebb..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgParagraphItem.cs +++ /dev/null @@ -1,168 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using OfficeOpenXml.Interfaces.Drawing.Text; -using OfficeOpenXml.Style; -using System.Globalization; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - internal class SvgParagraphItem : ParagraphItem - { - public override RenderItemType Type => RenderItemType.Paragraph; - - public SvgParagraphItem(TextBodyItem textBody, DrawingBase renderer, BoundingBox parent) : base(textBody, renderer, parent) - { - } - - public SvgParagraphItem(TextBodyItem textBody, DrawingBase renderer, BoundingBox parent, string text) : base(textBody, renderer, parent) - { - ImportLinesAndTextRunsDefault(text); - } - - public SvgParagraphItem(TextBodyItem textBody, DrawingBase renderer, BoundingBox parent, ExcelDrawingParagraph p, string textIfEmpty = null) : base(textBody, renderer, parent, p, textIfEmpty) - { - } - //private string GetVerticalAlignAttribute(double textOrigY) - //{ - // string ret = "dominant-baseline="; - - // switch (VerticalAlignment) - // { - // case eTextAnchoringType.Top: - // ret += $"\"text-top\" "; - // break; - // case eTextAnchoringType.Center: - // ret += $"\"middle\" "; - // break; - // case eTextAnchoringType.Bottom: - // ret += $"\"text-bottom\" "; - // break; - // default: - // return ""; - // } - - // var yStrValue = textOrigY.ToString(CultureInfo.InvariantCulture); - // ret += $" y=\"{yStrValue}\""; - - // return ret; - //} - - public override void Render(StringBuilder sb) - { - var fontSize = _paragraphFont.Size.PointToPixel().ToString(CultureInfo.InvariantCulture); - - sb.AppendLine($""); - - sb.AppendLine("paragraph "); - - if(DisplayBounds) - { - sb.AppendLine($""); - sb.AppendLine("Bounding-Box: Paragraph "); - SvgRenderRectItem visualBoundingBox = new SvgRenderRectItem(DrawingRenderer, ParentTextBody.Bounds); - - //Left/Top handled by transform - visualBoundingBox.Bounds.Width = Bounds.Width; - visualBoundingBox.Bounds.Height = Bounds.Height; - - visualBoundingBox.FillOpacity = 0.3; - visualBoundingBox.FillColor = "red"; - visualBoundingBox.Render(sb); - sb.AppendLine($""); - - } - - //var bb = new SvgRenderRectItem(DrawingRenderer, Bounds); - ////The bb is affected by the Transform so set pos to zero - //if (IsFirstParagraph == false) - //{ - // bb.Y = 0; - //} - //bb.X = 0; - - //bb.Width = Bounds.Width; - //bb.Height = Bounds.Height; - //bb.FillColor = FillColor; - //bb.FillOpacity = 0.3; - //bb.Render(sb); - - //Render text run debug boxes as we cannot place the rects after or inside the text element - - //if (Runs.Count > 0) - //{ - // //double lastWidth = 0; - - // var bbLines = new SvgRenderRectItem(); - - // foreach (var textRun in Runs) - // { - // ////render txtRun debug - // bbLines.X = textRun.Bounds.Left; - // bbLines.Width = textRun.Bounds.Width; - // if (IsFirstParagraph == false) - // { - // bbLines.Y = -textRun.FontSizeInPixels.PixelToPoint(); - // } - - // //Render each line debug - // bbLines.FillColor = "purple"; - // for (int i = 0; i < textRun.Lines.Count; i++) - // { - // if (i > 0) - // { - // bbLines.Y += textRun.YIncreasePerLine[i]; - // //lastWidth = 0; - // bbLines.X = 0; - // } - // else - // { - // bbLines.X = textRun.Bounds.Left; - // } - - // bbLines.Height = textRun.FontSizeInPixels; - // bbLines.Width = textRun.PerLineWidth[i]; - // bbLines.RenderRect(sb); - // } - // } - //} - - sb.Append(""); - - if (Runs != null && Runs.Count > 0) - { - foreach (var textRun in Runs) - { - textRun.Render(sb); - } - } - - sb.AppendLine(""); - sb.AppendLine(""); - } - internal override TextRunItem CreateTextRun(ExcelParagraphTextRunBase run, BoundingBox parent, string displayText) - { - return new SvgTextRunItem(DrawingRenderer, parent, run, displayText); - } - internal override TextRunItem CreateTextRun(string text, ExcelTextFont font, BoundingBox parent, string displayText) - { - return new SvgTextRunItem(DrawingRenderer, parent, text, font, displayText); - } - - internal override TextRunItem CreateTextRun(MeasurementFont font, BoundingBox parent, string displayText) - { - return new SvgTextRunItem(DrawingRenderer, parent, font, displayText); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTextBodyItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTextBodyItem.cs deleted file mode 100644 index 605f8651b7..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTextBodyItem.cs +++ /dev/null @@ -1,77 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - internal class SvgTextBodyItem : TextBodyItem - { - public SvgTextBodyItem(DrawingBase renderer, BoundingBox parent, bool autoSize, bool clampedToParent = false) : base(renderer, parent, autoSize) - { - MaxWidth = parent.Width; - MaxHeight = parent.Height; - } - public SvgTextBodyItem(DrawingBase renderer, BoundingBox parent, double left, double top, double maxWidth, double maxHeight, bool clampedToParent = false, bool autoSize=false) : base(renderer, parent, autoSize) - { - Bounds.Left = left; - Bounds.Top = top; - Bounds.Width = maxWidth; - Bounds.Height = maxHeight; - MaxWidth = maxWidth; - MaxHeight = maxHeight; - } - - internal override List Paragraphs { get; set; } = new List(); - - internal override void AppendRenderItems(List renderItems) - { - SvgGroupItem groupItem; - if (Bounds.Parent.Rotation == 0) //If the parent is rotated, we should not apply rotation again. This is usually when the parent is a textbox. - { - groupItem = new SvgGroupItem(DrawingRenderer, Bounds, Bounds.Rotation); - } - else - { - groupItem = new SvgGroupItem(DrawingRenderer, Bounds); - } - - if (FontColorString != null) - { - groupItem.GroupTransform += $" fill=\"{FontColorString}\""; - } - - renderItems.Add(groupItem); - foreach (SvgParagraphItem item in Paragraphs) - { - renderItems.Add(item); - } - renderItems.Add(new SvgEndGroupItem(DrawingRenderer, Bounds)); - } - - internal override ParagraphItem CreateParagraph(TextBodyItem textBody, BoundingBox parent) - { - return new SvgParagraphItem(this, DrawingRenderer, parent); - } - - internal override ParagraphItem CreateParagraph(TextBodyItem textBody, ExcelDrawingParagraph paragraph, BoundingBox parent, string textIfEmpty = null) - { - return new SvgParagraphItem(this, DrawingRenderer, parent, paragraph, textIfEmpty); - } - - internal override ParagraphItem CreateParagraph(TextBodyItem textBody, BoundingBox parent, string textIfEmpty = "") - { - return new SvgParagraphItem(this, DrawingRenderer, parent, textIfEmpty); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTextBox.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTextBox.cs deleted file mode 100644 index 071e63fcc3..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTextBox.cs +++ /dev/null @@ -1,242 +0,0 @@ -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -using OfficeOpenXml.Utils.EnumUtils; -using OfficeOpenXml.Style; -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; - - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - internal class SvgTextBox : DrawingObjectNoBounds - { - internal SvgTextBox(DrawingBase renderer, BoundingBox parent, double left, double top, double width, double height, double maxWidth = double.NaN, double maxHeight = double.NaN) : base(renderer) - { - Init(renderer, parent, maxWidth, maxHeight); - Left = left; - Top = top; - } - - private void Init(DrawingBase renderer, BoundingBox parent, double maxWidth, double maxHeight) - { - Parent = parent; - _rectangle = new SvgRenderRectItem(DrawingRenderer, Parent); - TextBody = new SvgTextBodyItem(renderer, Rectangle.Bounds, true); - TextBody.MaxWidth = maxWidth; - TextBody.MaxHeight = maxHeight; - } - - internal SvgTextBox(DrawingBase renderer, BoundingBox parent, double maxWidth, double maxHeight) : base(renderer) - { - Init(renderer, parent, maxWidth, maxHeight); - } - - //Simplified input - internal SvgTextBox(DrawingBase renderer, BoundingBox parent, BoundingBox maxBounds) : this( - renderer, parent, maxBounds.Left, maxBounds.Top, maxBounds.Width, maxBounds.Height, maxBounds.Width, maxBounds.Height) - { - } - SvgRenderItem _rectangle=null; - public SvgRenderItem Rectangle - { - get - { - _rectangle.Bounds.Width = Width; - _rectangle.Bounds.Height = Height; - return _rectangle; - } - } - public SvgTextBodyItem TextBody {get;set;} - public double Left - { - get - { - return Rectangle.Bounds.Left; //TextBody.Bounds.Left - LeftMargin; - - } - set - { - //TextBody.Bounds.Left = value + LeftMargin; - Rectangle.Bounds.Left = value; - } - } - public double Top - { - get - { - return Rectangle.Bounds.Top; //TextBody.Bounds.Top - TopMargin; - } - set - { - //TextBody.Bounds.Top = value + TopMargin; - Rectangle.Bounds.Top = value; - } - } - public double Width - { - get - { - return LeftMargin + (TextBody?.Width ?? 0D) + RightMargin; - } - } - public double Height - { - get - { - return TopMargin + (TextBody?.Height ?? 0d) + BottomMargin; - } - } - internal double LeftMargin - { - get; set; - } - - internal double TopMargin - { - get; set; - } - - internal double RightMargin - { - get; set; - } - - internal double BottomMargin - { - get; set; - } - internal BoundingBox Parent { get; private set; } - internal double Rotation - { - get - { - return Rectangle.Bounds.Rotation; - } - set - { - Rectangle.Bounds.Rotation = value; - } - } - /// - /// Gets the actual width of the rotated textbox. - /// - /// - internal double GetActualWidth() - { - return Width * Math.Abs(Math.Cos(MathHelper.Radians(Rotation))) + Height * Math.Abs(Math.Sin(MathHelper.Radians(Rotation))); - } - /// - /// Gets the actual right position of the rotated textbox. - /// - /// - internal double GetActualRight() - { - return Left+GetActualWidth(); - } - /// - /// Gets the actual height of the rotated textbox. - /// - /// - internal double GetActualHeight() - { - return Width * Math.Abs(Math.Sin(MathHelper.Radians(Rotation))) + Height * Math.Abs(Math.Cos(MathHelper.Radians(Rotation))); - } - /// - /// Gets the actual right position of the rotated textbox. - /// - /// - internal double GetActualBottom() - { - return Top + GetActualHeight(); - } - /// - /// How the text is anchored. - /// - internal eTextAnchor TextAnchor - { - get; - set; - } - - internal void ImportTextBody(ExcelTextBody body, bool useDefaults = true, ExcelHorizontalAlignment horizontalDefault = ExcelHorizontalAlignment.Left) - { - double l, r, t, b; - if (useDefaults) - { - body.GetInsetsOrDefaults(out l, out t, out r, out b); - } - else - { - body.GetInsetsInPoints(out l, out t, out r, out b); - } - LeftMargin = l; - TopMargin = t; - RightMargin = r; - BottomMargin = b; - - TextBody.ImportTextBody(body); - } - - internal override void AppendRenderItems(List renderItems) - { - var rect = Rectangle; - - SvgGroupItem groupItem; - if (Rotation == 0) - { - groupItem = new SvgGroupItem(DrawingRenderer, new BoundingBox(Left, Top, Width, Height)); - } - else - { - groupItem = new SvgGroupItem(DrawingRenderer, new BoundingBox(Left, Top, Width, Height), Rotation); - } - groupItem.TextAnchor = TextAnchor.ToEnumString(); - renderItems.Add(groupItem); - - var textboxGroupItem = new SvgGroupItem(DrawingRenderer); - renderItems.Add(textboxGroupItem); - - var titleItem = new SvgTitleItem(DrawingRenderer, "TextBodySvg Rect"); - //The rect shound encapse the text element, so we need to set the left depending on the text anchor. - if(TextAnchor==eTextAnchor.Middle) - { - rect.Bounds.Left = -(rect.Bounds.Width / 2); - } - else if(TextAnchor==eTextAnchor.End) - { - rect.Bounds.Left = -rect.Bounds.Width; - } - else - { - rect.Bounds.Left = 0; - } - rect.Bounds.Top = 0; - renderItems.Add(titleItem); - renderItems.Add(rect); - - renderItems.Add(new SvgEndGroupItem(DrawingRenderer, rect.Bounds)); - - TextBody.Bounds.Left = LeftMargin; - TextBody.Bounds.Top = TopMargin; - TextBody.AppendRenderItems(renderItems); - renderItems.Add(new SvgEndGroupItem(DrawingRenderer, rect.Bounds)); - } - - internal void ImportParagraph(ExcelDrawingParagraph item, double startingY, string text = null) - { - TextBody.ImportParagraph(item, startingY, text); - } - - internal void AddText(double startingY, string text = null) - { - TextBody.AddParagraph(startingY, text); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTextRunItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTextRunItem.cs deleted file mode 100644 index f94744b29d..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTextRunItem.cs +++ /dev/null @@ -1,138 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using OfficeOpenXml.Interfaces.Drawing.Text; -using OfficeOpenXml.Style; -using System; -using System.Globalization; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - internal class SvgTextRunItem : TextRunItem - { - public SvgTextRunItem(DrawingBase renderer, BoundingBox parent, MeasurementFont font, string displayText) : base(renderer, parent, font, displayText) - { - } - - public SvgTextRunItem(DrawingBase renderer, BoundingBox parent, ExcelParagraphTextRunBase run, string displayText = "") : base(renderer,parent, run, displayText) - { - } - - public SvgTextRunItem(DrawingBase renderer, BoundingBox parent, string text, ExcelTextFont font, string displayText) : base(renderer, parent, text, font, displayText) - { - - } - string GetFontStyleAttributes() - { - string fontStyleAttributes = " "; - - if (_isItalic) - { - fontStyleAttributes += "font-style=\"italic\" "; - } - if (_isBold) - { - fontStyleAttributes += "font-weight=\"bold\" "; - } - if (_underLineType != eUnderLineType.None | _strikeType != eStrikeType.No) - { - - fontStyleAttributes += "text-decoration=\" "; - if (_underLineType != eUnderLineType.None) - { - switch (_underLineType) - { - case eUnderLineType.Single: - fontStyleAttributes += "underline"; - break; - //These are all css only apparently - //case eUnderLineType.Double: - // fontStyleAttributes += "double"; - // break; - //case eUnderLineType.Dotted: - // fontStyleAttributes += "dotted"; - // break; - //case eUnderLineType.Dash: - // fontStyleAttributes += "dashed"; - // break; - //case eUnderLineType.Wavy: - // fontStyleAttributes += "wavy"; - // break; - default: - fontStyleAttributes += "underline"; - break; - //throw new NotImplementedException("Not implemented yet"); - } - } - - if (_strikeType == eStrikeType.Single) - { - //Has to check if Both underline and strike - if (_underLineType != eUnderLineType.None) - { - fontStyleAttributes += ","; - } - fontStyleAttributes += "line-through"; - } - - fontStyleAttributes += "\" "; - } - - return fontStyleAttributes; - } - - public override void Render(StringBuilder sb) - { - string finalString = ""; - var xString = $"x =\"{(Bounds.Left.PointToPixelString())}\" "; - - var currentYEndPos = Bounds.Position.Y; // Global position Y - finalString += $"= ClippingHeight) - { - //visibility = " display=\"none\""; - } - finalString += visibility; - finalString += $"{GetFontStyleAttributes()}"; - - if (_measurementFont != null) - { - finalString += $"font-family=\"{_measurementFont.FontFamily}," - + $"{_measurementFont.FontFamily}_MSFontService,sans-serif\" " - + $"font-size=\"{fontSize.ToString(CultureInfo.InvariantCulture)}px\" "; - } - - sb.Append(finalString); - //Get color etc. - //Renders up until this point - SvgBaseRenderer.BaseRender(sb, this); - //Since final string has been written in base.render erase it. - finalString = ""; - - finalString += ">"; - finalString += _currentText; - finalString += ""; - - sb.Append(finalString); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTitleItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTitleItem.cs deleted file mode 100644 index a053fbb56e..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTitleItem.cs +++ /dev/null @@ -1,26 +0,0 @@ -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - internal class SvgTitleItem : RenderItem - { - internal string Title { get; private set; } - - public SvgTitleItem(DrawingBase renderer, string titleName) : base(renderer) - { - Title = titleName; - } - - public override RenderItemType Type => RenderItemType.CommentTitle; - - public override void Render(StringBuilder sb) - { - sb.Append($"{Title}"); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTransformGroup.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTransformGroup.cs deleted file mode 100644 index 59a1cd69c0..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/SvgTransformGroup.cs +++ /dev/null @@ -1,107 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using System.Globalization; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - - internal class SvgTransformGroup : TransformGroup - { - const string transformTranslate = "translate({0}, {1})"; - const string transformRotate = "rotate({0})"; - const string transformScale = "scale({0}, {1})"; - - public SvgTransformGroup(DrawingBase renderer) : base(renderer) - { - } - - public SvgTransformGroup(DrawingBase renderer, double localXPos, double localYPos) : base(renderer, localXPos, localYPos) - { - } - - public SvgTransformGroup(DrawingBase renderer, BoundingBox parent, double rotation, Transform rotationPoint = null) : base(renderer, parent, rotation, rotationPoint) - { - } - - string GetTransformOrigin() - { - string tOrigin = string.Empty; - - if (TransformOrigin != null && (TransformOrigin.X == 0 && TransformOrigin.Y == 0) == false) - { - tOrigin = $"transform-origin=\"{TransformOrigin.X.PointToPixelString()} {TransformOrigin.Y.PointToPixelString()}\""; - } - return tOrigin; - } - - string GetCombinedTransformString() - { - string positionStr = ""; - string rotationStr = GetRotationStr(); - string scalingStr = GetScalingStr(); - - - if ((Bounds.Left == 0 && Bounds.Top == 0) == false) - { - positionStr = string.Format(transformTranslate, Bounds.Left.PointToPixelString(), Bounds.Top.PointToPixelString()) + " "; - } - - return positionStr + rotationStr + scalingStr; - } - - string GetScalingStr() - { - var scaleStr = string.Empty; - - if (Scale != null) - { - scaleStr = string.Format(transformScale, Scale.X.ToString(CultureInfo.InvariantCulture), Scale.Y.ToString(CultureInfo.InvariantCulture)) + " "; - } - - return scaleStr; - } - - string GetRotationStr() - { - if (double.IsNaN(Rotation) == false) - { - string rot = Rotation.ToString(CultureInfo.InvariantCulture); - - if (RotationPoint != null && RotationPoint != Bounds) - { - rot += $", {RotationPoint.Left.PointToPixelString()}, {RotationPoint.Top.PointToPixelString()}" + " "; - } - - return string.Format(transformRotate, rot); - } - - return string.Empty; - } - - internal override InnerGroup CreateInnerGroup() - { - return new SvgInnerGroup(DrawingRenderer); - } - - public override void Render(StringBuilder sb) - { - string combinedTransform = GetCombinedTransformString(); - - if (string.IsNullOrEmpty(combinedTransform) == false) - { - sb.Append($""); - } - else - { - sb.Append($""); - } - - _innerGroup.Render(sb); - - sb.Append(""); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/eTextAnchor.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/eTextAnchor.cs deleted file mode 100644 index f1bead4f9c..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgItem/eTextAnchor.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace EPPlus.Export.ImageRenderer.RenderItems.SvgItem -{ - /// - /// Matches the enum for an svg text anchor of a text element. - /// - internal enum eTextAnchor - { - /// - /// Text is anchored to the start of the text element. - /// - Start, - /// - /// Text is anchored in the center of the text element. - /// - Middle, - /// - /// Text is anchored in the end of the text element. - /// - End - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgLineJoin.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgLineJoin.cs deleted file mode 100644 index 9b1e1afc04..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgLineJoin.cs +++ /dev/null @@ -1,24 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -namespace EPPlusImageRenderer.RenderItems -{ - internal enum SvgLineJoin - { - Arcs, - Bevel, - Miter, - MiterClip, - Round - } - -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgRenderEllipseItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgRenderEllipseItem.cs deleted file mode 100644 index 9eb13a0723..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgRenderEllipseItem.cs +++ /dev/null @@ -1,78 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.Utils; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using System.Globalization; -using System.Text; -namespace EPPlusImageRenderer.RenderItems -{ - internal class SvgRenderEllipseItem : SvgRenderItem - { - public SvgRenderEllipseItem(DrawingBase renderer, BoundingBox parent) : base(renderer, parent) - { - - } - public double Cx { get; set; } - public double Cy { get; set; } - public double Rx { get; set; } - public double Ry { get; set; } - - public override RenderItemType Type => RenderItemType.Rect; - - public override void Render(StringBuilder sb) - { - sb.AppendFormat(""); - } - - internal override SvgRenderItem Clone(SvgShape svgDocument) - { - var clone = new SvgRenderEllipseItem(svgDocument, svgDocument.Bounds); - CloneBase(clone); - //if (AdjustmentPoints != null && AdjustmentPoints.Commands == null && AdjustmentPoints.AdjustmentType == AdjustmentType.AdjustToWidthHeight) - //{ - // var wh = Math.Min(Width, Height); - // clone.Left = Left * svgDocument.FontSize.Item1; - // clone.Top = Top * svgDocument.FontSize.Item2; - // clone.Width = svgDocument.FontSize.Item1 * wh; - // clone.Height = svgDocument.FontSize.Item2 * wh; - //} - //else - //{ - // clone.Left = Left * svgDocument.FontSize.Item1; - // clone.Top = Top * svgDocument.FontSize.Item2; - // clone.Width = svgDocument.FontSize.Item1 * Width; - // clone.Height = svgDocument.FontSize.Item2 * Height; - //} - return clone; - } - internal override void GetBounds(out double il, out double it, out double ir, out double ib) - { - il = Cx - Rx; - it = Cy - Ry; - ir = Cx + Rx; - ib = Cx + Rx; - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgRenderItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgRenderItem.cs deleted file mode 100644 index fc7556827a..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgRenderItem.cs +++ /dev/null @@ -1,138 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using System; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading; -namespace EPPlusImageRenderer.RenderItems -{ - internal enum SvgFillType - { - SolidFill, - GradientFill, - PatternFill - } - internal abstract class SvgRenderItem : RenderItem - { - //Refrence string if this is part of a definition - internal string DefId = null; - - internal SvgRenderItem(DrawingBase renderer, BoundingBox parent) : base(renderer, parent) - { - } - public override void Render(StringBuilder sb) - { - RenderBase(sb); - } - - private void RenderBase(StringBuilder sb) - { - if(Bounds.Name != null) - { - sb.Append($" id=\"{Bounds.Name}\" "); - } - - if (string.IsNullOrEmpty(DefId) == false) - { - sb.Append($"id=\"{DefId}\" "); - } - - if (string.IsNullOrEmpty(FillColor) == false) - { - sb.Append($"fill=\"{FillColor}\" "); - } - //If fill is null it may in e.g. Rect still get the color black which can have an opacity - if (FillOpacity != null && FillOpacity != 1) - { - sb.Append($"opacity=\"{FillOpacity.Value.ToString(CultureInfo.InvariantCulture)}\" "); - } - if (string.IsNullOrEmpty(FilterName) == false) - { - sb.Append($"filter=\"{FilterName}\" "); - } - - if (BorderWidth.HasValue) - { - if (string.IsNullOrEmpty(BorderColor) == false) - { - sb.Append($"stroke=\"{BorderColor}\" "); - } - var v = BorderWidth.Value * ExcelDrawing.EMU_PER_POINT / ExcelDrawing.EMU_PER_PIXEL; - sb.Append($"stroke-width=\"{v.ToString(CultureInfo.InvariantCulture)}\" "); - - if (BorderDashArray != null) - { - var BorderDashArrayStr = BorderDashArray.Select(x => - x.ToString(CultureInfo.InvariantCulture)).ToArray(); - - sb.Append($"stroke-dasharray=\"" + $"{string.Join(",", BorderDashArrayStr)}\" "); - } - if (BorderOpacity.HasValue) - { - sb.Append($" stroke-opacity=\"{(Math.Round(BorderOpacity.Value * 100)).ToString(CultureInfo.InvariantCulture)}%\" "); - } - } - - if (TransformOrigin != null) - { - sb.Append($" transform-origin=\"{TransformOrigin.X.ToString(CultureInfo.InvariantCulture)} {TransformOrigin.Y.ToString(CultureInfo.InvariantCulture)}\" "); - } - - sb.Append($"stroke-miterlimit =\"{StrokeMiterLimit}\" "); - } - - internal abstract SvgRenderItem Clone(SvgShape svgDocument); - private protected void RenderCompoundItems(StringBuilder sb, double? borderWidth, string color, string filter) - { - var tmpBorderWidth = BorderWidth; - string tmpBorderColor = null; - BorderWidth = borderWidth ?? BorderWidth; - if (string.IsNullOrEmpty(color) == false) - { - tmpBorderColor = BorderColor; - BorderColor = color; - } - - RenderBase(sb); - if (LineCap != eLineCap.Flat) - { - sb.AppendFormat(" stroke-linecap=\"{0}\"", LineCap == eLineCap.Round ? "round" : "square"); - } - if (LineJoin != SvgLineJoin.Miter) - { - sb.AppendFormat(" stroke-linejoin=\"{0}\"", LineJoin); - } - - if (string.IsNullOrEmpty(filter) == false) - { - sb.Append(" " + filter); - } - - sb.AppendFormat("/>"); - - BorderWidth = tmpBorderWidth; - if (string.IsNullOrEmpty(color) == false) - { - BorderColor = tmpBorderColor; - } - } - - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgRenderLineItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgRenderLineItem.cs deleted file mode 100644 index 37298cc720..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgRenderLineItem.cs +++ /dev/null @@ -1,196 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.Utils; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlus.Graphics.Math; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using System; -using System.Globalization; -using System.Text; -namespace EPPlusImageRenderer.RenderItems -{ - internal class SvgRenderLineItem : SvgRenderItem - { - - public SvgRenderLineItem(DrawingBase renderer, BoundingBox parent) : base(renderer, parent) - { - - } - double _x1, _y1, _x2, _y2; - public double X1 - { - get - { - return _x1; - } - set - { - _x1 = value; - UpdateBounds(); - } - } - public double Y1 - { - get - { - return _y1; - } - set - { - _y1 = value; - UpdateBounds(); - } - } - public double X2 - { - get - { - return _x2; - } - set - { - _x2 = value; - UpdateBounds(); - } - } - public double Y2 - { - get - { - return _y2; - } - set - { - _y2 = value; - UpdateBounds(); - } - } - private void UpdateBounds() - { - var px = Math.Min(X1, X2); - var py = Math.Min(Y1, Y2); - var sizeX = Math.Abs(X2 - X1); - var sizeY = Math.Abs(Y2 - Y1); - - Bounds.Position = new Vector2(px, py); - Bounds.Size = new Vector2(sizeX, sizeY); - } - - public override RenderItemType Type => RenderItemType.Line; - - public override void Render(StringBuilder sb) - { - //Draw transparent lines to create the compond line effect, as SVG does not support compound lines natively - switch (CompoundLineStyle) - { - case eCompoundLineStyle.Double: - LineCap = eLineCap.Flat; - var name = $"double-stroke-{Guid.NewGuid().ToString()}"; - sb.Append($""); - - RenderLineItem(sb, BorderWidth, "white", null); - RenderLineItem(sb, BorderWidth * (3D / 7D), "black", null); - sb.Append($""); - break; - case eCompoundLineStyle.DoubleThickThin: - WriteThickThin(sb, "double-thick-thin-stroke-{0}", (BorderWidth ?? 1D) * 1D / 7D); - break; - case eCompoundLineStyle.DoubleThinThick: - WriteThickThin(sb, "double-thin-thick-stroke-{0}", ((BorderWidth ?? 1D) * 1D / 7D) * -1); - break; - case eCompoundLineStyle.TripleThinThickThin: - var guid= Guid.NewGuid().ToString(); - var gapOffset = 5 * BorderWidth.Value / 16; - name = $"triple-stroke-{guid}"; - sb.Append($""); - sb.Append($""); - sb.Append($""); - sb.Append($""); - RenderLineItem(sb, BorderWidth, "white", null); - RenderLineItem(sb, BorderWidth * (1D / 8D), "black", $"filter=\"url(#gap-left-{guid})\""); - RenderLineItem(sb, BorderWidth * (1D / 8D), "black", $"filter=\"url(#gap-right-{guid})\""); - sb.Append($""); - break; - default: - RenderLineItem(sb, null, null, null); - break; - } - } - private void WriteThickThin(StringBuilder sb, string name, double gapOffset) - { - var guid = Guid.NewGuid().ToString(); - name = string.Format(name, guid); - string gapFilterName = $"f-gap-shift-{guid}"; - sb.Append(""); - sb.Append($""); - sb.Append($""); - RenderLineItem(sb, BorderWidth, "white", null); - RenderLineItem(sb, BorderWidth * (1D / 4D), "black", $"filter=\"url(#{gapFilterName})\""); - sb.Append($""); - } - - internal string Suffix = "px"; - - private void RenderLineItem(StringBuilder sb, double? borderWidth, string color, string filter) - { - if(Suffix == "%") - { - sb.AppendFormat(" RenderItemType.Path; } - public List Commands { get; set; } = new List(); - - public override void Render(StringBuilder sb) - { - //Draw transparent lines to create the compond line effect, as SVG does not support compound lines natively - switch (CompoundLineStyle) - { - case eCompoundLineStyle.Single: - RenderPathItem(sb, null, null, null); - break; - case eCompoundLineStyle.Double: - var name = $"double-stroke-{Guid.NewGuid().ToString()}"; - sb.Append($""); - - RenderPathItem(sb, BorderWidth, "white", null); - RenderPathItem(sb, BorderWidth * (3D / 7D), "black", null); - sb.Append($""); - break; - case eCompoundLineStyle.DoubleThickThin: - WriteThickThin(sb, (BorderWidth??1D) * 1D / 7D); - break; - case eCompoundLineStyle.DoubleThinThick: - WriteThickThin(sb, ((BorderWidth ?? 1D) * 1D / 7D) * -1); - break; - case eCompoundLineStyle.TripleThinThickThin: - var guid = Guid.NewGuid().ToString(); - var gapOffset = 5 * BorderWidth.Value / 16; - name = $"triple-stroke-{guid}"; - sb.Append($""); - sb.Append($""); - sb.Append($""); - sb.Append($""); - RenderPathItem(sb, BorderWidth, "white", null); - RenderPathItem(sb, BorderWidth * (1D / 8D), "black", $"filter=\"url(#gap-left-{guid})\""); - RenderPathItem(sb, BorderWidth * (1D / 8D), "black", $"filter=\"url(#gap-right-{guid})\""); - sb.Append($""); - break; - } - } - - private void WriteThickThin(StringBuilder sb, double gapOffset) - { - var guid = Guid.NewGuid().ToString(); - var name = $"double-thick-thin-stroke-{guid}"; - string gapFilterName = $"f-gap-shift-{guid}"; - sb.Append(""); - sb.Append($""); - sb.Append($""); - RenderPathItem(sb, BorderWidth, "white", null); - RenderPathItem(sb, BorderWidth * (1 / 4D), "black", $"filter=\"url(#{gapFilterName})\""); - sb.Append($""); - } - - private void RenderPathItem(StringBuilder sb, double? borderWidth, string color, string filter) - { - //var width = Bounds.Width.PointToPixel(); - //var height = Bounds.Height.PointToPixel(); - - sb.Append($" CloneCommands(List commands) - { - var cloneList = new List(); - foreach (var cmd in commands) - { - cloneList.Add(cmd.Clone()); - } - return cloneList; - } - internal override void GetBounds(out double il, out double it, out double ir, out double ib) - { - il = it = ir = ib = 0; - foreach(var c in Commands) - { - if(c.Type==PathCommandType.Arc) - { - var wR = c.Coordinates[0]; - var hR = c.Coordinates[1]; - var x = c.Coordinates[5]; - var y = c.Coordinates[6]; - if (x < il) il = x + wR; //TODO: Maybe adjust with start- and swing- angle - if (x > ir) ir = x + wR; - if (y < it) it = y + hR; - if (y > ib) ib = y + hR; - } - else - { - for(int i = 0; i < c.Coordinates.Length;i++) - { - if (i % 2 == 0) - { - if (c.Coordinates[i] < il) il = c.Coordinates[i]; - if (c.Coordinates[i] > ir) ir = c.Coordinates[i]; - } - else - { - if (c.Coordinates[i] < it) it = c.Coordinates[i]; - if (c.Coordinates[i] > ib) ib = c.Coordinates[i]; - } - } - } - } - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgRenderRectItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgRenderRectItem.cs deleted file mode 100644 index 8e71354cc0..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgRenderRectItem.cs +++ /dev/null @@ -1,94 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using System.Collections.Generic; -using System.Globalization; -using System.Text; -using EPPlus.Graphics; -using EPPlus.Export.ImageRenderer.Utils; -using OfficeOpenXml.Drawing.Theme; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Fonts.OpenType.Utils; - -namespace EPPlusImageRenderer.RenderItems -{ - internal class SvgRenderRectItem : SvgRenderItem - { - public SvgRenderRectItem(DrawingBase renderer, BoundingBox parent) : base(renderer, parent) - { - - } - - public double Left { get { return Bounds.Left; } set { Bounds.Left = value; } } - public double Top { get { return Bounds.Top; } set { Bounds.Top = value; } } - public double Width { get { return Bounds.Width; } set { Bounds.Width = value; } } - public double Height { get { return Bounds.Height; } set { Bounds.Height = value; } } - public double Right { get { return Bounds.Left + Width; } } - public double Bottom { get { return Bounds.Top + Height; } } - public double GlobalLeft => Bounds.GlobalLeft; - public double GlobalTop => Bounds.GlobalTop; - public double GlobalRight => Bounds.GlobalLeft + Width; - public double GlobalBottom => Bounds.GlobalTop + Height; - public override RenderItemType Type => RenderItemType.Rect; - - public override void Render(StringBuilder sb) - { - RenderRect(sb); - } - - internal string Suffix = "px"; - - internal void RenderRect(StringBuilder sb) - { - if (Suffix == "%") - { - sb.AppendFormat(""); - } - - internal override SvgRenderItem Clone(SvgShape svgDocument) - { - var clone = new SvgRenderRectItem(svgDocument, svgDocument.Bounds); - CloneBase(clone); - - clone.Left = Left * svgDocument.Bounds.Width; - clone.Top = Top * svgDocument.Bounds.Height; - clone.Width = svgDocument.Bounds.Width * Width; - clone.Height = svgDocument.Bounds.Height * Height; - - return clone; - } - internal override void GetBounds(out double il, out double it, out double ir, out double ib) - { - il = Left; - it = Top; - ir = Width; - ib = Height; - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgUseRefItem.cs b/src/EPPlus.Export.ImageRenderer/RenderItems/SvgUseRefItem.cs deleted file mode 100644 index d695f22428..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderItems/SvgUseRefItem.cs +++ /dev/null @@ -1,61 +0,0 @@ -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderItems -{ - internal class SvgUseRefItem : RenderItem - { - internal string Href { get; private set; } = null; - - internal double X - { - get - { - return Bounds.Left; - } - set - { - Bounds.Left = value; - } - } - - internal double Y - { - get - { - return Bounds.Top; - } - set - { - Bounds.Top = value; - } - } - - /// - /// Any bounds on this property are considered OFFSETS to the original object - /// Width and Height ONLY matter if this refers to an or element - /// - /// - internal SvgUseRefItem(DrawingBase renderer, BoundingBox parent, string idForHref) : base(renderer, parent) - { - Href = "#" + idForHref; - } - - public override RenderItemType Type => RenderItemType.Reference; - - - - public override void Render(StringBuilder sb) - { - string renderStr = $""; - sb.Append(renderStr); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderTool/RenderItemClassGenerator.cs b/src/EPPlus.Export.ImageRenderer/RenderTool/RenderItemClassGenerator.cs deleted file mode 100644 index fbf0ccea6e..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderTool/RenderItemClassGenerator.cs +++ /dev/null @@ -1,517 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems; -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlus.Export.ImageRenderer.Svg; -using EPPlus.Export.ImageRenderer.Svg.DefinitionUtils; -using EPPlus.Export.ImageRenderer.Svg.DefinitionUtils.UtillNodes; -using EPPlus.Export.ImageRenderer.Svg.NodeAttributes; -using EPPlus.Export.ImageRenderer.Svg.Writer; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -using OfficeOpenXml.Utils; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using System.Linq; -using System.Text; - - -namespace EPPlus.Export.ImageRenderer.RenderTool -{ - internal class RenderItemClassGenerator - { - DrawingItemForTesting SvgCanvas; - - internal RenderItemClassGenerator(double width, double height) - { - SvgCanvas = new DrawingItemForTesting(new BoundingBox(width.PixelToPoint(), height.PixelToPoint())); - - } - - public enum RenderItemClasses - { - Rect, - TextBox, - Shape, - CircleSegment - } - - SvgTextBox GenerateTextBox(DrawingBase baseRenderer, BoundingBox parent, BoundingBox maxBounds) - { - return new SvgTextBox(baseRenderer, parent, maxBounds); - } - - - public Dictionary GetItemProperties(RenderItemClasses item) - { - switch (item) - { - case RenderItemClasses.Rect: - return new Dictionary { { "Top", 10d }, { "Left", 10d }, { "Width", 10d }, { "Height", 10d }, { "Opacity", 0.8 }, { "Fill", Color.Goldenrod } }; - case RenderItemClasses.CircleSegment: - return new Dictionary { { "angle", 90d }, { "radius", 144d }, { "cx", 144d }, { "cy", 144d } }; - case RenderItemClasses.TextBox: - default: - throw new NotImplementedException("This class has not been implemented as an option yet"); - - } - } - - private string RenderCircleSegment(DrawingBase baseItem, Dictionary itemProperties) - { - return RenderCircleSegment((double)itemProperties["angle"], (double)itemProperties["radius"], (double)itemProperties["cx"], (double)itemProperties["cy"]); - } - - string RenderCircleSegment(double degree, double radius, double cx, double cy) - { - degree = degree % 360; - - if (degree < 0) - { - degree = 360 - degree; - } - - //Adjust by -90 so it starts from the top - var angleRadians = (degree - 90d) * (Math.PI / 180.0d); - - //radius = radius.PixelToPoint(); - //cx = radius.PixelToPoint(); - //cy = radius.PixelToPoint(); - - var xPoint = cx + (radius * Math.Cos(angleRadians)); - var yPoint = cy + (radius * Math.Sin(angleRadians)); - - Coordinate endPoint = new Coordinate(xPoint, yPoint); - - var baseBB = new BoundingBox(); - - //96x96 px - baseBB.Width = 72 * 4; - baseBB.Height = 72 * 4; - - var baseItem = new DrawingItemForTesting(baseBB); - - BoundingBox parent = new BoundingBox(); - - //Transform rotationPoint = new Transform(); - //rotationPoint.SetLocalPositionWithWorldCoordinates(new Vector2(cx, cy)); - //var item = new SvgGroupItemNew(baseItem, parent, -45d, rotationPoint); - - var slice = new SvgRenderPathItem(baseItem, baseItem.Bounds); - - //item.AddChildItem(slice); - - var startPoint = new Coordinate(cx, cy - radius); - - var moveCenter = new PathCommands(PathCommandType.Move, slice, cx / baseItem.Bounds.Width, cy / baseItem.Bounds.Height); - var lineToStart = new PathCommands(PathCommandType.Line, slice, startPoint.X / baseItem.Bounds.Width, startPoint.Y / baseItem.Bounds.Height); - - var w = baseItem.Bounds.Width.PointToPixel(); - var h = baseItem.Bounds.Height.PointToPixel(); - - var radX = radius.PointToPixel() / w; - var radY = radius.PointToPixel() / h; - - var arcCommand = new PathCommands(PathCommandType.Arc, slice, new double[] { radX, radY, 0, degree > 180 ? 1 : 0, 1, endPoint.X / baseItem.Bounds.Width, endPoint.Y / baseItem.Bounds.Height }); - - slice.Commands.Add(moveCenter); - slice.Commands.Add(lineToStart); - slice.Commands.Add(arcCommand); - - slice.FillColor = "red"; - slice.BorderColor = "green"; - - baseItem.RenderItems.Add(slice); - - var sb = new StringBuilder(); - - baseItem.Render(sb); - - return sb.ToString(); - - //return baseItem; - } - - public SvgRenderRectItem AddRect(SvgGroupItemNew parentGroup = null) - { - SvgRenderRectItem rectItem; - - if (parentGroup == null) - { - rectItem = new SvgRenderRectItem(SvgCanvas, SvgCanvas.Bounds); - SvgCanvas.RenderItems.Add(rectItem); - } - else - { - rectItem = new SvgRenderRectItem(SvgCanvas, parentGroup.Bounds); - parentGroup.AddChildItem(rectItem); - } - return rectItem; - } - - //SvgRenderRectItem GenerateRect(DrawingBase baseItem) - //{ - // var rect = new SvgRenderRectItem(baseItem, baseItem.Bounds); - - // return rect; - //} - - //public RenderItem GenerateItem(RenderItemClasses preset, DrawingBase baseItem) - //{ - - // switch (preset) - // { - // case RenderItemClasses.Rect: - // return GenerateRect(baseItem); - - - // default: - // throw new NotImplementedException("This class has not been implemented as an option yet"); - // } - //} - - //private RenderItem GenerateClass(RenderItemClasses preset, DrawingBase baseItem) - //{ - // switch (preset) - // { - // case RenderItemClasses.Rect: - // return GenerateRect(baseItem); - - - // default: - // throw new NotImplementedException("This class has not been implemented as an option yet"); - // } - //} - /////// - /////// For Testing the specific renderItem class - /////// - /////// - /////// - /////// - /////// - ////public RenderItem AddIndividualClass(RenderItemClasses item) - ////{ - //// GenerateFromClasses - //// var svgCanvas = new DrawingItemForTesting(new BoundingBox(width.PixelToPoint(), height.PixelToPoint())); - - //// RenderItem renderItem; - - //// if (item == RenderItemClasses.CircleSegment) - //// { - //// return GenerateFromCircle(item, svgCanvas, itemProperties); - //// } - //// else - //// { - //// renderItem = GenerateFromClasses(item, svgCanvas, itemProperties); - //// } - - //// svgCanvas.RenderItems.Add(renderItem); - - //// var sb = new StringBuilder(); - - //// svgCanvas.Render(sb); - - //// return sb.ToString(); - ////} - - //public enum RenderPresets - //{ - // ContainerMargins, - // RotatingContainer, - // PatternFill, - //} - - //public string RenderTest(RenderPresets preset) - //{ - // return GenerateFromPreset(preset); - //} - - - //private string rotatingContainer() - //{ - // var baseBB = new BoundingBox(); - - // //96x96 px - // baseBB.Width = 72; - // baseBB.Height = 72; - - // var baseItem = new DrawingItemForTesting(baseBB); - - // BoundingBox parent = new BoundingBox(); - - // var groupItem = new SvgGroupItemNew(baseItem, parent, 45); - - // groupItem.Position.Left = 10; - // groupItem.Position.Top = 10; - - // SvgRenderRectItem rectItem = new SvgRenderRectItem(baseItem, groupItem.Bounds); - - // rectItem.FillColor = "red"; - // rectItem.FillOpacity = 0.2d; - - // rectItem.Width = 20; - // rectItem.Height = 20; - - // groupItem.AddChildItem(rectItem); - - - // SvgRenderRectItem siblingItem = new SvgRenderRectItem(baseItem, groupItem.Bounds); - // siblingItem.FillColor = "blue"; - // siblingItem.FillOpacity = 0.2d; - - // siblingItem.Width = 20; - // siblingItem.Height = 20; - - // siblingItem.Bounds.Left = 20; - // siblingItem.Bounds.Top = 20; - - // groupItem.AddChildItem(siblingItem); - - // groupItem.SetRotationPointToCenterOfGroup(); - - // SvgRenderRectItem centerOfGroupMarker = new SvgRenderRectItem(baseItem, baseItem.Bounds); - // centerOfGroupMarker.FillColor = "green"; - // centerOfGroupMarker.FillOpacity = 0.8d; - - // centerOfGroupMarker.Width = 6; - // centerOfGroupMarker.Height = 6; - - // centerOfGroupMarker.Left = 30 - (centerOfGroupMarker.Width / 2); - // centerOfGroupMarker.Top = 30 - (centerOfGroupMarker.Height / 2); - - // baseItem.RenderItems.Add(centerOfGroupMarker); - - // var sb = new StringBuilder(); - - // baseItem.RenderItems.Add(groupItem); - - // baseItem.Render(sb); - - // return sb.ToString(); - //} - - //private string containerMargins() - //{ - // var baseBB = new BoundingBox(); - - // baseBB.Width = 400; - // baseBB.Height = 400; - - // var baseItem = new DrawingItemForTesting(baseBB); - - // SvgRenderRectItem myBgItem = new SvgRenderRectItem(baseItem, baseItem.Bounds); - // myBgItem.FillColor = "purple"; - // myBgItem.FillOpacity = 0.2d; - - // SvgRenderRectItem myInnerItem = new SvgRenderRectItem(baseItem, myBgItem.Bounds); - - // myInnerItem.FillColor = "green"; - // myInnerItem.FillOpacity = 0.8d; - - // myInnerItem.Width = 50; - // myInnerItem.Height = 50; - - // var container = new SvgContainerItem(myInnerItem, myBgItem); - - // container.MarginLeft = 5; - // container.MarginRight = 5; - // container.MarginTop = 5; - // container.MarginBottom = 5; - - // container.ApplyMargins(); - - // baseItem.RenderItems.Add(container); - - // var sb = new StringBuilder(); - - // baseItem.Render(sb); - - // return sb.ToString(); - //} - - //internal string pattern() - //{ - // var baseBB = new BoundingBox(); - - // baseBB.Width = 400; - // baseBB.Height = 400; - - // var baseItem = new DrawingItemForTesting(baseBB); - - - // var linePattern = new LinePattern(baseItem, "testLines", LinePatternType.Vertical); - // linePattern.SetNumberOfLines(3); - - // var defItem = new DefinitionGroup(baseItem); - // defItem.Items.Add(linePattern); - - // var rectItem = new SvgRenderRectItem(baseItem, baseItem.Bounds); - - // rectItem.FillColor = $"url(#{"testLines"})"; - - // rectItem.Width = 200; - // rectItem.Height = 200; - - // baseItem.RenderItems.Add(defItem); - // baseItem.RenderItems.Add(rectItem); - // var useItem = new SvgUseRefItem(baseItem, baseItem.Bounds, "testLines"); - - // baseItem.RenderItems.Add(useItem); - - // var sb = new StringBuilder(); - - // baseItem.Render(sb); - - // return sb.ToString(); - //} - - //internal string pattern2() - //{ - // var baseBB = new BoundingBox(); - - // baseBB.Width = 400; - // baseBB.Height = 400; - - // var baseItem = new DrawingItemForTesting(baseBB); - - // string refId = "grid"; - - // var defItem = new DefinitionGroup(baseItem); - - // var dynaGrid = new DynamicGridDefGroup(baseItem, refId, 7, 5); - // defItem.Items.Add(dynaGrid); - - // baseItem.RenderItems.Add(defItem); - - // var useItem = new SvgUseRefItem(baseItem, baseItem.Bounds, refId); - // useItem.Bounds.Width = 300; - // useItem.Bounds.Height = 200; - - // baseItem.RenderItems.Add(useItem); - - // var sb = new StringBuilder(); - - // baseItem.Render(sb); - - // return sb.ToString(); - //} - - //private string GenerateFromPreset(RenderPresets preset) - //{ - // switch (preset) - // { - // case RenderPresets.ContainerMargins: - // return containerMargins(); - // case RenderPresets.RotatingContainer: - // return rotatingContainer(); - // case RenderPresets.PatternFill: - // return pattern2(); - // } - // return ""; - //} - - ////private RenderItem GenerateRect(DrawingBase baseItem, Dictionary itemProperties) - ////{ - //// var rectItem = new SvgRenderRectItem(baseItem, baseItem.Bounds); - - //// if (itemProperties.ContainsKey("Top")) - //// { - //// rectItem.Top = (double)itemProperties["Top"]; - //// } - //// if (itemProperties.ContainsKey("Left")) - //// { - //// rectItem.Left = (double)itemProperties["Left"]; - //// } - //// if (itemProperties.ContainsKey("Width")) - //// { - //// rectItem.Width = (double)itemProperties["Width"]; - //// } - //// if (itemProperties.ContainsKey("Height")) - //// { - //// rectItem.Height = (double)itemProperties["Height"]; - //// } - //// if (itemProperties.ContainsKey("Opacity")) - //// { - //// rectItem.FillOpacity = (double)itemProperties["Opacity"]; - //// } - //// if (itemProperties.ContainsKey("Fill")) - //// { - //// rectItem.FillColor = "#" + ((Color)itemProperties["Fill"]).ToColorString(); - //// } - - //// return rectItem; - ////} - - ////private string GenerateFromCircle(RenderItemClasses preset, DrawingBase baseItem, Dictionary itemProperties) - ////{ - //// return RenderCircleSegment(baseItem, itemProperties); - ////} - - ////private RenderItem GenerateFromClasses(RenderItemClasses preset, DrawingBase baseItem, Dictionary itemProperties) - ////{ - //// switch (preset) - //// { - //// case RenderItemClasses.Rect: - //// return GenerateRect(baseItem, itemProperties); - //// case RenderItemClasses.CircleSegment: - //// //return RenderCircleSegment(baseItem, itemProperties); - //// case RenderItemClasses.RenderTextbox: - - - //// default: - //// throw new NotImplementedException("This class has not been implemented as an option yet"); - //// } - ////} - - ////internal string RenderSvgElement(SvgElement element) - ////{ - //// string retStr = string.Empty; - - //// using (var ms = EPPlusMemoryManager.GetStream()) - //// { - //// SvgWriter writer = new SvgWriter(ms, Encoding.UTF8); - //// writer.RenderSvgElement(element, true); - //// ms.Position = 0; - //// using (var sr = new StreamReader(ms)) - //// { - //// retStr = sr.ReadToEnd(); - //// return retStr; - //// } - //// } - ////} - - ////internal SvgElement GetDefinitions(BoundingBox boundingBox, out string nameId, bool AllowOverflow = false) - ////{ - //// nameId = "boundingBox"; - //// var def = new SvgElement("defs"); - - //// string defaultName = "defaultRect"; - - //// if (AllowOverflow == false) - //// { - //// var bb = new SvgElement("rect"); - //// bb.AddAttribute("width", boundingBox.Width); - //// bb.AddAttribute("height", boundingBox.Height); - //// bb.AddAttribute("id", defaultName); - - //// def.AddChildElement(bb); - - //// var clipPath = new SvgElement("clipPath"); - //// clipPath.AddAttribute("id", nameId); - - //// def.AddChildElement(clipPath); - - //// var useElement = new SvgElement("use"); - //// useElement.AddAttribute("href", $"#{defaultName}"); - - //// clipPath.AddChildElement(useElement); - //// } - - //// return def; - ////} - - } -} diff --git a/src/EPPlus.Export.ImageRenderer/RenderTool/RenderToolUtils.cs b/src/EPPlus.Export.ImageRenderer/RenderTool/RenderToolUtils.cs deleted file mode 100644 index fbca170203..0000000000 --- a/src/EPPlus.Export.ImageRenderer/RenderTool/RenderToolUtils.cs +++ /dev/null @@ -1,30 +0,0 @@ -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.ShapeDefinitions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.RenderToolUtils -{ - internal static class RenderToolUtils - { - - //static T GetRenderItemOfType(DrawingBase baseItem, object[] objParams) where T : RenderItem - //{ - // var textboxType = typeof(TextBoxRect); - - // switch (typeof(T)) - // { - // case TextBoxRect textBox: - // { - - // break; - // } - // default: - // throw new NotSupportedException(); - // } - //} - } -} diff --git a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/PresetShapeDefinition.Sync.cs b/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/PresetShapeDefinition.Sync.cs deleted file mode 100644 index 9a1926e767..0000000000 --- a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/PresetShapeDefinition.Sync.cs +++ /dev/null @@ -1,253 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Utils.XML; -using OfficeOpenXml.Utils.EnumUtils; -using System; -using System.Collections.Generic; -using System.IO; -using System.Xml; - -namespace EPPlusImageRenderer.ShapeDefinitions -{ - internal partial class PresetShapeDefinitions - { - public static void LoadPresetShapeDefinitionFromXml() - { - var xmlFile = Directory.GetCurrentDirectory() + "\\resource\\presetShapeDefinitions.xml"; - - try - { - var ms = new MemoryStream(File.ReadAllBytes(xmlFile)); -#if NET35 - var xr = XmlReader.Create(ms, new XmlReaderSettings() - { - ProhibitDtd=true, - IgnoreWhitespace = true - }); -#else - var xr = XmlReader.Create(ms, new XmlReaderSettings() - { - DtdProcessing = DtdProcessing.Prohibit, - IgnoreWhitespace = true, - Async = true - }); -#endif - - while (xr.Read()) - { - if (xr.NodeType == XmlNodeType.Element) - { - if (xr.LocalName != "presetShapeDefinitons") - { - var item = LoadPresetShapeDefinition(xr); - _shapeDefinitions.Add(item.Style, item); - } - } - } - } - catch (Exception ex) - { - throw (new IOException("Cannot preset shape definitions file:presetShapeDefinitions.xml", ex)); - } - } - - public static bool LoadPresetShapeDefinitionFromXmlTriangle() - { - _shapeDefinitions = new Dictionary(); - - var xmlFile = Directory.GetCurrentDirectory() + "\\resource\\triangleOnly.xml"; - var fs = new FileStream(xmlFile, FileMode.Open, FileAccess.Read); -#if NET35 - var xr = XmlReader.Create(fs, new XmlReaderSettings() - { - ProhibitDtd=true, - IgnoreWhitespace = true - }); -#else - var xr = XmlReader.Create(fs, new XmlReaderSettings() - { - DtdProcessing = DtdProcessing.Prohibit, - IgnoreWhitespace = true, - Async = true - }); -#endif - while (xr.Read()) - { - if (xr.NodeType == XmlNodeType.Element) - { - if (xr.LocalName == "triangle" && xr.NodeType != XmlNodeType.EndElement) - { - var item = LoadPresetShapeDefinition(xr); - _shapeDefinitions.Add(item.Style, item); - } - } - } - return true; - } - - private static ShapeDefinition LoadPresetShapeDefinition(XmlReader xr) - { - var style = xr.LocalName.ToEnum(); - if (!style.HasValue) throw new InvalidOperationException(); - var psd = new ShapeDefinition - { - Style = style.Value, - }; - LoadFromXml(psd, xr); - return psd; - } - - private static void LoadFromXml(ShapeDefinition psd, XmlReader xr) - { - while (xr.Read()) - { - if (xr.NodeType == XmlNodeType.Element) - { - switch (xr.LocalName) - { - case "avLst": - psd.ShapeAdjustValues = LoadShapeGuides(xr); - break; - case "gdLst": - psd.ShapeGuides = LoadShapeGuides(xr); - break; - case "ahLst": - psd.ShapeAdjustHandles = LoadAdjustHandle(xr); - break; - case "cxnLst": - psd.ShapeConnectionSite = LoadConnectionLst(xr); - break; - case "rect": - psd.TextBoxRect = LoadRect(xr); - break; - case "pathLst": - psd.ShapePaths = LoadShapePaths(xr); - break; - } - } - else if (xr.NodeType == XmlNodeType.EndElement && xr.LocalName.Equals(psd.Style.ToString(), StringComparison.InvariantCultureIgnoreCase)) - { - return; - } - } - } - - private static TextBoxRect LoadRect(XmlReader xr) - { - // < rect l = "l" t = "y1" r = "x4" b = "b" xmlns = "http://schemas.openxmlformats.org/drawingml/2006/main" /> - var rect = new TextBoxRect() - { - TopName = xr.GetAttribute("t"), - BottomName = xr.GetAttribute("b"), - LeftName = xr.GetAttribute("l"), - RightName = xr.GetAttribute("r") - }; - - return rect; - } - - private static List LoadConnectionLst(XmlReader xr) - { - var shapeConnectionSite = new List(); - - while (xr.Read() && (xr.NodeType != XmlNodeType.EndElement && xr.LocalName != "cxnLst")) - { - var newConnection = new ShapeConnectionSite(); - - var attrStr = xr.GetAttribute("ang"); - - newConnection.Angle = xr.GetAttribute("ang"); - xr.Read(); - - newConnection.PositionCoordinate = new ShapePositionCoordinate() { X = xr.GetAttribute("x"), Y = xr.GetAttribute("y") }; - - shapeConnectionSite.Add(newConnection); - xr.Read(); - - if (xr.LocalName == "cxnLst" && xr.NodeType == XmlNodeType.EndElement) - { - break; - } - } - - return shapeConnectionSite; - } - - private static List LoadAdjustHandle(XmlReader xr) - { - var l = new List(); - var name = xr.LocalName; - while (xr.Read()) - { - if (xr.NodeType == XmlNodeType.Element) - { - switch (xr.LocalName) - { - case "ahXY": - l.Add(ShapeAdjustHandleBase.CreateXy(xr)); - break; - case "ahPolar": - l.Add(ShapeAdjustHandleBase.CreatePolar(xr)); - break; - case "pos": - l[l.Count - 1].PositionCoordinate = new ShapePositionCoordinate() { X = xr.GetAttribute("x"), Y = xr.GetAttribute("y") }; - break; - } - } - else if (xr.NodeType == XmlNodeType.EndElement && xr.LocalName == name) - { - break; - } - } - return l; - } - - private static List LoadShapeGuides(XmlReader xr) - { - var l = new List(); - var name = xr.LocalName; - while (xr.Read()) - { - if (xr.NodeType == XmlNodeType.Element && xr.LocalName == "gd") - { - l.Add(new ShapeGuide() { Name = xr.GetAttribute("name"), Formula = xr.GetAttribute("fmla") }); - } - - if (xr.NodeType == XmlNodeType.EndElement && xr.LocalName == name) - { - break; - } - } - return l; - } - - - private static List LoadShapePaths(XmlReader xr) - { - var list = new List(); - while (xr.Read()) - { - if (xr.LocalName == "path" && xr.NodeType == XmlNodeType.Element) - { - list.Add(new DrawingPath(xr)); - } - else if (xr.IsEndElementWithName("pathLst")) - { - break; - } - } - return list; - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/PresetShapeDefiniton.Async.cs b/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/PresetShapeDefiniton.Async.cs deleted file mode 100644 index 4ab70b9087..0000000000 --- a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/PresetShapeDefiniton.Async.cs +++ /dev/null @@ -1,255 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using OfficeOpenXml.Drawing; -using System; -using System.Collections.Generic; -using System.IO; - -#if !NET35 -//using System.Reflection.Metadata.Ecma335; -using System.Threading.Tasks; -//using System.Windows.Markup; -#endif -using System.Xml; - -namespace EPPlusImageRenderer.ShapeDefinitions -{ - internal partial class PresetShapeDefinitions - { - static object _syncRoot=new object(); - static Dictionary _shapeDefinitions=null; - public static Dictionary ShapeDefinitions - { - get - { - lock (_syncRoot) - { - if (_shapeDefinitions == null) - { - _shapeDefinitions = new Dictionary(); -#if NET35 - LoadPresetShapeDefinitionFromXml(); -#else - Task.Run(() => LoadPresetShapeDefinitionFromXmlAsync()).Wait(); -#endif - - } - return _shapeDefinitions; - } - } - } - -#if !NET35 - public static async Task LoadPresetShapeDefinitionFromXmlAsync() - { - var xmlFile = Directory.GetCurrentDirectory() + "\\resource\\presetShapeDefinitions.xml"; - - try - { - var ms=new MemoryStream(File.ReadAllBytes(xmlFile)); - var xr = XmlReader.Create(ms, new XmlReaderSettings() - { - DtdProcessing = DtdProcessing.Prohibit, - IgnoreWhitespace = true, - Async = true - }); - - while (await xr.ReadAsync()) - { - if (xr.NodeType == XmlNodeType.Element) - { - if (xr.LocalName != "presetShapeDefinitons") - { - var item = await LoadPresetShapeDefinitionAsync(xr); - _shapeDefinitions.Add(item.Style, item); - } - } - } - } - catch(Exception ex) - { - throw (new IOException("Cannot preset shape definitions file:presetShapeDefinitions.xml", ex)); - } - } - - public static async Task LoadPresetShapeDefinitionFromXmlAsyncTriangle() - { - _shapeDefinitions = new Dictionary(); - - var xmlFile = Directory.GetCurrentDirectory() + "\\resource\\triangleOnly.xml"; - var fs = new FileStream(xmlFile, FileMode.Open, FileAccess.Read); - var xr = XmlReader.Create(fs, new XmlReaderSettings() - { - DtdProcessing = DtdProcessing.Prohibit, - IgnoreWhitespace = true, - Async = true - }); - while (await xr.ReadAsync()) - { - if (xr.NodeType == XmlNodeType.Element) - { - if (xr.LocalName == "triangle" && xr.NodeType != XmlNodeType.EndElement) - { - var item = await LoadPresetShapeDefinitionAsync(xr); - _shapeDefinitions.Add(item.Style, item); - } - } - } - return true; - } - - private static async Task LoadPresetShapeDefinitionAsync(XmlReader xr) - { - if (Enum.TryParse(xr.LocalName, true, out var style)) - { - var psd = new ShapeDefinition() - { - Style = style - }; - await LoadFromXmlAsync(psd, xr); - return psd; - } - throw new InvalidOperationException(); - } - private static async Task LoadFromXmlAsync(ShapeDefinition psd, XmlReader xr) - { - while (await xr.ReadAsync()) - { - if (xr.NodeType == XmlNodeType.Element) - { - switch (xr.LocalName) - { - case "avLst": - psd.ShapeAdjustValues = await LoadShapeGuidesAsync(xr); - break; - case "gdLst": - psd.ShapeGuides = await LoadShapeGuidesAsync(xr); - break; - case "ahLst": - psd.ShapeAdjustHandles = await LoadAdjustHandleAsync(xr); - break; - case "cxnLst": - psd.ShapeConnectionSite = await LoadConnectionLstAsync(xr); - break; - case "rect": - psd.TextBoxRect = new TextBoxRect() { TopName = xr.GetAttribute("t"), BottomName = xr.GetAttribute("b"), LeftName = xr.GetAttribute("l"), RightName = xr.GetAttribute("r") }; - break; - case "pathLst": - psd.ShapePaths = LoadShapePaths(xr); - break; - } - } - else if (xr.NodeType == XmlNodeType.EndElement && xr.LocalName.Equals(psd.Style.ToString(), StringComparison.InvariantCultureIgnoreCase)) - { - return; - } - } - } - - private static async Task LoadRectAsync(XmlReader xr) - { - TextBoxRect rect; - while (await xr.ReadAsync()) - { - if (xr.NodeType == XmlNodeType.Element && xr.LocalName == "rect") - { - rect = new TextBoxRect() - { - TopName = xr.GetAttribute("t"), - BottomName = xr.GetAttribute("b"), - LeftName = xr.GetAttribute("l"), - RightName = xr.GetAttribute("r") - }; - return rect; - } - } - return null; - } - - private static async Task> LoadConnectionLstAsync(XmlReader xr) - { - var shapeConnectionSite = new List(); - - while (await xr.ReadAsync() && (xr.NodeType != XmlNodeType.EndElement && xr.LocalName != "cxnLst")) - { - var newConnection = new ShapeConnectionSite(); - - var attrStr = xr.GetAttribute("ang"); - - newConnection.Angle = xr.GetAttribute("ang"); - await xr.ReadAsync(); - - newConnection.PositionCoordinate = new ShapePositionCoordinate() { X = xr.GetAttribute("x"), Y = xr.GetAttribute("y") }; - - shapeConnectionSite.Add(newConnection); - await xr.ReadAsync(); - - if (xr.LocalName == "cxnLst" && xr.NodeType == XmlNodeType.EndElement) - { - break; - } - } - - return shapeConnectionSite; - } - - private static async Task> LoadAdjustHandleAsync(XmlReader xr) - { - var l = new List(); - var name = xr.LocalName; - while (await xr.ReadAsync()) - { - if (xr.NodeType == XmlNodeType.Element) - { - switch (xr.LocalName) - { - case "ahXY": - l.Add(ShapeAdjustHandleBase.CreateXy(xr)); - break; - case "ahPolar": - l.Add(ShapeAdjustHandleBase.CreatePolar(xr)); - break; - case "pos": - l[l.Count - 1].PositionCoordinate = new ShapePositionCoordinate() { X = xr.GetAttribute("x"), Y = xr.GetAttribute("y") }; - break; - } - } - else if (xr.NodeType == XmlNodeType.EndElement && xr.LocalName == name) - { - break; - } - } - return l; - } - - private static async Task> LoadShapeGuidesAsync(XmlReader xr) - { - var l = new List(); - var name = xr.LocalName; - while (await xr.ReadAsync()) - { - if (xr.NodeType == XmlNodeType.Element && xr.LocalName == "gd") - { - l.Add(new ShapeGuide() { Name = xr.GetAttribute("name"), Formula = xr.GetAttribute("fmla") }); - } - - if (xr.NodeType == XmlNodeType.EndElement && xr.LocalName == name) - { - break; - } - } - return l; - } -#endif - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeAdjustHandle.cs b/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeAdjustHandle.cs deleted file mode 100644 index f1ae84b9a1..0000000000 --- a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeAdjustHandle.cs +++ /dev/null @@ -1,49 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using System.Xml; - -namespace EPPlusImageRenderer.ShapeDefinitions -{ - - public abstract class ShapeAdjustHandleBase - { - public abstract ShapeAdjustHandleType AhType { get; } - public ShapePositionCoordinate PositionCoordinate { get; set; } - internal static ShapeAdjustHandleXY CreateXy(XmlReader xr) - { - return new ShapeAdjustHandleXY() - { - HorizontalAdjustmentGuide = xr.GetAttribute("gdRefX"), - VerticalAdjustmentGuide = xr.GetAttribute("gdRefY"), - MinimumHorizontalAdjustment = xr.GetAttribute("minX"), - MaximumHorizontalAdjustment = xr.GetAttribute("minY"), - MinimumVerticalAdjustment = xr.GetAttribute("maxX"), - MaximumVerticalAdjustment = xr.GetAttribute("maxY"), - }; - } - internal static ShapeAdjustHandlePolar CreatePolar(XmlReader xr) - { - return new ShapeAdjustHandlePolar() - { - AngleAdjustmentGuide = xr.GetAttribute("gdRefAng"), - RadialAdjustmentGuide = xr.GetAttribute("gdRefR"), - MinimumAngleAdjustment = xr.GetAttribute("minAng"), - MinimumRadialAdjustment = xr.GetAttribute("minR"), - MaximumAngleAdjustment = xr.GetAttribute("maxAng"), - MaximumRadialAdjustment = xr.GetAttribute("maxR"), - }; - } - - internal abstract ShapeAdjustHandleBase Clone(); - } -} diff --git a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeAdjustHandlePolar.cs b/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeAdjustHandlePolar.cs deleted file mode 100644 index 37a80e1952..0000000000 --- a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeAdjustHandlePolar.cs +++ /dev/null @@ -1,39 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -namespace EPPlusImageRenderer.ShapeDefinitions -{ - public class ShapeAdjustHandlePolar : ShapeAdjustHandleBase - { - public override ShapeAdjustHandleType AhType => ShapeAdjustHandleType.Polar; - public string AngleAdjustmentGuide { get; set; } - public string RadialAdjustmentGuide { get; set; } - public object MaximumAngleAdjustment { get; set; } - public object MaximumRadialAdjustment { get; set; } - public object MinimumAngleAdjustment { get; set; } - public object MinimumRadialAdjustment { get; set; } - internal override ShapeAdjustHandleBase Clone() - { - return new ShapeAdjustHandlePolar() - { - AngleAdjustmentGuide = AngleAdjustmentGuide, - RadialAdjustmentGuide = RadialAdjustmentGuide, - MaximumAngleAdjustment = MaximumAngleAdjustment, - MinimumAngleAdjustment = MinimumAngleAdjustment, - MaximumRadialAdjustment = MaximumRadialAdjustment, - MinimumRadialAdjustment = MinimumRadialAdjustment, - }; - } - - } -} diff --git a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeAdjustHandleType.cs b/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeAdjustHandleType.cs deleted file mode 100644 index 17aaff6565..0000000000 --- a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeAdjustHandleType.cs +++ /dev/null @@ -1,20 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -namespace EPPlusImageRenderer.ShapeDefinitions -{ - public enum ShapeAdjustHandleType - { - XY, - Polar, - } -} diff --git a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeAdjustHandleXY.cs b/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeAdjustHandleXY.cs deleted file mode 100644 index e2f3a09bcb..0000000000 --- a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeAdjustHandleXY.cs +++ /dev/null @@ -1,65 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -namespace EPPlusImageRenderer.ShapeDefinitions -{ - public class ShapeAdjustHandleXY : ShapeAdjustHandleBase - { - public override ShapeAdjustHandleType AhType => ShapeAdjustHandleType.XY; - - /// - ///Specifies the name of the guide that is updated with the adjustment x position from this adjust handle. - ///The possible values for this attribute are defined by the ST_GeomGuideName simple type (§20.1.10.28). - /// - public string HorizontalAdjustmentGuide { get; set; } - /// - /// Specifies the name of the guide that is updated with the adjustment y position from this adjust handle. - /// - public string VerticalAdjustmentGuide { get; set; } - /// - /// Specifies the minimum horizontal position that is allowed for this adjustment handle. - /// If this attribute is omitted, then it is assumed that this adjust handle cannot move in the x direction. - /// That is the maxX and minX are equal. - /// - public object MinimumHorizontalAdjustment{ get; set; } - /// - /// Specifies the maximum horizontal position that is allowed for this adjustment handle. - /// If this attribute is omitted, then it is assumed that this adjust handle cannot move in the x direction. - /// That is the maxX and minX are equal. - /// - public object MaximumHorizontalAdjustment { get; set; } - /// - /// Specifies the minimum vertical position that is allowed for this adjustment handle. - /// If this attribute is omitted, then it is assumed that this adjust handle cannot move in the y direction.That is the maxY and minY are equal. - /// - public object MinimumVerticalAdjustment { get; set; } - /// - /// Specifies the minimum vertical position that is allowed for this adjustment handle. - /// If this attribute is omitted, then it is assumed that this adjust handle cannot move in the y direction. - /// That is the maxY and minY are equal. - /// - public object MaximumVerticalAdjustment { get; set; } - internal override ShapeAdjustHandleBase Clone() - { - return new ShapeAdjustHandleXY() - { - HorizontalAdjustmentGuide = HorizontalAdjustmentGuide, - VerticalAdjustmentGuide = VerticalAdjustmentGuide, - MinimumHorizontalAdjustment = MinimumHorizontalAdjustment, - MaximumHorizontalAdjustment = MaximumHorizontalAdjustment, - MinimumVerticalAdjustment = MinimumVerticalAdjustment, - MaximumVerticalAdjustment = MaximumVerticalAdjustment, - }; - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeConnectionSite.cs b/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeConnectionSite.cs deleted file mode 100644 index 5f2b84fbc3..0000000000 --- a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeConnectionSite.cs +++ /dev/null @@ -1,21 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -namespace EPPlusImageRenderer.ShapeDefinitions -{ - public class ShapeConnectionSite - { - public object Angle { get; set; } - public ShapePositionCoordinate PositionCoordinate { get; set; } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeDefinition.cs b/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeDefinition.cs deleted file mode 100644 index 3afe83e6e8..0000000000 --- a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeDefinition.cs +++ /dev/null @@ -1,511 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.DrawingRenderer.ShapeDefinitions; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Linq; - -namespace EPPlusImageRenderer.ShapeDefinitions -{ - [DebuggerDisplay("{Style}")] - public class ShapeDefinition - { - internal Dictionary _calculatedValues = new Dictionary(); - - internal ShapeDefinition() - { - - } - /// - /// Clone constructor - /// - /// The original to clone from - public ShapeDefinition(ShapeDefinition original) - { - Style=original.Style; - if (original.ShapeAdjustValues != null) - { - ShapeAdjustValues = new List(); - foreach (var av in original.ShapeAdjustValues) - { - ShapeAdjustValues.Add(av.Clone()); - } - } - if (original.ShapeGuides != null) - { - ShapeGuides = new List(); - foreach (var g in original.ShapeGuides) - { - ShapeGuides.Add(g.Clone()); - } - } - - if (original.ShapeAdjustHandles!=null) - { - ShapeAdjustHandles = new List(); - foreach (var ah in original.ShapeAdjustHandles) - { - ShapeAdjustHandles.Add(ah.Clone()); - } - } - - TextBoxRect = original.TextBoxRect?.Clone(); - - ShapePaths = new List(); - foreach (var p in original.ShapePaths) - { - ShapePaths.Add(p.Clone()); - } - } - - - public eShapeStyle Style { get; set; } - /// - /// avLst - /// - public List ShapeAdjustValues { get; set; } - /// - /// gdLst - /// - public List ShapeGuides { get; set; } - /// - /// ahLst - /// - public List ShapeAdjustHandles { get; set; } - //cxnLst - public List ShapeConnectionSite { get; set; } - //rect - /// - /// The rectangle for the text inside the shape. - /// - public TextBoxRect TextBoxRect { get; set; } - //pathLst - /// - /// Paths to draw the shape - /// - public List ShapePaths { get; set; } - - public void Calculate(ExcelShape shape) - { - InitCalculatedValues(shape); - - if (ShapeAdjustValues != null) - { - if (shape.HasCustomAdjustmentPoints()) - { - var names = shape.GetAdjustmentPointsNames(); - var l = shape.GetAdjustmentPointsList(); - for(int i=0;i txtContainers = new List(); - - ////TODO: This needs to actually iterate through the individual paragraphs/txtRuns when differing fonts/font sizes exist - ////foreach(var paragraph in shape.TextBodyItem.Paragraphs) - ////{ - //// foreach(var txtRun in paragraph.TextRuns) - //// { - //// var newContainer = new TextContainer(txt, txtRun.GetMeasureFont(), true); - //// txtContainers.Add(newContainer); - //// } - ////} - //var newContainer = new TextContainer(txt, shape.TextBodyItem.Paragraphs.FirstDefaultRunProperties.GetMeasureFont(), true); - - //var cW = newContainer.Width; - //var cH = newContainer.Height; - - //var expectedWidth = (GetValue(TextBoxRect.RightName) - GetValue(TextBoxRect.LeftName)) / (double)ExcelDrawing.EMU_PER_PIXEL; - //var expectedHeight = (GetValue(TextBoxRect.BottomName) - GetValue(TextBoxRect.TopName)) / (double)ExcelDrawing.EMU_PER_PIXEL; - - //overrideWidthRatio = cW / expectedWidth; - //overrideHeightRatio = cH / expectedHeight; - - //TextBoxRect.RightValue = cW; - //TextBoxRect.BottomValue = cH; - - //TextBoxRect.LeftValue = GetValue(TextBoxRect.LeftName) / (double)ExcelDrawing.EMU_PER_PIXEL * overrideWidthRatio; - //TextBoxRect.TopValue = GetValue(TextBoxRect.TopName) / (double)ExcelDrawing.EMU_PER_PIXEL * overrideHeightRatio; - } - } - - if (ShapePaths.Count > 0) - { - foreach(var item in ShapePaths) - { - var shapeWidth = (double)shape._width * (double)ExcelDrawing.EMU_PER_PIXEL; - var shapeHeight = (double)shape._height * (double)ExcelDrawing.EMU_PER_PIXEL; - - var widthRatio = item.Width.HasValue ? (double)shapeWidth / (double)item.Width : 1D; - var heightRatio = item.Height.HasValue ? (double)shapeWidth / (double)item.Height : 1D; - - widthRatio *= overrideWidthRatio; - heightRatio *= overrideHeightRatio; - - item.Width = shapeWidth; - item.Height = shapeHeight; - - foreach (var p in item.Paths) - { - switch(p.Type) - { - case PathDrawingType.Close: - break; - case PathDrawingType.ArcTo: - var arc = (ArcTo)p; - if (string.IsNullOrEmpty(arc.WidthRadiusName) == false) - { - arc.WidthRadius = _calculatedValues[arc.WidthRadiusName] * overrideWidthRatio; - } - else - { - arc.WidthRadius = (double)((arc.WidthRadius ?? 0D) * widthRatio); - } - if (string.IsNullOrEmpty(arc.HeightRadiusName) == false) - { - arc.HeightRadius = _calculatedValues[arc.HeightRadiusName] * overrideHeightRatio; - } - else - { - arc.HeightRadius = (double)((arc.HeightRadius ?? 0D) * heightRatio); - } - if (string.IsNullOrEmpty(arc.StartAngleName) == false) arc.StartAngle = _calculatedValues[arc.StartAngleName]; - if (string.IsNullOrEmpty(arc.SwingAngleName) == false) arc.SwingAngle = _calculatedValues[arc.SwingAngleName]; - break; - default: - var pb = (PathWithCoordinates)p; - for(var i=0; i() - { - {"t", 0d }, - {"l", 0d }, - {"w", w }, - {"r", w }, - {"h", h }, - {"b", h }, - {"hc", w/2d }, - {"vc", h/2d }, - {"ls", ls }, - {"ss", ss }, - {"3cd4", 16200000.0d}, - {"3cd8", 8100000.0d}, - {"5cd8", 13500000.0d}, - {"7cd8", 18900000.0d}, - {"cd2", 10800000.0d}, - {"cd4", 5400000.0d}, - {"cd8", 2700000.0d}, - {"hd2", h/2d}, - {"hd3", h/3d}, - {"hd4", h/4d}, - {"hd5", h/5d}, - {"hd6", h/6d}, - {"hd8", h/8d}, - {"wd2", w/2d}, - {"wd3", w/3d}, - {"wd4", w/4d}, - {"wd5", w/5d}, - {"wd6", w/6d}, - {"wd8", w/8d}, - {"wd10", w/10d}, - {"wd12", w/12d}, - {"wd16", w/16d}, - {"wd32", w/32d}, - {"ssd2", ss/2d }, - {"ssd4", ss/4d }, - {"ssd6", ss/6d }, - {"ssd8", ss/8d }, - {"ssd16", ss/16d }, - {"ssd32", ss/32d }, - }; - } - - internal double CalculateFormula(string formula) - { - var tokens = formula.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - var t = tokens[0]; - switch (t) - { - case "val": - var val = GetValue(tokens[1]); - return GetValue(tokens[1]); - case "*/": - var divBy = GetValue(tokens[3]); - if (divBy == 0) return 0; - var val1 = GetValue(tokens[1]); - var val2 = GetValue(tokens[2]); - - return (val1 * val2) / (double)divBy; - case "+-": - return (GetValue(tokens[1]) + GetValue(tokens[2])) - GetValue(tokens[3]); - case "+/": - divBy = GetValue(tokens[3]); - if (divBy == 0) return 0; - return (GetValue(tokens[1]) + GetValue(tokens[2])) / (double)divBy; - case "?:": - return GetValue(tokens[1]) > 0 ? GetValue(tokens[2]) : GetValue(tokens[3]); - case "sqrt": - return Math.Sqrt(Math.Abs(GetValue(tokens[1]))); - case "abs": - return Math.Abs(GetValue(tokens[1])); - case "min": - return Math.Min(GetValue(tokens[1]), GetValue(tokens[2])); - case "max": - return Math.Max(GetValue(tokens[1]), GetValue(tokens[2])); - case "mod": - return Math.Sqrt(Math.Pow((double)GetValue(tokens[1]), 2d) + Math.Pow((double)GetValue(tokens[2]), 2) + Math.Pow((double)GetValue(tokens[3]), 2)); - case "pin": - //if (y < x), then x = value of this guide else if (y > z), then z - double x = GetValue(tokens[1]); - double y = GetValue(tokens[2]); - double z = GetValue(tokens[3]); - - return (y < x ? x : y > z ? z : y); - case "sin": - var angleSin = GetValue(tokens[2]) / 60000D; - if(angleSin == 0d || angleSin == 180d) - { - return 0; - } - var radAngleSin = Math.Sin(MathHelper.Radians(angleSin)); - return (GetValue(tokens[1]) * radAngleSin); - case "cos": - var angleCos = GetValue(tokens[2]) / 60000D; - - if (angleCos == 90d || angleCos == 270d) - { - return 0; - } - var radAngleCos = Math.Cos(MathHelper.Radians(angleCos)); - return (GetValue(tokens[1]) * radAngleCos); - case "tan": - var angleTan = GetValue(tokens[2]) / 60000D; - - if (angleTan == 0d || angleTan == 90d) - { - //Tan technically undefined - return 0; - } - - return (GetValue(tokens[1]) * Math.Tan(MathHelper.Radians(angleTan))); - case "at2": - x = GetValue(tokens[1]); - y = GetValue(tokens[2]); - double angleRad = Math.Atan2(y, x); // radians - double angleDeg = angleRad * (180d / Math.PI); // degrees - - while (angleDeg < -360) - { - angleDeg += 360; // normalize to [0, 360) - } - while (angleDeg > 360) - { - angleDeg -= 360; - } - - return (angleDeg * 60000D); - case "cat2": - x = GetValue(tokens[1]); - y = GetValue(tokens[2]); - z = GetValue(tokens[3]); - - double angleRadCatOrig = Math.Atan2(z, y); // radians - double angleDegCat2Orig = angleRadCatOrig * (180.0 / Math.PI); // degrees - - if (angleDegCat2Orig == 90d || angleDegCat2Orig == 270d) - { - return 0; - } - - var dist = (x * Math.Cos(angleRadCatOrig)); - return dist; - case "sat2": - x = GetValue(tokens[1]); - y = GetValue(tokens[2]); - z = GetValue(tokens[3]); - - double angleRadSatOrig = Math.Atan2(z, y); // radians - double angleDegSat2Orig = angleRadSatOrig * (180.0 / Math.PI); // degrees - - if (angleDegSat2Orig == 0d || angleDegSat2Orig == 180d) - { - return 0; - } - - var sat2Rad = (x * Math.Sin(angleRadSatOrig)); - - return sat2Rad; - default: - if (_calculatedValues.TryGetValue(t, out var v)) - { - return v; - } - throw new InvalidOperationException($"Unknown function or variable {{{t}}}"); - } - - } - private double GetValue(string v) - { - if(double.TryParse(v, out var l)) - { - return l; - } - else - { - if(_calculatedValues.TryGetValue(v, out var cv)) - { - return cv; - } - throw new InvalidOperationException($"Unknown variable {{{v}}}"); - } - } - - internal ShapeDefinition Clone() - { - return new ShapeDefinition(this); - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeGuide.cs b/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeGuide.cs deleted file mode 100644 index dc63b5ee0a..0000000000 --- a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapeGuide.cs +++ /dev/null @@ -1,37 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using System.Diagnostics; - -namespace EPPlusImageRenderer.ShapeDefinitions -{ - /// - /// 20.1.9.11 Ecma part 1 - /// 20.1.10.76 - Preset Textbox Shape Types - /// - [DebuggerDisplay("{Name}-{Formula}={CalculatedValue}")] - public class ShapeGuide - { - public string Name { get; set; } - public string Formula { get; set; } - public double CalculatedValue - { - get; - set; - } - - internal ShapeGuide Clone() - { - return new ShapeGuide() { Name=Name, Formula=Formula, CalculatedValue=CalculatedValue}; - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapePositionCoordinate.cs b/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapePositionCoordinate.cs deleted file mode 100644 index 40f39dba5a..0000000000 --- a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/ShapePositionCoordinate.cs +++ /dev/null @@ -1,20 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -namespace EPPlusImageRenderer.ShapeDefinitions -{ - public class ShapePositionCoordinate - { - public object X { get; set; } - public object Y { get; set; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/TextBoxRect.cs b/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/TextBoxRect.cs deleted file mode 100644 index e2341a600d..0000000000 --- a/src/EPPlus.Export.ImageRenderer/ShapeDefinitions/TextBoxRect.cs +++ /dev/null @@ -1,38 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -namespace EPPlusImageRenderer.ShapeDefinitions -{ - public class TextBoxRect - { - public string LeftName { get; set; } - public string RightName { get; set; } - public string TopName { get; set; } - public string BottomName { get; set; } - - public double LeftValue { get; set; } - public double RightValue { get; set; } - public double TopValue { get; set; } - public double BottomValue { get; set; } - - internal TextBoxRect Clone() - { - return new TextBoxRect() - { - LeftName = LeftName, - RightName = RightName, - TopName = TopName, - BottomName = BottomName - }; - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Style/IFillBasic.cs b/src/EPPlus.Export.ImageRenderer/Style/IFillBasic.cs deleted file mode 100644 index b80bb8fef9..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Style/IFillBasic.cs +++ /dev/null @@ -1,21 +0,0 @@ -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Style -{ - public interface IFillBasic - { - /// - /// Fill type of object - /// - eFillStyle Style { get; } - - string GetBackgroundColor(ExcelTheme theme); - string GetGradientColor1(ExcelTheme theme); - string GetGradientColor2(ExcelTheme theme); - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Style/IStyleExportDrawing.cs b/src/EPPlus.Export.ImageRenderer/Style/IStyleExportDrawing.cs deleted file mode 100644 index 8407997c2d..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Style/IStyleExportDrawing.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Style -{ - internal interface IStyleExportDrawing - { - string StyleKey { get; } - - bool HasStyle { get; } - - IFillBasic Fill { get; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Style/SvgChartStyleTranslator.cs b/src/EPPlus.Export.ImageRenderer/Style/SvgChartStyleTranslator.cs deleted file mode 100644 index 34c760aa80..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Style/SvgChartStyleTranslator.cs +++ /dev/null @@ -1,21 +0,0 @@ -using OfficeOpenXml.Drawing.Chart.Style; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Style -{ - internal class SvgChartStyleTranslator : IStyleExportDrawing - { - public SvgChartStyleTranslator(ExcelChartStyleEntry chartStyle) - { - var fill = chartStyle.Fill; - } - public string StyleKey => throw new NotImplementedException(); - - public bool HasStyle => throw new NotImplementedException(); - - public IFillBasic Fill => throw new NotImplementedException(); - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Style/SvgFill.cs b/src/EPPlus.Export.ImageRenderer/Style/SvgFill.cs deleted file mode 100644 index 400566b210..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Style/SvgFill.cs +++ /dev/null @@ -1,116 +0,0 @@ -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using OfficeOpenXml.Style.XmlAccess; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Globalization; -using System.Linq; -using System.Text; -using OfficeOpenXml.Style; -using OfficeOpenXml.Utils.EnumUtils; - -namespace EPPlus.Export.ImageRenderer.Style -{ - internal class SvgFill : IFillBasic - { - ExcelDrawingFillBasic _fill; - - public SvgFill(ExcelDrawingFillBasic fill) - { - Style = fill.Style; - _fill = fill; - } - - public SvgFill(ExcelDrawingFill fill) - { - Style = fill.Style; - _fill = fill; - } - - public eFillStyle Style { get; set; } - - public bool HasValue - { - get - { - return !_fill.IsEmpty; - } - } - - - public bool IsGradient - { - get - { - return Style == eFillStyle.GradientFill; - } - } - - public string GetBackgroundColor(ExcelTheme theme) - { - return GetColor(_fill.Color, theme); - } - - /// - /// Gets hexcode color for html as a string - /// - /// - /// - internal static string GetColor(Color c, ExcelTheme theme) - { - //Color ret; - //if (!string.IsNullOrEmpty(c.ToColorString())) - //{ - // if (int.TryParse(c.Rgb, NumberStyles.HexNumber, null, out int hex)) - // { - // ret = Color.FromArgb(hex); - // } - // else - // { - // ret = Color.Empty; - // } - //} - //else if (c.Theme.HasValue) - //{ - // ret = Utils.TypeConversion.ColorConverter.GetThemeColor(theme, c.Theme.Value); - //} - //else if (c.Indexed >= 0) - //{ - // ret = theme._wb.Styles.GetIndexedColor(c.Indexed); - //} - //else - //{ - // //Automatic, set to black. - // if (c.Auto) - // { - // ret = Color.Black; - // } - // else if (c.Exists) - // { - // ret = Color.Empty; - // } - // else - // { - // return null; - // } - //} - //if (c.Tint != 0) - //{ - // ret = Utils.TypeConversion.ColorConverter.ApplyTint(ret, Convert.ToDouble(c.Tint)); - //} - - return "#" + c.ToArgb().ToString("x8").Substring(2); - } - - public string GetGradientColor1(ExcelTheme theme) - { - return GetColor(_fill.GradientFill.Colors.ToArray()[0].Color.GetColor(), theme); - } - public string GetGradientColor2(ExcelTheme theme) - { - return GetColor(_fill.GradientFill.Colors.ToArray()[1].Color.GetColor(), theme); - } - - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Style/SvgFillTranslator.cs b/src/EPPlus.Export.ImageRenderer/Style/SvgFillTranslator.cs deleted file mode 100644 index 634146cd33..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Style/SvgFillTranslator.cs +++ /dev/null @@ -1,90 +0,0 @@ -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using OfficeOpenXml.Export.HtmlExport; -using OfficeOpenXml.Export.HtmlExport.CssCollections; -using OfficeOpenXml.Export.HtmlExport.Translators; -using OfficeOpenXml.Style; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -namespace EPPlus.Export.ImageRenderer.Style -{ - internal class SvgFillTranslator : TranslatorBase - { - ExcelTheme _theme; - IFillBasic _fill; - - internal SvgFillTranslator(IFillBasic fill) - { - _fill = fill; - } - - internal override List GenerateDeclarationList(TranslatorContext context) - { - _theme = context.Theme; - - if (context.Exclude.Fill) return null; - - if (_fill.Style == eFillStyle.GradientFill) - { - AddGradient(); - } - else - { - if (_fill.Style == eFillStyle.PatternFill) - { - var bc = _fill.GetBackgroundColor(_theme) ?? "#0"; - if (string.IsNullOrEmpty(bc) == false) - { - AddDeclaration("fill", bc); - } - } - else if (_fill.Style == eFillStyle.NoFill) - { - var fc = _fill.GetBackgroundColor(_theme); - if (string.IsNullOrEmpty(fc) == false) - { - AddDeclaration("fill", fc); - } - } - else if(_fill.Style == eFillStyle.BlipFill) - { - string bgColor = _fill.GetBackgroundColor(_theme) ?? "#0"; - //string patternColor = _fill.GetPatternColor(_theme) ?? "#0"; - - var svg = PatternFills.GetPatternSvgConvertedOnly(ExcelFillStyle.Solid, bgColor, ""); - AddDeclaration("background-repeat", "repeat"); - //arguably some of the values should be its own declaration...Should still work though. - AddDeclaration("background", $"url(data:image/svg+xml;base64,{svg})"); - } - } - - return declarations; - } - - private void AddGradient() - { - //AddDeclaration("linearGradient"); - - //GetLinearGradientNodeStop - //AddDeclaration("stop"); - //var gradientDeclaration = declarations.LastOrDefault(); - - //if (_fill.Style == eFillStyle.GradientFill) - //{ - // AddDeclaration("offset",) - //} - //else - //{ - // gradientDeclaration.AddValues($"radial-gradient(ellipse {_fill.Right * 100}% {_fill.Bottom * 100}%"); - //} - - //gradientDeclaration.AddValues - // ( - // $",{_fill.GetGradientColor1(_theme)} 0%", - // $",{_fill.GetGradientColor2(_theme)} 100%)" - // ); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/AxisOptions.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/AxisOptions.cs deleted file mode 100644 index 8ac618a85c..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/AxisOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml.Drawing.Chart; -using System.Drawing; - -internal class AxisOptions -{ - public double? LockedMin { get; set; } - public double? LockedMax { get; set; } - public double? LockedInterval { get; set; } - public eTimeUnit? LockedIntervalUnit { get; set; } - public bool AddPadding { get; set; } = false; - public ExcelChartAxisStandard Axis { get; set; } - public bool IsStacked100 { get; set; } - public RenderItem ChartSize { get; set; } - public string NumberFormat { get; set; } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/AxisScale.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/AxisScale.cs deleted file mode 100644 index 6acf61b5b5..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/AxisScale.cs +++ /dev/null @@ -1,16 +0,0 @@ -using EPPlus.Export.ImageRenderer; -using OfficeOpenXml.Drawing.Chart; -using System.Collections.Generic; - -internal class AxisScale -{ - public double Min { get; set; } - public double Max { get; set; } - public double MajorInterval { get; set; } - public double MinorInterval { get; set; } - public int TickCount { get; set; } - public eTimeUnit? MajorDateUnit { get; set; } - public eTimeUnit? MinorDateUnit { get; set; } - public eTextOrientation TextOrientation { get; set; } - public List DisplayValues { get; set; } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/CategoryAxisScaleCalculator.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/CategoryAxisScaleCalculator.cs deleted file mode 100644 index 7f6008e31c..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/CategoryAxisScaleCalculator.cs +++ /dev/null @@ -1,186 +0,0 @@ -using EPPlusImageRenderer.Svg; -using OfficeOpenXml; -using OfficeOpenXml.DataValidation; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Information; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using OfficeOpenXml.Interfaces.Drawing.Text; -using OfficeOpenXml.Utils.DateUtils; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; - -namespace EPPlus.Export.ImageRenderer.Svg.Chart.Util -{ - internal class CategoryAxisScaleCalculator - { - - internal static AxisScale CalculateHorizontalAxisByWidth(ref List values, ITextMeasurer tm, AxisOptions options) - { - var ax = options.Axis; - var plotAreaWidth = options.ChartSize.Bounds.Width; - var mf = ax.Font.GetMeasureFont(); - List displayValues = GetUniqueValues(values).Select(x=>(object)x.ToString()).ToList(); - var uniqeItems = displayValues.Count; - var res = tm.MeasureText(displayValues[0].ToString(), mf); - - //Get interval for maximum width with vertical text. - var interval = GetMinUnitVerticalText(displayValues.Count, res.Height, plotAreaWidth); - - - //Get max text width when using diagonal text - var width = mf.Size * Math.Sqrt(2); - var margin = mf.Size * 0.5; - - if (FitAsVerticalDiagonalText(displayValues.Count, interval, width, margin, plotAreaWidth)) //Check diagonal - { - if(FitAsHorizontalText(displayValues, interval, mf, tm, plotAreaWidth)) //Check horizontal - { - return new AxisScale() - { - MajorInterval = interval, - MinorInterval = 1, - Min = 1, - Max = displayValues.Count, - TextOrientation = eTextOrientation.Horizontal, - DisplayValues = displayValues - }; - } - else - { - return new AxisScale() - { - MajorInterval = interval, - MinorInterval = 1, - Min = 1, - Max = displayValues.Count, - TextOrientation = eTextOrientation.Diagonal, - DisplayValues = displayValues - }; - } - } - if(interval != 1) - { - var removeCount = interval - 1; - var c = (int)Math.Truncate(values.Count / (double)interval); - for(int i=0;i<=c;i++) - { - for(int j=0;j values, ITextMeasurer tm, AxisOptions options) - { - var ax = options.Axis; - var plotAreaHeight = options.ChartSize.Bounds.Height; - var mf = ax.Font.GetMeasureFont(); - List displayValues = GetUniqueValues(values).Select(x => (object)x.ToString()).ToList(); - var uniqeItems = displayValues.Count; - var res = tm.MeasureText(displayValues[0].ToString(), mf); - int interval = 1; - while (FitAsVerticalDiagonalText(displayValues.Count, interval, res.Height, 1.2D, plotAreaHeight)==false) //Check horizontal - { - interval++; - } - if (interval != 1) - { - var removeCount = interval-1; - var c = (int)Math.Truncate(displayValues.Count / (double)interval); - for (int i = 0; i <= c; i++) - { - for (int j = 0; j < removeCount; j++) - { - if (i + 1 < displayValues.Count) - { - displayValues.RemoveAt(i + 1); - } - } - } - } - - return new AxisScale() - { - MajorInterval = interval, - MinorInterval = 1, - Min = 1, - Max = uniqeItems, - TextOrientation = eTextOrientation.Horizontal, - DisplayValues = displayValues - }; - } - internal static List GetUniqueValues(List values) - { - var ret = new List(); - var hs = new HashSet(); - foreach(var v in values) - { - if(v is string[]) - { - var s = (string[])v; - var key = s[0] + s[1]; - if(hs.Add(key)) - { - ret.Add(s[0]); - } - } - else - { - ret.Add(v); - } - } - return ret; - } - - private static bool FitAsHorizontalText(List displayValues, int interval, MeasurementFont mf, ITextMeasurer tm, double plotAreaWidth) - { - var margin = mf.Size * 0.3; - var width = tm.MeasureText(displayValues[0].ToString(), mf).Width + margin; - var pos = interval; - while (pos < displayValues.Count && width < plotAreaWidth) - { - width = tm.MeasureText(displayValues[pos].ToString(), mf).Width + margin; - if(width > plotAreaWidth) return false; - pos += interval; - } - return width <= plotAreaWidth; - } - - private static bool FitAsVerticalDiagonalText(int itemCount, int interval, double textWidth, double margin, double plotAreaWidthHeight) - { - var items = Math.Truncate(itemCount * 1D / interval); - return items * textWidth + (items - 1) * margin < plotAreaWidthHeight; - } - - private static int GetMinUnitVerticalText(int itemCount, double textHeight, double plotAreaWidth) - { - var interval = 1; - var margin = 0D; - var items = Math.Truncate((double)itemCount / interval); - while (items * textHeight + (items - 1) * margin >= plotAreaWidth) - { - interval++; - items = Math.Truncate((double)itemCount / interval); - } - - return interval; - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/DateAxisScaleCalculator.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/DateAxisScaleCalculator.cs deleted file mode 100644 index 5e2b871f45..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/DateAxisScaleCalculator.cs +++ /dev/null @@ -1,561 +0,0 @@ -using OfficeOpenXml; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Information; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using OfficeOpenXml.Interfaces.Drawing.Text; -using OfficeOpenXml.Utils.DateUtils; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; - -namespace EPPlus.Export.ImageRenderer.Svg.Chart.Util -{ - internal class DateAxisScaleCalculator - { - - - /// - /// Calculate automatic axis settings for date data - /// - internal static AxisScale Calculate(double dataMin, double dataMax, AxisOptions axisOptions = null) - { - - ComputeAutoAxis(DateTime.FromOADate(dataMin), DateTime.FromOADate(dataMax), axisOptions.AddPadding); - var range = dataMax - dataMin; - - // Calculate major majorUnit based on range - CalculateMajorUnit(axisOptions.LockedMin ?? dataMin, axisOptions.LockedMax ?? dataMax, axisOptions.AddPadding, out double majorValue, out double axisMin, out double axisMax, out double minorUnit, out eTimeUnit majorUnit); - if(axisOptions.LockedInterval.HasValue) - { - majorValue = axisOptions.LockedInterval.Value; - } - - return new AxisScale - { - Min = axisMin, - Max = axisMax, - MajorInterval = majorValue, - MajorDateUnit = majorUnit, - MinorDateUnit = majorUnit - }; - } - // Excel's date serial epoch: Dec 30, 1899 - private static readonly DateTime ExcelEpoch = new DateTime(1899, 12, 30); - - // Excel legacy leap year bug: dates after Feb 28, 1900 are offset by 1 - private static readonly DateTime ExcelLeapBugThreshold = new DateTime(1900, 3, 1); - - // Excel's "nice" interval ladder in days - private static readonly int[] NiceIntervals = { 1, 2, 5, 7, 14, 30, 91, 182, 365, 730 }; - private const int TargetTicks = 11; // Excel typically targets 10–12 ticks - public class AxisResult - { - public DateTime AxisMin { get; set; } - public DateTime AxisMax { get; set; } - public int IntervalDays { get; set; } - public double AxisMinSerial { get; set; } - public double AxisMaxSerial { get; set; } - public int TickCount => (int)Math.Round((AxisMaxSerial - AxisMinSerial) / IntervalDays) + 1; - - public override string ToString() => - $"Axis Min : {AxisMin:yyyy-MM-dd} (serial {AxisMinSerial})\n" + - $"Axis Max : {AxisMax:yyyy-MM-dd} (serial {AxisMaxSerial})\n" + - $"Interval : {IntervalDays} day(s)\n" + - $"Ticks : {TickCount}"; - } - /// - /// Converts a DateTime to an Excel serial number (days since Dec 30, 1899), - /// accounting for Excel's 1900 leap year bug. - /// - public static double ToExcelSerial(DateTime date) - { - double serial = (date - ExcelEpoch).TotalDays; - if (date >= ExcelLeapBugThreshold) - serial += 1; // Excel's legacy off-by-one - return serial; - } - - /// - /// Converts an Excel serial number back to a DateTime. - /// - public static DateTime FromExcelSerial(double serial) - { - if (serial >= 61) // 61 = the phantom Feb 29, 1900 in Excel - serial -= 1; - return ExcelEpoch.AddDays(serial); - } - /// - /// Computes Excel-compatible auto axis min, max, and major unit for a date axis. - /// If the OOXML already contains explicit min/max values, use those directly instead. - /// - public static AxisResult ComputeAutoAxis(DateTime dataMin, DateTime dataMax, bool snapInterval) - { - double serialMin = ToExcelSerial(dataMin); - double serialMax = ToExcelSerial(dataMax); - - // Step 1: pick a nice major unit - double rawInterval = (serialMax - serialMin) / 10.0; - int interval = NiceIntervals.FirstOrDefault(v => v >= rawInterval); - if (interval == 0) - interval = 730; // fallback for very large ranges - - double axisMin = serialMin; - double axisMax = serialMax; - if (snapInterval) - { - // Step 2: snap outward to multiples of the interval - axisMin = Math.Floor(serialMin / interval) * interval; - axisMax = Math.Ceiling(serialMax / interval) * interval; - } - else - { - axisMin = serialMin; - axisMax = serialMax; - } - // Step 3: expand until we have enough ticks, then re-snap - while ((axisMax - axisMin) / interval < TargetTicks - 1) - { - axisMin -= interval; - axisMax += interval; - } - - axisMin = Math.Floor(axisMin / interval) * interval; - axisMax = Math.Ceiling(axisMax / interval) * interval; - - return new AxisResult - { - AxisMin = FromExcelSerial(axisMin), - AxisMax = FromExcelSerial(axisMax), - IntervalDays = interval, - AxisMinSerial = axisMin, - AxisMaxSerial = axisMax, - }; - } - /// - /// Calculate major majorUnit based on data range (in days) - /// - private static void CalculateMajorUnit(double min, double max, bool snapToInterval, out double majorValue, out double axisMin, out double axisMax, out double minorUnits, out eTimeUnit majorUnit) - { - var dtMin = DateTime.FromOADate(ExcelNormalizeOADate(min)); - var dtMax = DateTime.FromOADate(ExcelNormalizeOADate(max)); - - var rangeDays = (dtMax - dtMin).TotalDays; - // Target approximately 10 intervals - const int targetIntervals = 9; - var roughUnit = rangeDays / targetIntervals; - double interval; - // Return nice round numbers based on the rough majorUnit - if (roughUnit < 1) - { - interval = majorValue = 1; - majorUnit = eTimeUnit.Days; - } - else if (roughUnit < 2) - { - interval = majorValue = 2; - majorUnit = eTimeUnit.Days; - } - else if (roughUnit < 5) - { - // Multiple days - round to nearest day - interval = majorValue = 5; - majorUnit = eTimeUnit.Days; - } - else if (roughUnit < 7) - { - // Multiple days - round to nearest day - interval = majorValue = 7; - majorUnit = eTimeUnit.Days; - } - else if (roughUnit < 14) - { - interval = majorValue = 14; - majorUnit = eTimeUnit.Days; - } - else if (roughUnit < 21) - { - interval = majorValue = 21; - majorUnit = eTimeUnit.Days; - } - else if (roughUnit < 60) - { - majorValue = 1; - interval = 30; - majorUnit = eTimeUnit.Months; - } - else if (roughUnit < 120) - { - majorValue = 2; - interval = 60; - majorUnit = eTimeUnit.Months; - } - else if (roughUnit < 180) - { - majorValue = 3; - interval = 90; - majorUnit = eTimeUnit.Months; - } - else if (roughUnit < 365) - { - majorValue = 6; - interval = 180; - majorUnit = eTimeUnit.Months; - } - else if (roughUnit < 730) - { - majorValue = 1; - interval = 365; - majorUnit = eTimeUnit.Years; - } - else if (roughUnit < 1825) - { - // Multiple years - majorValue = Math.Ceiling(roughUnit / 365); - interval = 365 * majorValue; - majorUnit = eTimeUnit.Years; - } - else - { - // 5-year increments for very long ranges - majorValue = Math.Ceiling(roughUnit / 365 / 5) * 5; - interval = 365 * majorValue; - majorUnit = eTimeUnit.Years; - } - - if (snapToInterval) - { - axisMin = Math.Floor(min / interval) * interval; - axisMax = Math.Ceiling(max / interval) * interval; - - if (axisMax == max) - { - axisMax += interval; - } - // Step 3: expand until we have enough ticks, then re-snap - while ((axisMax - axisMin) / interval < TargetTicks - 1 && ((min - axisMin) < interval * 2)) - { - axisMin -= majorValue; - } - } - else - { - axisMin= min; - axisMax = max; - } - - minorUnits = 1; - } - - private static double ExcelNormalizeOADate(double value) - { - return Math.Min(Math.Max(value, 0), 2958465.99999999); - } - - private static DateTime FloorToUnit(double value, eTimeUnit unit, int interval) - { - var date = DateTime.FromOADate(value); - switch (unit) - { - case eTimeUnit.Days: - if(interval % 7 != 0) - { - return date.Date.AddDays(-(((int)date.DayOfWeek + 6) % 7)); - } - else - { - return date; - } - //return date.Date.AddDays(-(date.DayOfYear % interval)); - case eTimeUnit.Months: - return new DateTime(date.Year, date.Month, 1) - .AddMonths(-((date.Month - 1) % interval)); - - case eTimeUnit.Years: - return new DateTime(date.Year - (date.Year % interval), 1, 1); - default: - return date; - }; - } - - private static DateTime CeilingToUnit(double value, eTimeUnit unit, int interval) - { - DateTime floor = FloorToUnit(value, unit, interval); - - var date = DateTime.FromOADate(value); - if (floor == date) return date; // already on boundary - - switch (unit) - { - case eTimeUnit.Days: - return floor.AddDays(interval); - case eTimeUnit.Months: - return floor.AddMonths(interval); - case eTimeUnit.Years: - return floor.AddYears(interval); - default: - return date; - }; - } - - internal static AxisScale CalculateByWidthAllowDiagonal(double min, double max, ITextMeasurer tm, AxisOptions options) - { - var ax = options.Axis; - var plotAreaWidth = options.ChartSize.Bounds.Width; - var mf = ax.Font.GetMeasureFont(); - int interval; - eTimeUnit unit; - var minString = DateTime.FromOADate(min).ToString(options.NumberFormat); - var res = tm.MeasureText(minString, mf); - if (options.LockedInterval.HasValue) - { - interval = (int)options.LockedInterval.Value; - unit = options.LockedIntervalUnit ?? eTimeUnit.Days; - } - else - { - interval = 1; - unit = eTimeUnit.Days; - //Get interval for maximum width with vertical text. - while (FitAsVerticalDiagonalText(min, max, interval, unit, res.Height, res.Height * 0.3, plotAreaWidth) == false) - { - AddIntervall(ref interval, ref unit); - } - //Get max text width when using diagonal text - var width = mf.Size * Math.Sqrt(2); - var margin = mf.Size * 0.5; - - if (FitAsVerticalDiagonalText(min, max, interval, unit, width, margin, plotAreaWidth)) //Check diagonal - { - if (FitAsHorizontalText(tm, options, min, max, interval, unit, res.Height, plotAreaWidth)) //Check horizontal - { - return new AxisScale() - { - MajorInterval = interval, - MinorInterval = 1, - MinorDateUnit = unit, - MajorDateUnit = unit, - Min = min, - Max = max, - TextOrientation = eTextOrientation.Horizontal, - }; - } - else - { - return new AxisScale() - { - MajorInterval = interval, - MinorInterval = 1, - MinorDateUnit = unit, - MajorDateUnit = unit, - Min = min, - Max = max, - TextOrientation = eTextOrientation.Diagonal, - }; - } - } - - } - - return new AxisScale() - { - MajorInterval = interval, - MinorInterval = 1, - MinorDateUnit = unit, - MajorDateUnit = unit, - Min = min, - Max = max, - TextOrientation = ax.TextBody.VerticalText==OfficeOpenXml.Drawing.eTextVerticalType.Horizontal ? eTextOrientation.Horizontal : eTextOrientation.Vertical, - }; - } - - //private static bool FitAsDiagonalText(double min, double max, int interval, eTimeUnit unit, float size, double plotAreaWidth) - //{ - // var margin = size * 0.5; - // var width = size * Math.Sqrt(2) + margin; - - // if (unit == eTimeUnit.Days) - // { - // return ((max - min) / interval) * width < plotAreaWidth; - // } - // else if(unit == eTimeUnit.Months) - // { - // w - // } - //} - - private static void AddIntervall(ref int interval, ref eTimeUnit unit) - { - if (unit == eTimeUnit.Days) - { - switch (interval) - { - case 1: - interval = 2; - break; - case 2: - interval = 7; //Week - break; - case 7: - interval = 1; //Month - unit = eTimeUnit.Months; - break; - default: - interval *= 10; - break; - } - } - else if (unit == eTimeUnit.Months) - { - unit = eTimeUnit.Years; - } - else if (unit == eTimeUnit.Years) - { - interval++; - } - } - - private static bool FitAsHorizontalText(ITextMeasurer tm, AxisOptions options, double min, double max, int interval, eTimeUnit unit, float height, double width) - { - var minMargin = 2; //2 Points - var minDate = DateTime.FromOADate(min); - var maxDate = DateTime.FromOADate(max); - var date = minDate; - var horizontalWidth = 0D; - var nf = options.NumberFormat; - var mf = options.Axis.Font.GetMeasureFont(); - var angMult = Math.Sin(MathHelper.Radians(45)); - while (date < maxDate) - { - var textWidth = tm.MeasureText(date.ToString(), mf).Width; - horizontalWidth += textWidth + minMargin; - if(horizontalWidth > width) - { - return false; - } - if (unit == eTimeUnit.Days) - { - date = date.AddDays(interval); - } - else if(unit == eTimeUnit.Months) - { - date = minDate.AddDays(1).AddMonths(interval).AddDays(-1); //Extra adds to keep last day of month - } - else - { - date = minDate.AddDays(1); - } - } - return true; - } - - private static bool FitAsVerticalDiagonalText(double min, double max, int interval,eTimeUnit unit, double textWidth, double margin, double plotAreaWidth) - { - if (unit == eTimeUnit.Days) - { - var items = ((max - min) / interval); - return items * textWidth + (items-1) * margin < plotAreaWidth; - } - else if(unit== eTimeUnit.Months) - { - var minDate = DateTime.FromOADate(min); - var maxDate = DateTime.FromOADate(max); - var date = minDate.AddDays(1).AddMonths(interval).AddDays(-1); //Handles last day of month. - var items = 1; - while(date <= maxDate) - { - items++; - if (items * textWidth + (items - 1) * margin > plotAreaWidth) return false; - date = date.AddDays(1).AddMonths(interval).AddDays(-1); - } - return items * textWidth + (items - 1) * margin < plotAreaWidth; - } - else - { - var minDate = DateTime.FromOADate(min); - var maxDate = DateTime.FromOADate(max); - var date = minDate.AddDays(1).AddYears(interval).AddDays(-1); //Handles leap year - var items = 1; - while (date <= maxDate) - { - items++; - if (items * textWidth + (items - 1) * margin > plotAreaWidth) return false; - date = date.AddDays(1).AddYears(interval).AddDays(-1); - } - return items * textWidth + (items - 1) * margin < plotAreaWidth; - - } - } - - internal static AxisScale CalculateByWidthHeight(double widthOrHeight, double min, double max, ITextMeasurer tm, AxisOptions options) - { - var ax = options.Axis; - var mf = ax.Font.GetMeasureFont(); - int interval; - eTimeUnit unit; - var minString = DateTime.FromOADate(min).ToString(options.NumberFormat); - var res = tm.MeasureText(minString, mf); - if (options.LockedInterval.HasValue) - { - interval = (int)options.LockedInterval.Value; - unit = options.LockedIntervalUnit ?? eTimeUnit.Days; - } - else - { - interval = 1; - unit = eTimeUnit.Days; - var range = max - min; - - double axis_min, axis_max; - if (options.AddPadding) - { - axis_min = options.LockedMin ?? Math.Floor((min - 0.5 * range) / interval) * interval; - axis_max = options.LockedMax ?? Math.Ceiling((max + 0.2 * range) / interval) * interval; - } - else - { - axis_min = min; - axis_max = max; - } - //Get interval for maximum width with vertical text. - while (FitAsVerticalDiagonalText(axis_min, axis_max, interval, unit, res.Height, res.Height * 0.3, widthOrHeight) == false) - { - AddIntervall(ref interval, ref unit); - double days; - switch (unit) - { - case eTimeUnit.Months: - days = 30 * interval; - break; - case eTimeUnit.Years: - days = 365 * interval; - break; - default: - days = interval; - break; - } - - if (options.AddPadding) - { - axis_min = options.LockedMin ?? Math.Floor((min - 0.5 * range) / days) * days; - axis_max = options.LockedMax ?? Math.Ceiling((max + 0.2 * range) / days) * days; - } - } - min = axis_min; - max = axis_max; - } - - return new AxisScale() - { - MajorInterval = interval, - MinorInterval = 1, - MinorDateUnit = unit, - MajorDateUnit = unit, - Min = min, - Max = max, - TextOrientation = eTextOrientation.Horizontal - }; - - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/ValueAxisScaleCalculator.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/ValueAxisScaleCalculator.cs deleted file mode 100644 index 5c7c68233a..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/Axis/ValueAxisScaleCalculator.cs +++ /dev/null @@ -1,241 +0,0 @@ -using Microsoft.VisualBasic; -using OfficeOpenXml.Drawing.Chart; -using System; -using System.Collections.Generic; - -internal class ValueAxisScaleCalculator -{ - internal static AxisScale Calculate(double dataMin, double dataMax, double chartHeightPixels, AxisOptions axisOptions = null) - { - return GetNumberScale(ref dataMin, ref dataMax, chartHeightPixels, ref axisOptions); - } - - private static AxisScale GetNumberScale(ref double dataMin, ref double dataMax, double chartHeightPixels, ref AxisOptions axisOptions) - { - var desiredTicks = chartHeightPixels < 200 ? 4 : - chartHeightPixels < 400 ? 5 : 6; - - if (axisOptions == null) - { - axisOptions = new AxisOptions(); - } - - // Handle equal data series. - if (dataMin == dataMax) - { - if (dataMin < 0) - { - dataMax = 0; - } - else - { - dataMin = 0; - } - } - - var isAllPositive = dataMin >= 0 && dataMax >= 0; - var isAllNegative = dataMin <= 0 && dataMax <= 0; - if(dataMin < 0 && dataMax > 0 && axisOptions.IsStacked100) - { - desiredTicks *= 2; - } - double interval; - if (axisOptions.LockedInterval.HasValue) - { - interval = axisOptions.LockedInterval.Value; - - // Calculate min and max based on locked interval - if (!axisOptions.LockedMin.HasValue) - { - if (axisOptions.AddPadding) - { - dataMin = Math.Floor(dataMin * 0.1 / interval) * interval; - if(axisOptions.IsStacked100 && dataMin < -1) - { - dataMin = -1; - } - if (isAllPositive && dataMin < 0) - { - dataMin = 0; - } - } - } - else - { - dataMin = axisOptions.LockedMin.Value; - } - - if (!axisOptions.LockedMax.HasValue) - { - dataMax = Math.Ceiling(dataMax * 0.1 / interval) * interval; - if (axisOptions.IsStacked100 && dataMin > 1) - { - dataMax = 1; - } - } - else - { - if (axisOptions.AddPadding) - { - dataMax = axisOptions.LockedMax.Value; - if (isAllNegative && dataMax > 0) - { - dataMax = 0; - } - } - } - - int tickCount = (int)Math.Round((dataMax - dataMin) / Math.Min(desiredTicks, interval)) + 1; - return new AxisScale - { - Min = dataMin, - Max = dataMax, - MajorInterval = interval, - TickCount = tickCount - }; - } - else - { - if (axisOptions.LockedMin.HasValue) - { - dataMin = axisOptions.LockedMin.Value; - } - if (axisOptions.LockedMax.HasValue) - { - dataMax = axisOptions.LockedMax.Value; - } - var dataDiff = dataMax - dataMin; - if (axisOptions.LockedMin.HasValue==false && isAllPositive && dataDiff > 0 && dataMin / dataDiff < 5) - { - dataMin = 0; - } - - double dataRange = dataMax - dataMin; - - double axisMin; - double axisMax; - double roughInterval; - double scaleInterval; - - if (axisOptions.AddPadding) - { - // Add padding (10%) - if (axisOptions.LockedMin.HasValue) - { - axisMin = axisOptions.LockedMin.Value; - } - else - { - axisMin = dataMin - (dataRange * 0.05); - if (axisOptions.IsStacked100) - { - if (axisMin < -1) - { - axisMin = -1; - } - else if(axisMin>0) - { - axisMin = 0; - } - } - //Normalize to zero if all data is positive or negative - if (axisMin < 0 && isAllPositive) - { - axisMin = 0; - } - } - - if (axisOptions.LockedMax.HasValue) - { - axisMax = axisOptions.LockedMax.Value; - } - else - { - axisMax = dataMax + (dataRange * 0.05); - if (axisOptions.IsStacked100 && axisMax > 1) - { - axisMax = 1; - } - - if (axisMax > 0 && isAllNegative) - { - axisMax = 0; - } - //if (axisOptions.LockedMin.HasValue==false && axisMin > 0 && dataMin / dataMax <= 0.2) - //{ - // axisMin = 0; - //} - } - - // Calculate interval - roughInterval = (axisMax - axisMin) / desiredTicks; - scaleInterval = GetScaleNumber(roughInterval, true); - - if (!axisOptions.LockedMin.HasValue) axisMin = Math.Floor(axisMin / scaleInterval) * scaleInterval; - if (!axisOptions.LockedMax.HasValue) axisMax = Math.Ceiling(axisMax / scaleInterval) * scaleInterval; - } - else - { - roughInterval = (dataMax - dataMin) / desiredTicks; - scaleInterval = GetScaleNumber(roughInterval, true); - if (dataMin / dataMax >= 0.2) - { - axisMin = 0; - } - else - { - axisMin = Math.Floor(dataMin / scaleInterval) * scaleInterval; - } - - // Calculate interval - - axisMax = dataMax; - } - - var tickCount = (int)Math.Round((axisMax - axisMin) / scaleInterval) + 1; - return new AxisScale - { - Min = axisOptions.LockedMin ?? axisMin, - Max = axisOptions.LockedMax ?? axisMax, - MajorInterval = scaleInterval, - MinorInterval = scaleInterval / 5, - TickCount = tickCount, - }; - } - } - - private static double GetScaleNumber(double value, bool round) - { - double exponent = Math.Floor(Math.Log10(value)); - double fraction = value / Math.Pow(10, exponent); - double scaleFraction; - - if (round) - { - if (fraction < 1.5) scaleFraction = 1; - else if (fraction < 3) scaleFraction = 2; - else if (fraction < 7) scaleFraction = 5; - else scaleFraction = 10; - } - else - { - if (fraction <= 1) scaleFraction = 1; - else if (fraction <= 2) scaleFraction = 2; - else if (fraction <= 5) scaleFraction = 5; - else scaleFraction = 10; - } - - return scaleFraction * Math.Pow(10, exponent); - } - - public static List GetTickValues(AxisScale scale) - { - var ticks = new List(); - for (int i = 0; i < scale.TickCount; i++) - { - double tickValue = scale.Min + (i * scale.MajorInterval); - ticks.Add(Math.Round(tickValue, 10)); // Round to avoid floating point errors - } - return ticks; - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartAxisRenderer.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartAxisRenderer.cs deleted file mode 100644 index 99fc23bd3d..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartAxisRenderer.cs +++ /dev/null @@ -1,1120 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer; -using EPPlus.Export.ImageRenderer.RenderItems; -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlus.Export.ImageRenderer.Svg.Chart.Util; -using EPPlus.Fonts.OpenType; -using EPPlus.Fonts.OpenType.Integration; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.Drawing.Chart.Style; -using OfficeOpenXml.FormulaParsing.Excel.Functions.DateAndTime; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using OfficeOpenXml.FormulaParsing.Utilities; -using OfficeOpenXml.Style; -using OfficeOpenXml.Style.XmlAccess; -using OfficeOpenXml.Utils.String; -using OfficeOpenXml.Utils.TypeConversion; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace EPPlusImageRenderer.Svg -{ - internal class ChartAxisRenderer : SvgChartObject, IDrawingChartAxis - { - private const double COS45 = 0.70710678118654757; //Constant for Math.Sin(Math.PI / 4) --45 degrees - internal ChartAxisRenderer(SvgChart sc, ExcelChartAxisStandard ax) : base(sc) - { - SvgChart = sc; - Axis = ax; - SetMargins(ax.TextBody); - - if (sc.Chart.Series.Count == 0) - { - return; - } - - if(ax.HasTitle) - { - Title = new ChartTitleRenderer(sc, ax.Title, "Axis Title", this); - } - else - { - Title = null; - } - - Values = GetAxisValue(ax, sc.ChartArea.Rectangle, out double? min, out double? max, out double? majorUnit, out eTimeUnit? dateUnit, out eTextOrientation orientation); - AxisValues = GetAxisDisplayValues(ax, Values, min, max, majorUnit); - - Min = min ?? 0D; - Max = max ?? (Values.Count > 0 ? ConvertUtil.GetValueDouble(Values[Values.Count - 1], false, true) : 0D); - MajorUnit = majorUnit ?? 1; - MinorUnit = ax.MinorUnit ?? GetAutoMinUnit(MajorUnit); - MajorDateUnit = dateUnit; - LabelOrientation = orientation; - - if (ax.Deleted == false) - { - if (ax.Layout.HasLayout) - { - Rectangle = GetRectFromManualLayout(sc, ax.Layout); - } - else - { - Rectangle = new SvgRenderRectItem(sc, sc.Bounds); - if (ax.AxisPosition == eAxisPosition.Left || ax.AxisPosition == eAxisPosition.Right) - { - if (ax.AxisPosition == eAxisPosition.Left) - { - Rectangle.Width = GetTextWidest(sc, ax) + LeftMargin; - var ll = 8D; - if (sc.Chart.HasLegend && sc.Chart.Legend.Position == eLegendPosition.Left) - { - ll = sc.Legend.Rectangle.Right + sc.Legend.RightMargin; - } - Rectangle.Left = Title == null ? ll : Title.Rectangle.Left + Title.TextBox.GetActualWidth(); - } - else - { - Rectangle.Width = GetTextWidest(sc, ax) + RightMargin; - var lp = sc.ChartArea.Rectangle.Width - Rectangle.Width - 8D; - if (sc.Chart.HasLegend && sc.Chart.Legend.Position == eLegendPosition.Right) - { - lp = sc.Legend.Rectangle.Left + -Rectangle.Width; - } - Rectangle.Left = Title == null ? lp : Title.Rectangle.Left - Rectangle.Width - LeftMargin; - } - } - else - { - Rectangle.Height = GetTextHeight(sc, ax); - //TODO:Fix - Rectangle.Top = Title == null || ax.AxisPosition == eAxisPosition.Top ? sc.ChartArea.Rectangle.Height - 8 - Rectangle.Height : Title.Rectangle.Top - Rectangle.Height - 8; - } - } - - Rectangle.FillColor = "none"; - - Line = new SvgRenderLineItem(sc, Rectangle.Bounds); - Line.SetDrawingPropertiesBorder(ax.Border, sc.Chart.StyleManager.Style.Title.BorderReference.Color, ax.Border.Fill.Style != eFillStyle.NoFill, 1); - if(Line.BorderWidth < 1) - { - Line.BorderWidth = 1; - } - } - } - - private double GetAutoMinUnit(double majorUnit) - { - return majorUnit / 5; - } - - internal ExcelChartAxisStandard Axis { get; } - internal SvgChart SvgChart { get; } - internal SvgRenderLineItem Line { get; set; } - private List GetAxisDisplayValues(ExcelChartAxisStandard ax, List values, double? min, double? max, double? majorUnit) - { - var displayValues = new List(); - var nf = new ExcelFormatTranslator(ax.Format, 0); - //Excel replaces the format with a default date format if the axis is date based. - if (nf.DataType == ExcelNumberFormatXml.eFormatType.DateTime) - { - if(ax.Format == "m/d/yyyy") - { - var sdFormat = ExcelNumberFormat.GetFromBuildInFromID(14); //14 is standard regional short date. - nf = new ExcelFormatTranslator(sdFormat, 14); - } - } - foreach (var v in CategoryAxisScaleCalculator.GetUniqueValues(values)) - { - var s = ValueToTextHandler.FormatValue(v, false, nf, null, out bool isValidFormat); - displayValues.Add(s); - } - return displayValues; - } - private double GetTextHeight(SvgChart sc, ExcelChartAxisStandard ax) - { - var tm = sc.TextMeasurer; - var highest = 0D; - var mf = ax.Font.GetMeasureFont(); - foreach (var s in AxisValues) - { - var m = tm.MeasureText(s, mf); - switch (LabelOrientation) - { - case eTextOrientation.Horizontal: - if (m.Height > highest) - { - highest = m.Height; - } - break; - case eTextOrientation.Diagonal: - var width = (m.Width + m.Height) * COS45; - if (width > highest) - { - highest = width; - } - break; - case eTextOrientation.Vertical: - if (m.Width > highest) - { - highest = m.Width; - } - break; - } - } - return highest.PointToPixel(); - } - - private double GetTextWidest(SvgChart sc, ExcelChartAxisStandard ax) - { - var mf = ax.Font.GetMeasureFont(); - var shaper = OpenTypeFonts.GetShaperForFont(mf); - var tm = new OpenTypeFontTextMeasurer(shaper); - - var widest = 0f; - - foreach(var s in AxisValues) - { - var m= tm.MeasureText(s, mf); - if (m.Width > widest) - { - widest = m.Width; - } - } - return widest.PointToPixel(); - } - - public List Values - { - get; - private set; - } - public List AxisValues { get; private set; } - - public List MajorAxisPositions { get; private set; } - public List MinorAxisPositions { get; private set; } - public List MajorGridlinePositions { get; private set; } - public List MinorGridlinePositions { get; private set; } - public SvgAxisTextBoxes Textboxes{get; private set;} - public ChartTitleRenderer Title { get; set; } - public double Min { get; set; } - public double Max { get; set; } - public double MajorUnit { get; set; } - public double MinorUnit { get; set; } - public eTimeUnit? MajorDateUnit { get; set; } - public eTextOrientation LabelOrientation { get; set; } - public bool IsDateScale - { - get; - private set; - } = false; - - internal override void AppendRenderItems(List renderItems) - { - Title?.AppendRenderItems(renderItems); - //Title?.Render(sb); - if(Rectangle!=null) renderItems.Add(Rectangle); - - if (MajorGridlinePositions != null) - { - foreach (var tm in MajorGridlinePositions) - { - renderItems.Add(tm); - } - } - if (MinorGridlinePositions != null) - { - foreach (var tm in MinorAxisPositions) - { - renderItems.Add(tm); - } - } - - if (Line != null) renderItems.Add(Line); - - if (MajorAxisPositions != null) - { - foreach (var tm in MajorAxisPositions) - { - renderItems.Add(tm); - } - } - if (MinorAxisPositions != null) - { - foreach (var tm in MinorAxisPositions) - { - renderItems.Add(tm); - } - } - - //The axis text boxes is rendered later as they have a higher Z-order. - } - - - internal void AddTickmarksAndValues(List DefItems) - { - if (Axis.Deleted == true) return; - - if (Axis.MajorTickMark != eAxisTickMark.None) - { - MajorAxisPositions = AddTickmarks(MajorUnit, MajorDateUnit, double.NaN, 4D.PixelToPoint(), Axis.MajorTickMark); - } - - if (Axis.MinorTickMark != eAxisTickMark.None) - { - MinorAxisPositions = AddTickmarks(MinorUnit, MajorDateUnit, MajorUnit, 2D.PixelToPoint(), Axis.MinorTickMark); - } - - if(Axis.HasMajorGridlines) - { - MajorGridlinePositions = AddGridlines(MajorUnit, double.NaN, Axis.MajorGridlines, Chart.StyleManager.Style.GridlineMajor); - } - - if ((Axis.HasMinorGridlines)) - { - MinorGridlinePositions = AddGridlines(MinorUnit, MajorUnit, Axis.MinorGridlines, Chart.StyleManager.Style.GridlineMinor); - } - - if (AxisValues != null && AxisValues.Count > 0 && Axis.Deleted==false && Axis.LabelPosition != eTickLabelPosition.None) - { - Textboxes = new SvgAxisTextBoxes(SvgChart); - Textboxes.TextBoxes = GetAxisValueTextBoxes(); - } - } - - private List GetAxisValueTextBoxes() - { - var ret = new List(); - if (Axis.LabelPosition == eTickLabelPosition.None) return ret; - - var mf = Axis.Font.GetMeasureFont(); - - var shaper = OpenTypeFonts.GetShaperForFont(mf); - var tm = new OpenTypeFontTextMeasurer(shaper); - - var axisStyle = GetAxisStyleEntry(); - double maxWidth, maxHeight; - if(Axis.AxisPosition==eAxisPosition.Left || Axis.AxisPosition == eAxisPosition.Right) - { - maxWidth = SvgChart.ChartArea.Rectangle.Width / 3; //TODO: Check this value. - maxHeight = Rectangle.Height / AxisValues.Count; - } - else - { - switch (LabelOrientation) - { - case eTextOrientation.Vertical: - maxWidth = SvgChart.ChartArea.Rectangle.Height / 3; - maxHeight = Rectangle.Width / AxisValues.Count; //TODO: Check this value. - break; - case eTextOrientation.Diagonal: - maxWidth = (Rectangle.Width + Rectangle.Height) / COS45; - maxHeight = SvgChart.ChartArea.Rectangle.Height / 3; //TODO: Check this value. - break; - default: - maxWidth = Rectangle.Width / AxisValues.Count; - maxHeight = SvgChart.ChartArea.Rectangle.Height / 3; //TODO: Check this value. - break; - } - } - double widest=0; - for (var i = 0; i < AxisValues.Count; i++) - { - var v = AxisValues[i]; - var m = tm.MeasureText(v, mf); - var ticMarkX = GetAxisItemLeft(i, m); - var ticMarkY = GetAxisItemTop(i, m); - var width = m.Width; - var height = m.Height; - double x, y; - if(LabelOrientation==eTextOrientation.Horizontal) - { - if (Axis.AxisType == eAxisType.Cat || Axis.AxisType==eAxisType.Date) - { - x = ticMarkX; - y = ticMarkY; - } - else - { - if(Axis.IsVertical) - { - x = ticMarkX; - if (SvgChart.Chart.IsTypeBar()) - { - y = ticMarkY; - } - else - { - y = ticMarkY - height / 2; - } - } - else - { - x = ticMarkX - width / 2; - y = ticMarkY; - } - } - } - else - { - var rot = LabelOrientation == eTextOrientation.Diagonal ? -45 : -90; - double cos = Math.Cos(MathHelper.Radians(rot)); - double sin = Math.Sin(MathHelper.Radians(rot)); - - if (LabelOrientation == eTextOrientation.Diagonal) - { - x = ticMarkX - (height / 2) * cos; - if (Axis.AxisPosition == eAxisPosition.Bottom) - { - y = ticMarkY + 4 + TopMargin - (height / 2 * cos); - } - else //Top - { - y = ticMarkY - 4 - BottomMargin - (height/2 * cos); - } - } - else - { - x = ticMarkX - (height / 2); - if (Axis.AxisPosition == eAxisPosition.Bottom) - { - y = ticMarkY + BottomMargin + 4; - } - else //Top - { - y = ticMarkY - TopMargin - 4; - } - } - } - - var tb = new SvgTextBox(SvgChart, Rectangle.Bounds, x, y, width, height, maxWidth, maxHeight); - if (LabelOrientation == eTextOrientation.Diagonal) - { - tb.Rotation = -45; - if(Axis.AxisPosition==eAxisPosition.Bottom) - { - tb.TextAnchor = eTextAnchor.End; - } - } - - else if (LabelOrientation == eTextOrientation.Vertical) - { - tb.Rotation = -90; - if (Axis.AxisPosition == eAxisPosition.Bottom) - { - tb.TextAnchor = eTextAnchor.End; - } - } - - var p = Axis.TextBody.Paragraphs.FirstOrDefault(); - - if (p.HorizontalAlignment != eTextAlignment.Center && Axis.AxisType!=eAxisType.Val && (Axis.AxisPosition == eAxisPosition.Bottom || Axis.AxisPosition == eAxisPosition.Top)) - { - //Horizontal axises are always center aligned visually - //Should be broken out as input to ImportParagraph instead of changing the base item - p.HorizontalAlignment = eTextAlignment.Center; - } - - tb.TextBody.ImportParagraph(p, 0, v); - - //tb.TextBody.Paragraphs[0].AddText(v, Axis.Font); - tb.Rectangle.SetDrawingPropertiesFill(Axis.Fill, axisStyle.FillReference.Color); - - if(widest < tb.Width) - { - widest = tb.Width; - } - ret.Add(tb); - } - - if(Axis.IsVertical) - { - //If the axis is vertical, we need to adjust the left position of the textboxes to align them to the right and not have them overlap with the axis line. - if (Axis.AxisPosition == eAxisPosition.Left) - { - foreach (var tb in ret) - { - tb.Left += (widest - tb.Width); - } - } - else - { - foreach (var tb in ret) - { - tb.Left += LeftMargin; - } - } - } - else if(LabelOrientation==eTextOrientation.Horizontal && IsCatAx()) //Only apples when labels are horizontally aligned - { - //Align the axis labels according to the label alignment setting. This is only relevant for horizontal axis, vertical axis are always right aligned. - var lblAlignment = (Axis as ExcelChartAxisStandard)?.LabelAlignment??OfficeOpenXml.eAxisLabelAlignment.Center; - var majorWidth = Rectangle.Width / AxisValues.Count; - if (Axis.CrossingAxis == null || Axis.CrossingAxis.CrossBetween == eCrossBetween.MidCat) - { - foreach (var tb in ret) - { - switch (lblAlignment) - { - case OfficeOpenXml.eAxisLabelAlignment.Left: - tb.Left -= tb.Width; - break; - case OfficeOpenXml.eAxisLabelAlignment.Center: - tb.Left -= tb.Width / 2; - break; - case OfficeOpenXml.eAxisLabelAlignment.Right: - break; - } - } - } - else - { - foreach (var tb in ret) - { - switch (lblAlignment) - { - case OfficeOpenXml.eAxisLabelAlignment.Left: - break; - case OfficeOpenXml.eAxisLabelAlignment.Center: - tb.Left += majorWidth / 2 - tb.Width / 2; - break; - case OfficeOpenXml.eAxisLabelAlignment.Right: - tb.Left += majorWidth - tb.Width; - break; - } - } - } - } - - return ret; - } - - private bool IsCatAx() - { - return Axis.AxisType == eAxisType.Cat || (Axis.AxisType == eAxisType.Date && IsDateScale==false); - } - - private double GetAxisItemLeft(int i, OfficeOpenXml.Interfaces.Drawing.Text.TextMeasurement m) - { - if (Axis.IsVertical) - { - return Rectangle.Left; - } - else - { - if (IsCatAx()) - { - double majorWidth; - if (Axis.CrossingAxis == null || Axis.CrossingAxis.CrossBetween == eCrossBetween.Between) - { - majorWidth = Rectangle.Width / AxisValues.Count; - } - else - { - majorWidth = Rectangle.Width / (AxisValues.Count - 1); - } - var majorTickStartingPosition = Rectangle.Left + majorWidth * i; - return majorTickStartingPosition; - } - else - { - var min = ConvertUtil.GetValueDouble(Values[0]); - var max = ConvertUtil.GetValueDouble(Values.Last()); - var v = ConvertUtil.GetValueDouble(Values[i]); - var majorWidth = Rectangle.Width * (v - Min) / (Max - Min); - return Rectangle.Left + majorWidth; - } - //} - } - } - - private double GetAxisItemTop(int i, OfficeOpenXml.Interfaces.Drawing.Text.TextMeasurement m) - { - if (Axis.AxisPosition == eAxisPosition.Top) - { - switch (LabelOrientation) - { - case eTextOrientation.Vertical: - case eTextOrientation.Diagonal: - return Rectangle.Bottom; - default: - return Rectangle.Bottom - m.Height - TopMargin; - } - } - else if (Axis.AxisPosition == eAxisPosition.Bottom) - { - switch(LabelOrientation) - { - case eTextOrientation.Vertical: - if (Axis.LabelPosition == eTickLabelPosition.Low) - { - return Rectangle.Bottom - m.Width; - } - else - { - return Rectangle.Top; - } - case eTextOrientation.Diagonal: - if (Axis.LabelPosition == eTickLabelPosition.Low) - { - return Rectangle.Bottom - (m.Width+m.Height)* COS45; - } - else - { - return Rectangle.Top; - } - default: - if (Axis.LabelPosition == eTickLabelPosition.Low) - { - return Rectangle.Bottom - m.Height; - } - else if (Axis.LabelPosition == eTickLabelPosition.NextTo) - { - return Rectangle.Top + BottomMargin; - } - else //TODO:Add support for hight. - { - return Rectangle.Top + BottomMargin; - } - } - } - else - { - if (Axis.AxisType == eAxisType.Cat || Axis.AxisType == eAxisType.Date) - { - var majorHeight = Rectangle.Height / (AxisValues.Count); - return Rectangle.Top + majorHeight * (AxisValues.Count - i) - ((majorHeight / 2) + m.Height / 2); - } - else - { - var majorHeight = Rectangle.Height / (AxisValues.Count-1); - return Rectangle.Top + majorHeight * (AxisValues.Count - i - 1); - //return Rectangle.Top + majorHeight * (AxisValues.Count - i - 1); - } - } - - } - - private List AddTickmarks(double units, eTimeUnit? dateUnit, double parentUnit, double tickMarkWidth, eAxisTickMark type) - { - var axisStyle = GetAxisStyleEntry(); - - var tms = new List(); - double min, max, addMinor=0D; - if(double.IsNaN(parentUnit)==false && parentUnit==units) - { - addMinor = parentUnit / 2; - } - - if (Axis.AxisType == eAxisType.Cat) - { - min = 1; - if (Axis.CrossingAxis==null || Axis.CrossingAxis.CrossBetween == eCrossBetween.Between) - { - max = AxisValues.Count; - } - else - { - max = AxisValues.Count - 1; - } - } - else - { - min = Min; - max = Max; - } - - double tickMarkWidthInside=0, tickMarkWidthOutside=0; - if(type==eAxisTickMark.In || type==eAxisTickMark.Cross) - { - tickMarkWidthInside = tickMarkWidth; - } - if(type==eAxisTickMark.Out || type == eAxisTickMark.Cross) - { - tickMarkWidthOutside = tickMarkWidth; - } - var diff = max - min + 1; - double d = min + addMinor; - while (d <= max+1) - { - if (double.IsNaN(parentUnit) || (d % parentUnit != 0)) - { - double x1, y1, x2, y2; - switch (Axis.AxisPosition) - { - case eAxisPosition.Left: - y1 = (float)(Rectangle.Top + Rectangle.Height - ((d - min) / diff * Rectangle.Height)); - y2 = y1; - x1 = (float)Rectangle.Right - tickMarkWidthOutside; - x2 = (float)Rectangle.Right + tickMarkWidthInside; - break; - case eAxisPosition.Right: - y1 = (float)(Rectangle.Top + Rectangle.Height - ((d - min) / diff * Rectangle.Height)); - y2 = y1; - x1 = (float)Rectangle.Left - tickMarkWidthInside; - x2 = (float)Rectangle.Left + tickMarkWidthOutside; - break; - case eAxisPosition.Top: - x1 = (float)(Rectangle.Left + ((d - min) / diff * Rectangle.Width)); - x2 = x1; - y1 = (float)Rectangle.Bottom - tickMarkWidthOutside; - y2 = (float)Rectangle.Bottom + tickMarkWidthInside; - break; - case eAxisPosition.Bottom: - x1 = (float)(Rectangle.Left + ((d - min) / diff * Rectangle.Width)); - x2 = x1; - y1 = (float)Rectangle.Top - tickMarkWidthInside; - y2 = (float)Rectangle.Top + tickMarkWidthOutside; - break; - default: - throw new InvalidOperationException("Invalid axis position"); - } - var tm = new SvgRenderLineItem(SvgChart, SvgChart.Bounds); - tm.X1 = x1; - tm.Y1 = y1; - tm.X2 = x2; - tm.Y2 = y2; - tm.SetDrawingPropertiesBorder(Axis.Border, axisStyle.BorderReference.Color, true); - if(tm.BorderWidth<1) //Excel seems to have this as minimum width for tick marks, so we enforce it here to make sure they are visible. - { - tm.BorderWidth = 1; - } - tms.Add(tm); - } - switch (dateUnit) - { - case eTimeUnit.Years: - d = DateTime.FromOADate(d).AddYears((int)units).ToOADate(); - break; - case eTimeUnit.Months: - if (units>=1D) - { - d = DateTime.FromOADate(d).AddMonths((int)units).ToOADate(); - } - else - { - var dt = DateTime.FromOADate(d); - var days = DateTime.DaysInMonth(dt.Year, dt.Month) * units; - d += days; - } - break; - default: - d += units; - break; - } - } - return tms; - } - private List AddGridlines(double units, double parentUnit, ExcelDrawingBorder lineItem, ExcelChartStyleEntry styleEntry) - { - var axisStyle = GetAxisStyleEntry(); - - var tms = new List(); - double min; - if (Axis.AxisType == eAxisType.Cat) - { - min = 0; - } - else - { - min = Min; - } - var pa = SvgChart.Plotarea; - var diff = Max - min; - - List points = new List(); - - for (double d = min; d <= Max; d += units) - { - if(d==min && Line!=null && Line.BorderWidth>0) continue; - if (double.IsNaN(parentUnit) || (d % parentUnit != 0)) - { - //float x1, y1, x2, y2; - switch (Axis.AxisPosition) - { - case eAxisPosition.Left: - case eAxisPosition.Right: - points.Add(new Point(0f, (float)(pa.Rectangle.Top + pa.Rectangle.Height - ((d - min) / diff * pa.Rectangle.Height)))); - //y1 = (float)(pa.Rectangle.Top + pa.Rectangle.Height - ((d - min) / diff * pa.Rectangle.Height)); - //y2 = y1; - //x1 = (float)pa.Rectangle.Right; - //x2 = (float)pa.Rectangle.Left; - break; - case eAxisPosition.Top: - case eAxisPosition.Bottom: - var xValue = (float)(pa.Rectangle.Left + ((d - min) / diff * pa.Rectangle.Width)); - points.Add(new Point(xValue, 0f)); - //x1 = (float)(pa.Rectangle.Left + ((d - min) / diff * pa.Rectangle.Width)); - //x2 = x1; - //y1 = (float)pa.Rectangle.Top; - //y2 = (float)pa.Rectangle.Bottom; - break; - default: - throw new InvalidOperationException("Invalid axis position"); - } - - //var tm = new SvgRenderLineItem(DrawingChart, DrawingChart.Bounds); - //tm.X1 = x1; - //tm.Y1 = y1; - //tm.X2 = x2; - //tm.Y2 = y2; - //tm.SetDrawingPropertiesBorder(lineItem, styleEntry.BorderReference.Color, true, lineItem.Width); - //tms.Add(tm); - } - } - - float x1, y1, x2, y2; - - string id = ""; - - switch (Axis.AxisPosition) - { - case eAxisPosition.Left: - case eAxisPosition.Right: - id = "xGridLine"; - y1 = (float)points.Last().Top; - y2 = y1; - x1 = (float)pa.Rectangle.Right; - x2 = (float)pa.Rectangle.Left; - break; - case eAxisPosition.Top: - case eAxisPosition.Bottom: - id = "yGridLine"; - x1 = (float)points[0].Left; - x2 = x1; - y1 = (float)pa.Rectangle.Top; - y2 = (float)pa.Rectangle.Bottom; - break; - default: - throw new InvalidOperationException("Invalid axis position"); - } - - var tm = new SvgRenderLineItem(SvgChart, SvgChart.Bounds); - tm.X1 = x1; - tm.Y1 = y1; - tm.X2 = x2; - tm.Y2 = y2; - tm.SetDrawingPropertiesBorder(lineItem, styleEntry.BorderReference.Color, true, lineItem.Width); - - tm.DefId = id; - - tms.Add(tm); - - var distX = (float)points.Last().Left - points[0].Left; - var distY = points[0].Top - (float)points.Last().Top; - - var offsetX = distX / (points.Count-1); - var offsetY = distY / (points.Count-1); - - for(int i = 0; i < points.Count; i++) - { - var refItem = new SvgUseRefItem(SvgChart, SvgChart.Bounds, id); - if(id == "xGridLine") - { - refItem.X = 0f; - refItem.Y = offsetY*i; - } - else if(id == "yGridLine") - { - refItem.X = offsetX*i; - refItem.Y = 0f; - } - tms.Add(refItem); - } - - - return tms; - } - - private ExcelChartStyleEntry GetAxisStyleEntry() - { - ExcelChartStyleEntry axisStyle; - switch (Axis.AxisType) - { - case eAxisType.Cat: - axisStyle = Chart.StyleManager.Style.CategoryAxis; - break; - case eAxisType.Serie: - axisStyle = Chart.StyleManager.Style.SeriesAxis; - break; - default: - axisStyle = Chart.StyleManager.Style.ValueAxis; - break; - } - - return axisStyle; - } - - internal double GetPositionInPlotarea(double val, bool startValue=false) - { - if (Axis.AxisPosition == eAxisPosition.Left || Axis.AxisPosition == eAxisPosition.Right) - { - if (Axis.AxisType == eAxisType.Cat) - { - var majorHeight = SvgChart.Plotarea.Rectangle.Height / Max; - if(startValue) - { - return majorHeight * val; - } - else - { - //if (Axis.CrossingAxis == null || Axis.CrossingAxis.CrossBetween == eCrossBetween.Between) - //{ - // majorHeight = DrawingChart.Plotarea.Rectangle.Height / (Max - 1); - // return majorHeight * val; - //} - //else - //{ - return majorHeight * val + (majorHeight / 2); - //} - } - } - else if (Axis.AxisType == eAxisType.Date && IsDateScale == false) - { - //if (val < Min || val > Max) return double.NaN; - var diff = Max - Min + 1; - return (((val - Min) / diff * SvgChart.Plotarea.Rectangle.Height)); - } - else - { - //if (val < Min || val > Max) return double.NaN; - var diff = Max - Min; - return (Max - val) / diff * SvgChart.Plotarea.Rectangle.Height; - } - } - else - { - if (Axis.AxisType == eAxisType.Cat) - { - var majorWidth = SvgChart.Plotarea.Rectangle.Width / Max; - if (startValue) - { - return majorWidth * val; - } - else - { - if(Axis.CrossingAxis == null || Axis.CrossingAxis.CrossBetween==eCrossBetween.Between) - { - return majorWidth * val + (majorWidth / 2); - } - else - { - majorWidth = SvgChart.Plotarea.Rectangle.Width / (Max - 1); - return majorWidth * val; - } - } - } - else if(Axis.AxisType == eAxisType.Date && IsDateScale==false) - { - if (val < Min || val > Max) return double.NaN; - var diff = Max - Min + 1; - return (((val - Min) / diff * SvgChart.Plotarea.Rectangle.Width)); - } - else - { - if (val < Min || val > Max) return double.NaN; - var diff = Max - Min; - return (((val - Min) / diff * SvgChart.Plotarea.Rectangle.Width)); - } - } - } - protected List GetAxisValue(ExcelChartAxisStandard ax, RenderItem rect, out double? min, out double? max, out double? majorUnit, out eTimeUnit? dateUnit, out eTextOrientation orientation) - { - var values = ax.GetAxisValues(out bool isCount); - var isNumeric = values.Any(x => x == null || x.IsNumeric()); - var options = new AxisOptions - { - LockedMin = ax.MinValue, - LockedMax = ax.MaxValue, - LockedInterval = ax.MajorUnit, - LockedIntervalUnit = ax.MajorTimeUnit, - AddPadding = ShouldHavePadding(), - Axis = ax, - IsStacked100 = Chart.IsTypePercentStacked(), - ChartSize = rect - }; - - if ((ax.AxisType == eAxisType.Cat || (ax.IsDate && isNumeric==false)) && - isCount == false) - { - AxisScale res; - if (ax.IsVertical) - { - res = CategoryAxisScaleCalculator.CalculateVerticalAxisByHeight(ref values, SvgChart.TextMeasurer, options); - } - else - { - res = CategoryAxisScaleCalculator.CalculateHorizontalAxisByWidth(ref values, SvgChart.TextMeasurer, options); - } - - min = res.Min; - max = res.Max; - majorUnit = res.MajorInterval; - dateUnit = null; - orientation = res.TextOrientation; - - return res.DisplayValues; - } - - var l = new List(); - min = double.MaxValue; - max = double.MinValue; - foreach (var v in values) - { - var d = ConvertUtil.GetValueDouble(v, false, true); - if (double.IsNaN(d)) - { - d = 0; - } - if (min > d) - { - min = d; - } - if (max < d) - { - max = d; - } - } - - var length = ax.AxisPosition == eAxisPosition.Left || ax.AxisPosition == eAxisPosition.Right ? SvgChart.Bounds.Height : SvgChart.Bounds.Width; //Fix and use plotarea width/height. - if(isCount) - { - majorUnit = 1; - dateUnit = null; - for (int i=1;i<=max;i++) - { - l.Add(i); - } - var res = CategoryAxisScaleCalculator.CalculateHorizontalAxisByWidth(ref l, SvgChart.TextMeasurer, options); - - min = res.Min; - max = res.Max; - majorUnit = res.MajorInterval; - dateUnit = null; - orientation = res.TextOrientation; - - return l.ToList(); - } - if(ax.AxisType==eAxisType.Val) - { - AdjustminMaxFromChartObjects(ax, ref min, ref max); - } - if (ax.IsDate) - { - AxisScale res; - if (ax.IsVertical) - { - res = DateAxisScaleCalculator.CalculateByWidthHeight(options.ChartSize.Bounds.Height, min ?? 0D, max ?? 0D, SvgChart.TextMeasurer, options); - } - else - { - if (ax.AxisType==eAxisType.Val) - { - res = DateAxisScaleCalculator.Calculate(min ?? 0D, max ?? 0D, options); - } - else - { - res = DateAxisScaleCalculator.CalculateByWidthAllowDiagonal(min ?? 0D, max ?? 0D, SvgChart.TextMeasurer, options); - } - } - - orientation = res.TextOrientation; - dateUnit = res.MajorDateUnit; - majorUnit = res.MajorInterval; - var dt = DateTime.FromOADate(res.Min); - var maxDt = DateTime.FromOADate(res.Max); - IsDateScale = (dateUnit != eTimeUnit.Days || majorUnit > 1) && values.Count > 31; - - while (dt <= maxDt) - { - l.Add(dt); - switch(res.MajorDateUnit ?? eTimeUnit.Days) - { - case eTimeUnit.Years: - dt = dt.AddYears((int)res.MajorInterval); - break; - case eTimeUnit.Months: - dt = dt.AddMonths((int)res.MajorInterval); - break; - case eTimeUnit.Days: - dt = dt.AddDays((int)res.MajorInterval); - break; - } - } - - min = res.Min; - max = res.Max; - } - else - { - var res = ValueAxisScaleCalculator.Calculate(min ?? 0, max ?? 0, length, options); - for (var v = res.Min; v <= res.Max; v += res.MajorInterval) - { - l.Add(v); - } - - min = res.Min; - max = res.Max; - majorUnit = res.MajorInterval; - dateUnit= null; - orientation = eTextOrientation.Horizontal; - } - - return l; - } - - /// - /// Adjust the min and max values based on the values in the chart objects, such as trendlines. This is needed to make sure that the trendlines are visible in the chart and not cut off because the axis scale is based on the data series only. - /// - /// The min value to adjust. - /// The max value to adjust. - private void AdjustminMaxFromChartObjects(ExcelChartAxisStandard ax, ref double? min, ref double? max) - { - foreach (var drawer in SvgChart.Plotarea.ChartTypeDrawers) - { - if (drawer.IsOnAxis(ax)) - { - if (drawer.SupportsTrendlines) - { - foreach (var tl in drawer.Trendlines) - { - foreach (var c in tl.Coordinates) - { - if (min > c.Y) - { - min = c.Y; - } - if (max < c.Y) - { - max = c.Y; - } - } - } - } - } - } - } - - private bool ShouldHavePadding() - { - return Axis.AxisType == eAxisType.Val || (Chart.IsTypeLine() && Axis.AxisType == eAxisType.Date); - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTitleRenderer.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTitleRenderer.cs deleted file mode 100644 index 7760d54fbe..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTitleRenderer.cs +++ /dev/null @@ -1,229 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -using OfficeOpenXml.Style; -using OfficeOpenXml.Utils.EnumUtils; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace EPPlusImageRenderer.Svg -{ - internal class ChartTitleRenderer : DrawingChartObject - { - ExcelChartTitleStandard _title; - string _titleText; - SvgChart _svgChart; - /// - /// - /// - /// - /// - /// - /// If null, this is the main chart title. - internal ChartTitleRenderer(ChartRenderer sc, ExcelChartTitleStandard t, string defaultText, ChartAxisRenderer axis=null) : base(sc) - { - _svgChart = sc; - - - //These are hard coded margins for the title box. - LeftMargin = RightMargin = 3; //4px - TopMargin = BottomMargin = 1.5; //2px - LeftMargin = RightMargin = 3; //4px - TopMargin = BottomMargin = 1.5; //2px - - var maxWidth = sc.Bounds.Width * 0.8; - var maxHeight = sc.Bounds.Height / 2D; - _title = t; - if (axis==null) - { - _titleText = GetDefaultChartTitleText(sc, t, defaultText); - } - else - { - if (string.IsNullOrEmpty(t.DisplayedText) == false) - { - _titleText = t.DisplayedText; - } - else - { - _titleText = t.Font.GetCapitalizedText(defaultText); - } - } - - if (t.Layout.HasLayout) //Only for the main chart title, axis titles don't support manual layout in Excel. - { - - InitTextBox(maxWidth, maxHeight); - var mr = GetRectFromManualLayout(sc, t.Layout); - TextBox.Top = mr.Top; - TextBox.Left = mr.Left; - } - else - { - if (axis == null) - { - InitTextBox(maxWidth, maxHeight); - TextBox.Top = (float)6; //6 point for the chart title standard offset. - TextBox.Left = (float)(sc.Bounds.Width - TextBox.Width) / 2; - } - else - { - var isVertical = axis.Axis.IsVertical; - maxWidth = sc.Bounds.Width * (isVertical ? 0.2 : 0.8); //Max Width. - maxHeight = sc.Bounds.Height * (isVertical ? 0.8 : 0.2); //Max Height. - - InitTextBox(maxWidth, maxHeight); - SetAxisTitleRect(sc, axis); - } - } - Bounds = Rectangle.Bounds; - Rectangle.SetDrawingPropertiesFill(t.Fill, sc.Chart.StyleManager.Style.Title.FillReference.Color); - Rectangle.SetDrawingPropertiesBorder(t.Border, sc.Chart.StyleManager.Style.Title.BorderReference.Color, t.Border.Fill.Style != eFillStyle.NoFill, 0.75); - } - - private void SetAxisTitleRect(SvgChart sc, ChartAxisRenderer axis) - { - var margin = 8F; - switch (axis.Axis.AxisPosition) - { - case eAxisPosition.Left: - Rectangle.Top = sc.GetPlotAreaTop(); - Rectangle.Left = sc.Chart.HasLegend && sc.Chart.Legend.Position == eLegendPosition.Left ? sc.Legend.Rectangle.Right + LeftMargin : margin; - break; - case eAxisPosition.Right: - Rectangle.Top = sc.GetPlotAreaTop(); - Rectangle.Left = sc.Chart.HasLegend && sc.Chart.Legend.Position == eLegendPosition.Right || sc.Chart.Legend.Position == eLegendPosition.TopRight ? sc.Legend.Rectangle.Left - Rectangle.Width - margin : sc.Bounds.Right - Rectangle.Width - margin; - break; - case eAxisPosition.Bottom: - Rectangle.Top = sc.ChartArea.Rectangle.Height - margin - Rectangle.Height; - Rectangle.Left = GetHorizontalLeft(sc); - break; - case eAxisPosition.Top: - Rectangle.Top = sc.Title != null && sc.Title._title.Layout.HasLayout==false ? sc.Title.Rectangle.Bottom+margin : margin; - Rectangle.Left = GetHorizontalLeft(sc); - break; - } - } - - private double GetHorizontalLeft(SvgChart sc) - { - var margin = 8F; - if (sc.HorizontalAxis!=null) - { - if(sc.HorizontalAxis.Axis.Deleted && sc.HorizontalAxis.Title != null && sc.HorizontalAxis.Title._title.Layout.HasLayout==false) - { - return sc.HorizontalAxis.Title.Rectangle.Right; - } - return sc.HorizontalAxis.Rectangle.Right; - } - else - { - return margin; - } - } - - private static string GetDefaultChartTitleText(SvgChart sc, ExcelChartTitleStandard t, string defaultText) - { - if (string.IsNullOrEmpty(t.DisplayedText) == false) - { - defaultText = t.DisplayedText; - } - else if (sc.Chart.PlotArea.ChartTypes.Count == 1 && sc.Chart.Series.Count == 1) - { - if (string.IsNullOrEmpty(sc.Chart.Series[0].Header)) - { - var s = sc.Chart.Series[0]; - if (s.NumberLiteralsX != null && s.NumberLiteralsX.Length > 0) - { - defaultText = s.NumberLiteralsX[0].ToString(CultureInfo.InvariantCulture); - } - else if (s.StringLiteralsX != null && s.StringLiteralsX.Length > 0) - { - defaultText = s.StringLiteralsX[0]; - } - else if(s.HeaderAddress!=null) - { - defaultText = s.GetHeaderText(0); - } - } - else - { - defaultText = sc.Chart.Series[0].Header; - } - } - - return defaultText; - } - - internal void InitTextBox(double maxWidth, double maxHeight) - { - TextBox = new SvgTextBox(_svgChart, _svgChart.ChartArea.Bounds, maxWidth, maxHeight); - if(_title.Rotation != 0) - { - TextBox.Rotation = _title.Rotation; - TextBox.Rotation = _title.Rotation; - } - if (_title.TextBody.Paragraphs.Count > 0) - { - TextBox.ImportTextBody(_title.TextBody, true, ExcelHorizontalAlignment.Center); - } - else - { - var p = _title.DefaultTextBody.Paragraphs.FirstOrDefault(); - TextBox.ImportParagraph(p, 0, _titleText); - } - - TextBox.LeftMargin = LeftMargin; - TextBox.RightMargin = RightMargin; - TextBox.TopMargin = TopMargin; - TextBox.BottomMargin = BottomMargin; - TextBox.TextBody.VerticalAlignment = eTextAnchoringType.Top; - Rectangle = (SvgRenderRectItem)TextBox.Rectangle; - - TextBox.LeftMargin = LeftMargin; - TextBox.RightMargin = RightMargin; - TextBox.TopMargin = TopMargin; - TextBox.BottomMargin = BottomMargin; - TextBox.TextBody.VerticalAlignment = eTextAnchoringType.Top; - Rectangle = (SvgRenderRectItem)TextBox.Rectangle; - } - - public SvgTextBox TextBox - { - get; private set; - } - internal override void AppendRenderItems(List renderItems) - { - var p = _title.DefaultTextBody.Paragraphs.FirstOrDefault(); - TextBox.TextBody.FontColorString = "#" + p.DefaultRunProperties.Fill.Color.ToColorString(); - TextBox.AppendRenderItems(renderItems); - TextBox.Rectangle.SetDrawingPropertiesFill(_title.Fill, _svgChart.Chart.StyleManager.Style.Title.FillReference.Color); - TextBox.Rectangle.SetDrawingPropertiesBorder(_title.Border, _svgChart.Chart.StyleManager.Style.Title.BorderReference.Color, _title.Border.Fill.Style != eFillStyle.NoFill, 0.75); - } - - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/BarColumnChartTypeDrawer.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/BarColumnChartTypeDrawer.cs deleted file mode 100644 index bfd81d6e08..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/BarColumnChartTypeDrawer.cs +++ /dev/null @@ -1,284 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlus.Graphics; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.DigitalSignatures; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Finance; -using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; -using OfficeOpenXml.Utils.TypeConversion; -using System; -using System.Collections.Generic; -using System.Linq; -using static OfficeOpenXml.ExcelErrorValue; -namespace EPPlus.Export.ImageRenderer.Svg.Chart -{ - internal class BarColumnChartTypeDrawer : ChartTypeDrawer - { - List> _catValues, _valValues; - List serieDataLabels = new List(); - List> dataPointsPerSerie = new List>(); - internal override bool SupportsTrendlines => true; - - internal BarColumnChartTypeDrawer(SvgChart svgChart, ExcelBarChart chartType) : base(svgChart, chartType) - { - _catValues = new List>(); - _valValues = new List>(); - - foreach (ExcelBarChartSerie serie in chartType.Series) - { - List valValue,catValue; - valValue = LoadSeriesValues(serie.Series, serie.NumberLiteralsY, serie.StringLiteralsY); - catValue = LoadSeriesValues(serie.XSeries, serie.NumberLiteralsX, serie.StringLiteralsX); - - _catValues.Add(catValue); - _valValues.Add(valValue); - - - //if (serie.HasDataLabel) - //{ - // var datalabel = new ChartSerieDataLabelRenderer(svgChart, serie.DataLabel, svgChart.Bounds, serie, catValue, valValue, serCounter); - // serieDataLabels.Add(datalabel); - //} - } - - if(chartType.IsTypeStacked()) - { - SumSeries(_valValues); - } - else if (chartType.IsTypePercentStacked()) - { - ExcelChartAxisStandard.CalculateStacked100(_valValues); - } - - CreateTrendlines(chartType, _catValues, _valValues); - } - - internal override void DrawSeries() - { - var groupItem = new SvgGroupItem(ChartRenderer, _svgChart.Plotarea.Rectangle.Bounds); - RenderItems.Add(groupItem); - - var isBar = _chartType.IsTypeBar(); - var count = Math.Min(_catValues.Count, _valValues.Count); - for (var i = 0; i < _catValues.Count; i++) - { - var serie = (ExcelBarChartSerie)_chartType.Series[i]; - - var dataPoints = new List(); - - //Add the bar or column. - AddBar((ExcelBarChart)_chartType, serie, _catValues, _valValues, dataPoints, count, i); - - dataPointsPerSerie.Add(dataPoints); - - //if (serie.HasDataLabel) - //{ - // for (int j = 0; j < dataPoints.Count; j++) - // { - // serieDataLabels[i].SetParentPoint(dataPoints[j], j); - // } - //} - } - - ////Trendlines and trendline labels - //foreach (var tr in Trendlines) - //{ - // tr.AppendRenderItems(RenderItems); - //} - //Append Trendline render items. - foreach (var tr in Trendlines) - { - tr.CreateRenderCoordinatesAndDatalabel(); - tr.AppendRenderItems(RenderItems); - } - - RenderItems.Add(new SvgEndGroupItem(ChartRenderer, null)); - - //Add data labels for trendlines after the trendline has been rendered, to ensure they are on top of the line. - foreach (var tr in Trendlines) - { - if (tr.DataLabel != null) - { - tr.DataLabel.AppendRenderItems(RenderItems); - } - } - - foreach (var dataLabel in serieDataLabels) - { - dataLabel.AppendRenderItems(RenderItems); - } - } - - private void AddBar(ExcelBarChart chartType, ExcelBarChartSerie serie, List> catSeries, List> valSeries, List dataPoints, int seriesCount, int position) - { - GetAxis(chartType, out var yAxis, out var xAxis); - ChartAxisRenderer valAx, catAx; - - var isColumn = chartType.IsTypeColumn(); - - if (isColumn) - { - catAx = xAxis; - valAx = yAxis; - } - else - { - catAx = yAxis; - valAx = xAxis; - } - - var catValues = catSeries[position]; - var valValues = valSeries[position]; - - var yWidth = (isColumn ? _svgChart.Plotarea.Rectangle.Width : _svgChart.Plotarea.Rectangle.Height); - - var slotSize = valValues.Count; - var gapPercent = chartType.GapWidth / 100D; // Gap width between bars/columns in percent - var overlapPercent = chartType.Overlap / 100D; // Overlap between bars/columns in percent - var slotWidth = yWidth / slotSize; - var clusterWidth = slotWidth * 100 / (100 + chartType.GapWidth); - var step = 1 - overlapPercent; - var barWidth = slotWidth / (1 + (seriesCount - 1) * step + gapPercent); - var halfGap = (barWidth * gapPercent) / 2; - - double yAxisStart; - if (catAx.Axis.Crosses == eCrosses.AutoZero) - { - yAxisStart = valAx.GetPositionInPlotarea(valAx.Min <= 0 ? 0D : valAx.Min, true); - } - else if (catAx.Axis.Crosses == eCrosses.Min) - { - yAxisStart = valAx.GetPositionInPlotarea(valAx.Min, true); - } - else - { - yAxisStart = valAx.GetPositionInPlotarea(valAx.Max, true); - } - - var isStacked = chartType.IsTypeStacked(); - var isStacked100 = chartType.IsTypePercentStacked(); - for (var i = 0; i < valValues.Count; i++) - { - double x; - if (catValues == null || catAx.Axis.AxisType == eAxisType.Cat) - { - if (isColumn) - { - x = (double)i; - } - else - { - x = valValues.Count - i - 1; - } - } - else - { - x = ConvertUtil.GetValueDouble(catValues[i], false, true); - } - - var y = ConvertUtil.GetValueDouble(valValues[i], false, true); - - var rect = new SvgRenderRectItem(ChartRenderer, _svgChart.Plotarea.Rectangle.Bounds); - var yPos = valAx.GetPositionInPlotarea(y); - - if (isColumn) - { - var xPos = catAx.GetPositionInPlotarea(x, true) + halfGap + position * barWidth * step; - rect.Left = xPos; - rect.Width = barWidth; - } - else - { - var xPos = catAx.GetPositionInPlotarea(x, true) + halfGap + (seriesCount - position - 1) * barWidth * step; - rect.Top = xPos; - rect.Height = barWidth; - } - - if (position > 0 && (isStacked || isStacked100)) - { - //Stacked - var pYValues = valSeries[position - 1]; - var pAxisEnd = ConvertUtil.GetValueDouble(pYValues[i]); - var yPrevPos = yAxis.GetPositionInPlotarea(pAxisEnd, false); - if (isColumn) - { - if (y < 0) - { - rect.Top = yPrevPos; - rect.Height = yPos - yPrevPos; - } - else - { - rect.Top = yPos; - rect.Height = yPrevPos - yPos; - } - } - else - { - if (y < 0) - { - rect.Left = yPrevPos; - rect.Width = yPos - yPrevPos; - } - else - { - rect.Left = yPos; - rect.Width = yPrevPos - yPos; - } - } - } - else - { - if (isColumn) - { - if (y < 0) - { - rect.Top = yAxisStart; - rect.Height = yPos - yAxisStart; - } - else - { - rect.Top = yPos; - rect.Height = yAxisStart - yPos; - } - } - else - { - if (y < 0) - { - rect.Left = yPos; - rect.Width = yAxisStart - yPos; - } - else - { - rect.Left = yAxisStart; - rect.Width = yPos - yAxisStart; - } - } - } - rect.SetDrawingPropertiesFill(serie.Fill, chartType.StyleManager.Style.SeriesAxis.FillReference.Color); - rect.SetDrawingPropertiesBorder(serie.Border, chartType.StyleManager.Style.SeriesAxis.BorderReference.Color, true); - rect.SetDrawingPropertiesEffects(serie.Effect); - RenderItems.Add(rect); - } - } - private void GetAxis(ExcelBarChart chartType, out ChartAxisRenderer yAxis, out ChartAxisRenderer xAxis) - { - if (chartType.UseSecondaryAxis) - { - yAxis = _svgChart.SecondVerticalAxis; - xAxis = _svgChart.SecondHorizontalAxis; - if (xAxis.Axis.Deleted && xAxis.Values == null) - { - xAxis = _svgChart.HorizontalAxis; - } - } - else - { - yAxis = _svgChart.VerticalAxis; - xAxis = _svgChart.HorizontalAxis; - } - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/ChartTypeDrawer.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/ChartTypeDrawer.cs deleted file mode 100644 index 428ba8d44b..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/ChartTypeDrawer.cs +++ /dev/null @@ -1,215 +0,0 @@ -using EPPlus.Export.ImageRenderer.Svg.Chart.ChartTypeDrawers; -using EPPlus.Export.ImageRenderer.Utils; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.Drawing.Chart.ChartEx; -using OfficeOpenXml.ExternalReferences; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Finance; -using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; -using OfficeOpenXml.Utils.TypeConversion; -using System; -using System.Collections.Generic; -using System.Linq; -using static OfficeOpenXml.ExcelErrorValue; - -namespace EPPlus.Export.ImageRenderer.Svg.Chart -{ - internal abstract class ChartTypeDrawer : SvgChartObject - { - protected SvgChart _svgChart; - internal protected ExcelChart _chartType; - internal List Trendlines { get; } = new List(); - - internal virtual bool SupportsTrendlines { get { return false; } } - internal virtual bool SupportsErrorBars { get { return false; } } - internal virtual bool SupportsUpDownBars { get { return false; } } - internal virtual bool SupportsDataTable { get { return false; } } - internal ChartTypeDrawer(SvgChart svgChart, ExcelChart chartType) : base(svgChart) - { - _svgChart = svgChart; - _chartType = chartType; - } - internal abstract void DrawSeries(); - protected List LoadSeriesValues(string serieAddressInput, double[] numLiterals, string[] strLiterals) - { - string serieAddress = serieAddressInput; - - //Some addresses are split and within parenthesis - if (serieAddressInput.StartsWith("(")) - { - serieAddress = serieAddressInput.Trim('(', ')'); - } - - List values = new List(); - if (numLiterals != null) - { - values.AddRange(numLiterals.Select(x => (object)x)); - } - else if (strLiterals != null) - { - values.AddRange(strLiterals.Select(x => (object)x)); - } - else - { - if (string.IsNullOrEmpty(serieAddress)) - { - return null; - } - var address = new ExcelAddressBase(serieAddress); - - if (address.Addresses != null && address.Addresses.Count > 1) - { - foreach (var splitAddress in address.Addresses) - { - FillValuesFromAddress(splitAddress, ref values); - } - } - else - { - FillValuesFromAddress(address, ref values); - } - } - return values; - } - - protected void FillValuesFromAddress(ExcelAddressBase address, ref List values) - { - if (address.IsExternal) - { - var wb = Chart.WorkSheet.Workbook; - var extWb = wb.ExternalLinks[address.ExternalReferenceIndex - 1] as ExcelExternalWorkbook; - if (extWb != null) - { - var wsName = address.WorkSheetName; - if (extWb.Package == null) - { - var extWs = extWb.CachedWorksheets[wsName]; - FillExternalValues(extWs, address, ref values); - } - else - { - var ws = extWb.Package.Workbook.Worksheets[wsName]; - FillInternalValues(ws, address, ref values); - } - } - } - else - { - var wsName = address.WorkSheetName; - - if (string.IsNullOrEmpty(wsName)) - { - wsName = Chart.WorkSheet.Name; - } - - var ws = Chart.WorkSheet.Workbook.Worksheets[wsName]; - FillInternalValues(ws, address, ref values); - } - } - - protected void FillExternalValues(ExcelExternalWorksheet extWs, ExcelAddressBase address, ref List values) - { - if (extWs != null) - { - for (int r = address.Start.Row; r <= address.End.Row; r++) - { - for (int c = address.Start.Column; c <= address.End.Column; c++) - { - values.Add(extWs.CellValues[r, c].Value); - } - } - } - } - - private void FillInternalValues(ExcelWorksheet ws, ExcelAddressBase address, ref List values) - { - - if (ws != null) - { - for (int r = address.Start.Row; r <= address.End.Row; r++) - { - for (int c = address.Start.Column; c <= address.End.Column; c++) - { - values.Add(ws.Cells[r, c].Value); - } - } - } - } - - public List RenderItems { get; } = new List(); - internal static List Create(SvgChart svgChart) - { - var drawers = new List(); - foreach (var ct in svgChart.Chart.PlotArea.ChartTypes) - { - switch (ct.ChartType) - { - case eChartType.Line: - case eChartType.LineMarkers: - case eChartType.LineStacked: - case eChartType.LineStacked100: - case eChartType.LineMarkersStacked: - case eChartType.LineMarkersStacked100: - drawers.Add(new LineChartTypeDrawer(svgChart, (ExcelLineChart)ct)); - break; - case eChartType.ColumnClustered: - case eChartType.ColumnStacked: - case eChartType.ColumnStacked100: - case eChartType.BarClustered: - case eChartType.BarStacked: - case eChartType.BarStacked100: - drawers.Add(new BarColumnChartTypeDrawer(svgChart, (ExcelBarChart)ct)); - break; - case eChartType.Pie: - case eChartType.PieExploded: - drawers.Add(new PieChartTypeDrawer(svgChart, (ExcelPieChart)ct)); - break; - default: - throw new NotImplementedException($"No Svg support for Chart type {ct} is implemented."); - } - } - return drawers; - } - internal void SumSeries(List> series) - { - for (var i = 1; i < series.Count; i++) - { - for (var j = 0; j < series[i].Count; j++) - { - series[i][j] = ConvertUtil.GetValueDouble(series[i][j]) + ConvertUtil.GetValueDouble(series[i - 1][j]); - } - } - } - protected void CreateTrendlines(ExcelChart chartType, List> xValues, List> yValues) - { - var serieIndex = 0; - foreach (ExcelChartSerie serie in chartType.Series) - { - if (serie.TrendLines.Count > 0) - { - var xSerie = xValues[serieIndex]; - var ySerie = yValues[serieIndex]; - foreach (var trendline in serie.TrendLines) - { - var tr = new SvgTrendline(_svgChart, trendline, xSerie, ySerie, _chartType, serieIndex); - Trendlines.Add(tr); - } - } - serieIndex++; - } - } - - internal override void AppendRenderItems(List renderItems) - { - renderItems.AddRange(RenderItems); - } - - internal bool IsOnAxis(ExcelChartAxisStandard ax) - { - return _chartType.YAxis==ax || _chartType.XAxis==ax; - } - } - -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/LineChartTypeDrawer.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/LineChartTypeDrawer.cs deleted file mode 100644 index 86ac0c452b..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/LineChartTypeDrawer.cs +++ /dev/null @@ -1,212 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlus.Graphics; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.DigitalSignatures; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using OfficeOpenXml.Utils.TypeConversion; -using System; -using System.Collections.Generic; - -namespace EPPlus.Export.ImageRenderer.Svg.Chart -{ - internal class LineChartTypeDrawer : ChartTypeDrawer - { - List> _xValues, _yValues; - - List serieDataLabels = new List(); - List> dataPointsPerSerie = new List>(); - internal override bool SupportsTrendlines => true; - internal LineChartTypeDrawer(SvgChart svgChart, ExcelLineChart chartType) : base(svgChart, chartType) - { - var isStacked = chartType.IsTypeStacked(); - var isPercentStacked = chartType.IsTypePercentStacked(); - int serCounter = 0; - _xValues = new List>(); - _yValues = new List>(); - foreach (ExcelLineChartSerie serie in chartType.Series) - { - var yValue = LoadSeriesValues(serie.Series, serie.NumberLiteralsY, serie.StringLiteralsY); - var xValue = LoadSeriesValues(serie.XSeries, serie.NumberLiteralsX, serie.StringLiteralsX); - - _xValues.Add(xValue); - _yValues.Add(yValue); - - serCounter++; - } - - if (chartType.IsTypeStacked()) - { - SumSeries(_yValues); - } - else if (chartType.IsTypePercentStacked()) - { - ExcelChartAxisStandard.CalculateStacked100(_yValues); - } - - CreateTrendlines(chartType, _xValues, _yValues); - } - internal override void DrawSeries() - { - var groupItem = new SvgGroupItem(ChartRenderer, _svgChart.Plotarea.Rectangle.Bounds); - RenderItems.Add(groupItem); - - var lct = (ExcelLineChart)_chartType; - for (var i = 0; i < _xValues.Count; i++) - { - var xSerie = _xValues[i]; - var ySerie = _yValues[i]; - var serie = lct.Series[i]; - - if (serie.HasDataLabel) - { - var datalabel = new SvgChartSerieDataLabel(_svgChart, serie.DataLabel, _svgChart.Bounds, serie, xSerie, ySerie, i); - serieDataLabels.Add(datalabel); - } - - - var dataPoints = new List(); - AddLine(_chartType, serie, xSerie, ySerie, dataPoints); - - dataPointsPerSerie.Add(dataPoints); - - if (serie.HasDataLabel) - { - for (int j = 0; j < dataPoints.Count; j++) - { - serieDataLabels[i].SetParentPoint(dataPoints[j], j); - } - } - } - - - //Append Trendline render items. - foreach (var tr in Trendlines) - { - tr.CreateRenderCoordinatesAndDatalabel(); - tr.AppendRenderItems(RenderItems); - } - - RenderItems.Add(new SvgEndGroupItem(ChartRenderer, null)); - - //Datalabels use the chart area as parent as they can be positioned on the entire chart. - - //Add data labels for trendlines after the trendline has been rendered, to ensure they are on top of the line. - foreach (var tr in Trendlines) - { - if (tr.DataLabel != null) - { - tr.DataLabel.AppendRenderItems(RenderItems); - } - } - - //Date series labels - foreach (var dataLabel in serieDataLabels) - { - dataLabel.AppendRenderItems(RenderItems); - } - } - private void SumSeries(List> series) - { - for(var i=1;i < series.Count;i++) - { - for(var j=0;j < series[i].Count;j++) - { - series[i][j] = ConvertUtil.GetValueDouble(series[i][j]) + ConvertUtil.GetValueDouble(series[i-1][j]); - } - } - } - private void AddLine(ExcelChart chartType, ExcelLineChartSerie serie, List xValues, List yValues, List dataPoints) - { - ChartAxisRenderer yAxis, xAxis; - if (chartType.UseSecondaryAxis) - { - yAxis = _svgChart.SecondVerticalAxis; - xAxis = _svgChart.SecondHorizontalAxis; - if(xAxis.Axis.Deleted && xAxis.Values==null) - { - xAxis = _svgChart.HorizontalAxis; - } - } - else - { - yAxis = _svgChart.VerticalAxis; - xAxis = _svgChart.HorizontalAxis; - } - var linePath = new SvgRenderPathItem(ChartRenderer, _svgChart.Plotarea.Rectangle.Bounds); - var coords = new List(); - var markerItems = new List(); - - for (var i = 0; i < yValues.Count; i++) - { - double x; - if (xValues == null || xAxis.Axis.AxisType==eAxisType.Cat) - { - x = (double)i; - } - else - { - x = ConvertUtil.GetValueDouble(xValues[i], false, true); - } - - var y = ConvertUtil.GetValueDouble(yValues[i], false, true); - var xPos = xAxis.GetPositionInPlotarea(x); - var yPos = yAxis.GetPositionInPlotarea(y); - - BoundingBox pt = null; - - if (double.IsNaN(yPos) == false) - { - coords.Add(xPos); - coords.Add(yPos); - - //Log point within chart coordinate system - pt = new BoundingBox(xPos, yPos, 0, 0); - pt.Parent = _svgChart.Plotarea.Bounds; - } - - if (serie.HasMarker() && serie.Marker.Style != eMarkerStyle.None) - { - float mx = (float)xPos; - float my = (float)yPos; - var ls = LineMarkerHelper.GetMarkerItem(_svgChart, serie, mx, my, false); - if ((serie.Marker.Style == eMarkerStyle.Plus || serie.Marker.Style == eMarkerStyle.X || serie.Marker.Style == eMarkerStyle.Star) && - serie.Marker.Fill.IsEmpty == false) - { - markerItems.Add(LineMarkerHelper.GetMarkerBackground(_svgChart, serie, mx, my, false)); - } - markerItems.Add(ls); - - if (pt != null) - { - pt.Width = ls.Bounds.Width; - pt.Height = ls.Bounds.Height; - dataPoints.Add(pt); - } - } - else - { - - if (pt != null) - { - //Default values in excel - pt.Width = 5; - pt.Height = 5; - dataPoints.Add(pt); - } - } - } - - linePath.Commands.Add(new EPPlusImageRenderer.PathCommands(PathCommandType.Move, linePath, coords.ToArray())); - linePath.SetDrawingPropertiesBorder(serie.Border, chartType.StyleManager.Style.SeriesLine.BorderReference.Color, true); - linePath.SetDrawingPropertiesEffects(serie.Effect); - linePath.FillColor = "none"; //No fill for line - linePath.StrokeMiterLimit = 4; //A much higher value of the miter limit, might cause the "spike" to get beyond the data point on the vertical scale.. - linePath.LineJoin = SvgLineJoin.Round; - RenderItems.Add(linePath); - RenderItems.AddRange(markerItems); - } - } - -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/PieChartTypeDrawer.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/PieChartTypeDrawer.cs deleted file mode 100644 index 90a849e3ac..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/PieChartTypeDrawer.cs +++ /dev/null @@ -1,286 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlus.Export.ImageRenderer.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.DigitalSignatures; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.Utils.TypeConversion; -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -namespace EPPlus.Export.ImageRenderer.Svg.Chart.ChartTypeDrawers -{ - internal class PieChartTypeDrawer : ChartTypeDrawer - { - List serieDataLabels = new List(); - List catValues = new List(); - List valValues = new List(); - List _serieValuesAsDoubles = new List(); - List> dataPointsPerSerie = new List>(); - double _totalOfSerieValues = 0; - - List Slices = new List(); - - SvgGroupItemNew _groupItem; - - double _startDegrees = -90d; - int _pieExplosionPercent = 0; - - double _sliceScaleFactor = 1.0; - int _serCounter = 0; - - /// - /// Radius in points - /// - double _radius; - - Point _circleCenter; - - public PieChartTypeDrawer(SvgChart chart, ExcelPieChart chartType) : base(chart, chartType) - { - //Moved to draw series - } - - void RenderDebugEllipse() - { - var circ = new SvgRenderEllipseItem(ChartRenderer, _svgChart.Plotarea.Rectangle.Bounds); - - circ.Bounds.Left = _circleCenter.Left; - circ.Bounds.Top = _circleCenter.Top; - - circ.Rx = _radius; - circ.Ry = _radius; - - circ.Cx = _circleCenter.Left; - circ.Cy = _circleCenter.Top; - - circ.FillColor = "transparent"; - circ.FillOpacity = 0.3d; - circ.BorderColor = "purple"; - circ.BorderWidth = 10; - - _groupItem.AddChildItem(circ); - } - - Coordinate CalculateLocalPointOnCircle(double degrees) - { - var angleRadians = MConverter.DegreesToRadians(degrees); - - var xPoint = _circleCenter.Left + (_radius * Math.Cos(angleRadians)); - var yPoint = _circleCenter.Top + (_radius * Math.Sin(angleRadians)); - - return new Coordinate(xPoint, yPoint); - } - - void InitializeSlices() - { - //The angle of the previous slice - //(or the 90 degree offset in the first slice) - var prevDegrees = _startDegrees; - - for (int i = 0; i < valValues.Count; i++) - { //Calculate how many percent of the pie this slice is - var valPercent = _serieValuesAsDoubles[i] / _totalOfSerieValues; - //Create and add slice - SvgPieSlice slice = new SvgPieSlice(ChartRenderer, _groupItem.Bounds, _circleCenter, _radius, valPercent, prevDegrees); - Slices.Add(slice); - - //Next slice will need to be calculated starting from the degrees of this slice - prevDegrees = slice.Degrees + prevDegrees; - } - } - - void CalculateLocalCenterAndRadius() - { - _circleCenter = new Point(); - _circleCenter.Parent = _groupItem.TranslationOffset; - _circleCenter.Left = _svgChart.Plotarea.Rectangle.Bounds.Width / 2; - _circleCenter.Top = _svgChart.Plotarea.Rectangle.Bounds.Height / 2; - - _groupItem.RotationPoint = _circleCenter; - - _radius = Math.Min(_circleCenter.Left, _circleCenter.Top); - } - - void LoadSeriesValues(ExcelPieChart chartType) - { - //Load series values - foreach (ExcelPieChartSerie serie in chartType.Series) - { - //Excel allows further series on a pie chart but ignores them for visualization - if(_serCounter == 0) - { - - valValues = LoadSeriesValues(serie.Series, serie.NumberLiteralsY, serie.StringLiteralsY); - catValues = LoadSeriesValues(serie.XSeries, serie.NumberLiteralsX, serie.StringLiteralsX); - - //Pie explosion - _pieExplosionPercent = serie.Explosion == int.MinValue ? 0 : serie.Explosion; - - //Add Datalabel - if (serie.HasDataLabel) - { - var datalabel = new SvgChartSerieDataLabel(_svgChart, serie.DataLabel, _svgChart.Bounds, serie, catValues, valValues, _serCounter); - serieDataLabels.Add(datalabel); - } - } - - _serCounter++; - } - - ConvertSerieValuesToDoubles(); - } - - void ConvertSerieValuesToDoubles() - { - for (int i = 0; i < valValues.Count; i++) - { - var origValue = valValues[i]; - var dValue = ConvertUtil.GetValueDouble(origValue, false, true); - if (double.IsNaN(dValue)) - { - //Ignore values that are NAN. Possibly we should throw here but Excel simply seems to skip it. - continue; - } - _serieValuesAsDoubles.Add(dValue); - _totalOfSerieValues += _serieValuesAsDoubles[i]; - } - } - - private void UpdateSlice(ExcelPieChart chartType, ExcelPieChartSerie serie, int seriesCount, int position) - { - var dataPoint = serie.DataPoints[position]; - - Slices[position].ImportPathData( - _svgChart.Plotarea.Rectangle.Bounds, _svgChart.Bounds, - _sliceScaleFactor, dataPoint.Explosion, _pieExplosionPercent, position); - - Slices[position].ImportStlyeInfo(dataPoint, chartType); - Slices[position].AppendGroupItem(_groupItem); - } - - internal override void DrawSeries() - { - _groupItem = new SvgGroupItemNew(ChartRenderer, 0, 0); - _groupItem.TranslationOffset.Left = _svgChart.Plotarea.Rectangle.Left; - _groupItem.TranslationOffset.Top = _svgChart.Plotarea.Rectangle.Top; - - Bounds.Name = "ChartDrawer"; - - _groupItem.Bounds.Name = "OuterGroupChartDrawer"; - - //_groupitem.bounds.parent = _groupitem.translationoffset; - - var chartType = (ExcelPieChart)_chartType; - - //Read and set Starting angle offset as a rotation on the container - //This way no rotation messes with the other calculations - var angleOffset = double.IsNaN(chartType.FirstSliceAngle) ? 0 : chartType.FirstSliceAngle; - _groupItem.Rotation = angleOffset; - - LoadSeriesValues(chartType); - CalculateLocalCenterAndRadius(); - InitializeSlices(); - - //How much to scale each slice due to pie explosion - //Essentially we start at 100% (100/100) - //And then scale down by adding the pie explosionPercent (0-400) - // 100/200 -> 0.5, 100/400 -> 0.2 etc. - _sliceScaleFactor = 100d / (_pieExplosionPercent + 100d); - - if (_sliceScaleFactor != 1) - { - //Small adjustment. Unsure why but closer results - //Could be Excel pixel rounding or 2px border buffer - _sliceScaleFactor += 0.02d; - } - - int count = 0; - if (catValues != null) - { - if (valValues != null) - { - count = Math.Min(catValues.Count, valValues.Count); - } - else - { - count = catValues.Count; - } - } - else - { - if (valValues != null) - { - count = valValues.Count; - } - } - - for (int i = 0; i < chartType.Series.Count; i++) - { - var serie = (ExcelPieChartSerie)chartType.Series[i]; - - List DataLabelGlobalOriginPoints = new(); - List VectorsCenterToMidPointPerSlice = new(); - - //Excel ignores series beyond the first for pie chart visualization - if (i == 0) - { - for (var j = 0; j < count; j++) - { - //Update the initialized slice with path, style and group data - UpdateSlice(chartType, serie, count, j); - - if(serie.HasDataLabel) - { - //Get the inner item local coordinates - var itemGroup = Slices[j].GetInnerItemGroup(); - var outer = Slices[j].GetOuterMidpointInGlobalCoords(); - ////Get the relevant point local coordinates (to the above) - //var slicePos = Slices[j].GetInnerGroupTransformOrigin(); - - //Get the global position of the inner items (innerGroup the parent of itemGroup has already had its position set correctly) - var dlblBounds = new BoundingBox(itemGroup.Bounds.GlobalLeft, itemGroup.Bounds.GlobalTop, Bounds.Width, Bounds.Height); - ////Add the origin point position - //dlblBounds.Left += slicePos.X; - //dlblBounds.Top += slicePos.Y; - - ////we now have the start point of the slice in global coords without the translation - ////Add the same translation - //dlblBounds.Translate() - - //var vector = Slices[j].GetWholeVectorCenterToMid(); - serieDataLabels[i].SetParentPoint(dlblBounds, j); - } - ////Add datalabel to slice - //if (serie.HasDataLabel) - //{ - - // //DataLabelGlobalOriginPoints.Add(); - // //serieDataLabels[i].AppendRenderItems(Slices[j].); - // //var slicePos = Slices[j].GetInnerGroupTransformOrigin(); - // //var bounds = Slices[j].GetInnerGroupBounds(); - // //BoundingBox dp = new BoundingBox(slicePos.X, slicePos.Y, 0, 0); - - //serieDataLabels[i].SetParentPoint(Slices[j].GetInnerGroupBounds(), j); - // //serieDataLabels[i].SetParentVector(Slices[j].GetInnerGroupBounds(), j, Slices[j].GetWholeVectorCenterToMid()); - // //serieDataLabels[i].SetParentShape(Slices[j].ExtremePoints, dp, j); - //} - } - } - } - - //RenderDebugEllipse(); - - RenderItems.Add(_groupItem); - //Date series labels - foreach (var dataLabel in serieDataLabels) - { - dataLabel.AppendRenderItems(RenderItems); - } - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/Trendlines/SvgTrendline.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/Trendlines/SvgTrendline.cs deleted file mode 100644 index 7d06bad998..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/Trendlines/SvgTrendline.cs +++ /dev/null @@ -1,746 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 22/10/2022 EPPlus Software AB EPPlus v6 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.DigitalSignatures; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Statistical; -using OfficeOpenXml.Utils.TypeConversion; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -namespace EPPlus.Export.ImageRenderer.Svg.Chart -{ - internal class SvgTrendline : SvgChartObject - { - private ExcelChartTrendline _trendline; - private double[] _ySerie; - private List _xSerie; - private SvgChart _svgChart; - private ExcelChart _chartType; - private bool _useSecondaryAxis; - private int _serieCount, _seriePos; - public SvgTrendline(SvgChart svgChart, ExcelChartTrendline trendline, List xSerie, List ySerie, ExcelChart chartType, int seriePos) : base(svgChart) - { - _svgChart = svgChart; - _chartType = chartType; - _trendline = trendline; - _xSerie = xSerie; - _ySerie = ySerie.Select(y => ConvertUtil.GetValueDouble(y)).ToArray(); - _useSecondaryAxis = chartType.UseSecondaryAxis; - _serieCount = _chartType.Series.Count; - _seriePos = seriePos; - double m, b; - switch (trendline.Type) - { - case eTrendLine.Linear: - CalculateLinear(); - Coordinates.Add(new Coordinate(0, GetLinearValueAtPosition(1))); - Coordinates.Add(new Coordinate(_xSerie.Count-1, GetLinearValueAtPosition(_xSerie.Count))); - break; - case eTrendLine.Exponential: - CalculateExponential(); - m = Coefficients[0]; - b = Coefficients[1]; - CreateCoordinates(x => b * Math.Exp(m * x)); - break; - case eTrendLine.Logarithmic: - CalculateLogarithmic(); - m = Coefficients[0]; - b = Coefficients[1]; - CreateCoordinates(x => m * Math.Log(x) + b); - break; - case eTrendLine.Polynomial: - CalculatePolynomial(); - CreateCoordinates(x=> PredictPolynomial(x)); - break; - case eTrendLine.Power: - CalculatePower(); - - m = Coefficients[0]; - b = Coefficients[1]; - CreateCoordinates(x => b * Math.Pow(x, m)); - break; - case eTrendLine.MovingAverage: - CalculateMoveingAverage(); - - for (int i = ((int)_trendline.Period) - 1; i < _xSerie.Count; i++) - { - var x = i; - var y = GetMonthlyAverageAtPosition(i); - - Coordinates.Add(new Coordinate(x, y)); - } - - - break; - default: - //Should not happen unless new trendline types arrive. - throw new NotImplementedException("Trendline type not implemented."); - } - } - - private void CreateDatalabel() - { - if(_trendline.DisplayEquation==false && _trendline.DisplayRSquaredValue==false) - { - return; - } - //Display the label for the trendline with equation and R² value. - var lbl = _trendline.Label; - var coord = RenderCoordinates; - var x = _svgChart.Plotarea.Rectangle.Left + coord[coord.Length - 2]; - var y = _svgChart.Plotarea.Rectangle.Top + coord[coord.Length - 1]; - double width = 0, height = 0; - - if (_trendline.Label.Layout.HasLayout) - { - var mlRect = GetRectFromManualLayout(_svgChart, _trendline.Label.Layout); - x += mlRect.Left; - y += mlRect.Top; - if (lbl.Layout.ManualLayout.Width.HasValue && lbl.Layout.ManualLayout.Height.HasValue) - { - width = mlRect.Width; - height = mlRect.Height; - } - } - - if (width > 0 && height > 0) - { - DataLabel = new SvgTextBox(_svgChart, _svgChart.ChartArea.Rectangle.Bounds, x, y, width, height); - DataLabel.TextBody.AutoSize = false; - } - else - { - DataLabel = new SvgTextBox(_svgChart, _svgChart.ChartArea.Rectangle.Bounds, _svgChart.ChartArea.Rectangle.Bounds); - if (x > 0) - { - DataLabel.Left = x; - } - if (y > 0) - { - DataLabel.Top = y; - } - DataLabel.TextBody.AutoSize = true; - } - //DataLabel.ImportTextBody(lbl.TextBody, true, OfficeOpenXml.Style.ExcelHorizontalAlignment.Center); - var labelText = ""; - if (_trendline.DisplayEquation) - { - labelText += Formula; - } - if (_trendline.DisplayRSquaredValue) - { - if (labelText.Length > 0) - { - labelText += Environment.NewLine; - } - labelText += RSquare; - } - DataLabel.ImportParagraph(lbl.TextBody.Paragraphs[0], 0, labelText); - //DataLabel.AddText(0, labelText); - //DataLabel.TextBody.Paragraphs[0].AddText(labelText, 0); - DataLabel.LeftMargin = DataLabel.RightMargin = 4; - DataLabel.TopMargin = DataLabel.BottomMargin = 2; - - //Set datalabel position. - if(DataLabel.Left - (DataLabel.Width + 5) > _svgChart.Bounds.Right) - { - DataLabel.Left = _svgChart.Bounds.Right - DataLabel.Width; - } - else - { - DataLabel.Left -= (DataLabel.Width + 5); - } - - if(DataLabel.Left<0) - { - DataLabel.Left = 0; - } - - if(DataLabel.Top - DataLabel.Height / 2 > _svgChart.Bounds.Bottom) - { - DataLabel.Top = _svgChart.Bounds.Bottom - DataLabel.Height / 2; - } - else - { - DataLabel.Top -= DataLabel.Height / 2; - } - - if (DataLabel.Top < 0) - { - DataLabel.Top = 0; - } - - - DataLabel.Rectangle.SetDrawingPropertiesFill(_trendline.Label.Fill, _svgChart.Chart.StyleManager.Style.TrendlineLabel.FillReference.Color); - DataLabel.Rectangle.SetDrawingPropertiesBorder(_trendline.Label.Border, _svgChart.Chart.StyleManager.Style.TrendlineLabel.BorderReference.Color, true, _trendline.Label.Border.Width); - DataLabel.Rectangle.SetDrawingPropertiesEffects(_trendline.Label.Effect); - } - - private void CalculateLinear() - { - var n = _xSerie.Count; - var sumX = n * (n + 1) / 2; - var sumY = _ySerie.Sum(y => y); - var sumX2 = n * (n + 1) * (2 * n + 1) / 6; - var sumXY = 0D; - - double slope, intercept; - if (double.IsNaN(_trendline.Intercept)) - { - for (int i = 0; i < _ySerie.Length; i++) - { - sumXY += _ySerie[i] * (i + 1); - } - - //Slope - slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX); - //Intercept - intercept = (sumY - slope * sumX) / n; - } - else - { - intercept = _trendline.Intercept; - for (int i = 0; i < _ySerie.Length; i++) - { - sumXY += (_ySerie[i] - intercept) * (i + 1); - } - slope = sumXY / sumX2; - } - - //var r2 = Math.Pow(Pearson.PearsonImpl(_ySerie.Cast(), GetLinearSerie(slope, intercept)), 2); - var r2 = CalculateRSquared(x => slope * x + intercept, _ySerie, _trendline.Intercept); - Coefficients = [slope, intercept]; - Formula = $"y={slope:G5}x{(GetValueAndSignSuppressZero(intercept))}"; - RSquare = $"R²={r2:N4}"; - } - - private string GetValueAndSignSuppressZero(double value) - { - if(value>0) - { - return $"+{value.ToString("G5")}"; - } - else if(value < 0) - { - return $"-{value.ToString("G5")}"; - } - return ""; - } - - private void CalculateExponential() - { - var n = _xSerie.Count; - var sumX = n * (n + 1) / 2; - var sumLnY = _ySerie.Sum(y => Math.Log(y)); - var sumX2 = n * (n + 1) * (2 * n + 1) / 6; - - var sumXLnY = 0D; - - //Slope - double slope, intercept; - if(double.IsNaN(_trendline.Intercept)) - { - for (var i = 0; i < _ySerie.Length; i++) - { - sumXLnY += Math.Log(_ySerie[i]) * (i + 1); - } - - slope = (n * sumXLnY - sumX * sumLnY) / (n * sumX2 - sumX * sumX); - //Intercept - intercept = Math.Pow(Math.E, (sumLnY - slope * sumX) / n); - } - else - { - intercept = _trendline.Intercept; - var logIntercept = Math.Log(intercept); - for (var i = 0; i < _ySerie.Length; i++) - { - sumXLnY += (Math.Log(_ySerie[i]) - logIntercept) * (i + 1); - } - - slope = sumXLnY / sumX2; - } - - - var r2 = Math.Pow(Pearson.PearsonImpl(_ySerie.Cast(), GetExponentialSerie(slope, intercept)), 2); - Coefficients = [slope, intercept]; - Formula = $"y={intercept:G5}|ss:e{slope:G3}|"; - RSquare = $"R²={r2:N4}"; - } - private void CalculateLogarithmic() - { - var n = _xSerie.Count; - var logSerie = _xSerie.Select(x => Math.Log(_xSerie.IndexOf(x) + 1)).ToList(); - var sumLnX = logSerie.Sum(x => x); - var sumLnX2 = logSerie.Sum(x => x * x); - var sumY = _ySerie.Sum(x => ConvertUtil.GetValueDouble(x)); - - var sumLnXY = 0D;// _ySerie.Sum(x => ConvertUtil.GetValueDouble(x) * Math.Log(_ySerie.IndexOf(x) + 1)); - for (int i = 0; i < _ySerie.Length; i++) - { - sumLnXY += _ySerie[i] * logSerie[i]; - } - - - - //Slope - var slope = (n * sumLnXY - sumLnX * sumY) / (n * sumLnX2 - sumLnX * sumLnX); - ////Intercept - var intercept = (sumY - slope * sumLnX) / n; - - Coefficients = [slope, intercept]; - - var r2 = CalculateRSquared(x => slope * Math.Log(x) + intercept, _ySerie, _trendline.Intercept); - Formula = $"y={slope:G5}ln(x)+{intercept:G5}"; - RSquare = $"R²={r2:N4}"; - } - public void CalculatePolynomial() - { - - /* - Σy = c·n + b·Σx + a·Σx² - Σxy = c·Σx + b·Σx² + a·Σx³ - Σx²y = c·Σx² + b·Σx³ + a·Σx⁴ - Σx³y = c·Σx³ + b·Σx⁴ + a·Σx⁵ - ... - */ - - var isForced = double.IsNaN(_trendline.Intercept) == false; - int n = _ySerie.Length; - var order = Math.Min((int)_trendline.Order, n-1); - int coeffCount = order + (isForced ? 0 : 1); - - // Step 1: Build sums - double[] sumX = new double[2 * order + 1]; - double[] sumXY = new double[order + 1]; - - if (isForced) - { - //Todo:Add intercept to the formula and adjust the y values accordingly - var intercept = _trendline.Intercept; - double[] yAdj = new double[n]; - for (int i = 0; i < n; i++) - { - yAdj[i] = _ySerie[i] - intercept; - } - - for (int i = 0; i < n; i++) - { - double xPow = 1.0; - for (int k = 0; k <= 2 * order; k++) - { - sumX[k] += xPow; - if (k <= order) - sumXY[k] += xPow * yAdj[i]; - xPow *= i + 1; - } - } - } - else - { - for (int i = 0; i < n; i++) - { - double xPow = 1.0; - for (int k = 0; k <= 2 * order; k++) - { - sumX[k] += xPow; - if (k <= order) - sumXY[k] += xPow * _ySerie[i]; - xPow *= i + 1; - } - } - } - - // Step 2: Build augmented matrix - double[,] matrix = new double[coeffCount, coeffCount + 1]; - - int offset = isForced ? 2 : 0; - for (int row = 0; row < coeffCount; row++) - { - for (int col = 0; col < coeffCount; col++) - matrix[row, col] = sumX[row + col + offset]; - matrix[row, coeffCount] = sumXY[row + (offset / 2)]; - } - - // Step 3: Gaussian elimination with partial pivoting - for (int i = 0; i < coeffCount; i++) - { - // Find best pivot - int maxRow = i; - for (int k = i + 1; k < coeffCount; k++) - { - if (Math.Abs(matrix[k, i]) > Math.Abs(matrix[maxRow, i])) - maxRow = k; - } - - // Swap rows - for (int k = 0; k <= coeffCount; k++) - { - double temp = matrix[i, k]; - matrix[i, k] = matrix[maxRow, k]; - matrix[maxRow, k] = temp; - } - - // Divide pivot row - double pivot = matrix[i, i]; - for (int k = 0; k <= coeffCount; k++) - matrix[i, k] /= pivot; - - // Eliminate column from other rows - for (int j = 0; j < coeffCount; j++) - { - if (j != i) - { - double factor = matrix[j, i]; - for (int k = 0; k <= coeffCount; k++) - matrix[j, k] -= factor * matrix[i, k]; - } - } - } - - // Step 4: Extract coefficients - Coefficients = new double[order+1]; - if (isForced) - { - Coefficients[0] = _trendline.Intercept; - for (int i = 0; i < coeffCount; i++) - { - Coefficients[i+1] = matrix[i, coeffCount]; - } - } - else - { - for (int i = 0; i < coeffCount; i++) - { - Coefficients[i] = matrix[i, coeffCount]; - } - } - Formula = "y=" + GetPolynormFormula(); - var r2 = CalculateRSquared(x => PredictLinear(x), _ySerie, _trendline.Intercept); - RSquare = $"R²={r2:N4}"; - } - - private void CalculatePower() - { - var n = _xSerie.Count; - var lnSerie = _xSerie.Select((x, i) => Math.Log(i + 1)); - var sumLnX = lnSerie.Sum(x => x); - var sumLnX2 = lnSerie.Sum(x => x*x); - var sumLnY = _ySerie.Sum(y => Math.Log(y)); - //double sumLnXLnY = _ySerie.Sum(y => Math.Log(ConvertUtil.GetValueDouble(y)) * Math.Log(_ySerie.IndexOf(y) + 1)); - double sumLnXLnY = 0; - for (int i=0;i < _ySerie.Length;i++) - { - sumLnXLnY += Math.Log(_ySerie[i]) * Math.Log(i + 1); - } - - var slope = (n * sumLnXLnY - sumLnX * sumLnY) / (n*sumLnX2 - sumLnX * sumLnX); - var intercept = Math.Pow(Math.E, (sumLnY - slope * sumLnX) / n); - Coefficients = [slope, intercept]; - - Formula = $"y={intercept:G5}x|ss:{slope:G3}|"; - var ylogSerie = _ySerie.Select(y => Math.Log(y)).ToArray(); - var r2 = CalculateRSquaredPearson(x => intercept * Math.Pow(x, slope), _ySerie); - RSquare = $"R²={r2:N4}"; - } - - private void CalculateMoveingAverage() - { - int n = _ySerie.Length; - double[] result = new double[n]; - var period = (int)(double.IsNaN(_trendline.Period) || _trendline.Period < 2 ? 2 : _trendline.Period); - for (int i = period - 1; i < n; i++) - { - double sum = 0; - for (int j = 0; j < period; j++) - sum += _ySerie[i - j]; - result[i] = sum / period; - } - - Coefficients = result; - - Formula = ""; - RSquare = ""; - } - - private string GetPolynormFormula() - { - var sb = new StringBuilder(Coefficients[0].ToString("G5")); - for (var i = 1; i < Coefficients.Length; i++) - { - if (Coefficients[i-1]>=0) - { - sb.Insert(0, "+"); - } - else - { - sb.Insert(0, "-"); - } - - if(i < 2) - { - sb.Insert(0, $"{Math.Abs(Coefficients[i]):G5}x"); - } - else - { - sb.Insert(0, $"{Math.Abs(Coefficients[i]):G5}x|ss:{i}|"); - } - } - if (Coefficients[Coefficients.Length - 1] < 0) - { - sb.Insert(0, "-"); - } - - return sb.ToString(); - } - - // Predict a y value for a given x - public double PredictLinear(double x) - { - double y = 0; - double xPow = 1.0; - for (int i = 0; i < Coefficients.Length; i++) - { - y += Coefficients[i] * xPow; - xPow *= x; - } - return y; - } - // Predict a y value for a given x - public double PredictPolynomial(double x) - { - double y = 0; - double xPow = 1.0; - for (int i = 0; i < Coefficients.Length; i++) - { - y += Coefficients[i] * xPow; - xPow *= x; - } - return y; - } - private IEnumerable GetExponentialSerie(double m, double b) - { - var l = new List() { b }; - for (var i = 1; i < _xSerie.Count; i++) - { - l.Add(b * Math.Pow(Math.E, m * i)); - } - return l; - } - public double CalculateRSquared(Func predictFunc, double[] serieY, double forcedIntercept) - { - int n = serieY.Length; - if (!double.IsNaN(forcedIntercept)) - { - // Forced intercept: use squared Pearson correlation - double meanY = serieY.Average(); - double[] predicted = new double[n]; - for (int i = 0; i < n; i++) - predicted[i] = predictFunc(i + 1); - double meanP = predicted.Average(); - - double sumYP = 0, sumY2 = 0, sumP2 = 0; - for (int i = 0; i < n; i++) - { - double dy = serieY[i] - meanY; - double dp = predicted[i] - meanP; - sumYP += dy * dp; - sumY2 += dy * dy; - sumP2 += dp * dp; - } - - double r = sumYP / Math.Sqrt(sumY2 * sumP2); - return r * r; - } - else - { - // Standard R² - double meanY = serieY.Average(); - double ssRes = 0, ssTot = 0; - for (int i = 0; i < n; i++) - { - double residual = serieY[i] - predictFunc(i + 1); - double deviation = serieY[i] - meanY; - ssRes += residual * residual; - ssTot += deviation * deviation; - } - return 1.0 - (ssRes / ssTot); - } - } - public double CalculateRSquaredPearson(Func predictFunc, double[] serieY) - { - int n = serieY.Length; - double meanA = serieY.Average(); - - double[] predicted = new double[n]; - for (int i = 0; i < n; i++) - predicted[i] = predictFunc(i + 1); - double meanP = predicted.Average(); - - double sumAP = 0, sumA2 = 0, sumP2 = 0; - for (int i = 0; i < n; i++) - { - double da = serieY[i] - meanA; - double dp = predicted[i] - meanP; - sumAP += da * dp; - sumA2 += da * da; - sumP2 += dp * dp; - } - - double r = sumAP / Math.Sqrt(sumA2 * sumP2); - return r * r; - } - public double[] Coefficients {get;set;} - public string Formula { get; set; } - public string RSquare { get; set; } - public SvgTextBox DataLabel { get; set; } - public override string ToString() - { - return _trendline.Type + "," + Formula + "," + RSquare; - } - internal void CreateRenderCoordinatesAndDatalabel() - { - CreateRenderCoordinates(); - CreateDatalabel(); - } - internal override void AppendRenderItems(List renderItems) - { - var pathItem = new SvgRenderPathItem(_svgChart, _svgChart.Plotarea.Rectangle.Bounds); - pathItem.Commands.Add(new EPPlusImageRenderer.PathCommands(PathCommandType.Move, pathItem, RenderCoordinates)); - pathItem.FillColor = "none"; - pathItem.SetDrawingPropertiesBorder(_trendline.Border, _svgChart.Chart.StyleManager.Style.Trendline.BorderReference.Color, true, _trendline.Border.Width); - pathItem.SetDrawingPropertiesEffects(_trendline.Effect); - renderItems.Add(pathItem); - } - - private void CreateCoordinates(Func predictPoint) - { - var x1 = 0; - var x2 = _xSerie.Count - 1; - - //We aim for 1 line per point for the trendline. - var diff = (x2 - x1); - var inc = diff / GetXInc(_svgChart.Bounds.Width); - double y; - for (double d = x1; d < x2; d += inc) - { - var x = (x2) * (d - x1) / diff; - y = predictPoint(x + 1); - Coordinates.Add(new Coordinate(x,y)); - } - - y = predictPoint(x2 + 1); - Coordinates.Add(new Coordinate(x2, y)); - } - private void CreateRenderCoordinates() - { - var isBar = _chartType.IsTypeBar(); - var isLine = _chartType.IsTypeLine(); - ChartAxisRenderer catAxis, valAxis; - if(isBar) - { - valAxis = _useSecondaryAxis ? _svgChart.SecondHorizontalAxis : _svgChart.HorizontalAxis; - catAxis = _useSecondaryAxis ? _svgChart.SecondVerticalAxis : _svgChart.VerticalAxis; - } - else - { - catAxis = _useSecondaryAxis ? _svgChart.SecondHorizontalAxis : _svgChart.HorizontalAxis; - valAxis = _useSecondaryAxis ? _svgChart.SecondVerticalAxis : _svgChart.VerticalAxis; - } - - var pa = _svgChart.Plotarea; - var coordinates=new List(); - for (var i=0;i _ySerie.Length ? _xSerie.Count: _ySerie.Length); - var ct = (ExcelBarChart) _chartType; - var yWidth = (isBar ? _svgChart.Plotarea.Rectangle.Height : _svgChart.Plotarea.Rectangle.Width); - var slotSize = valAxis.Values.Count; - var gapPercent = ct.GapWidth / 100D; // Gap width between bars/columns in percent - var overlapPercent = ct.Overlap / 100D; // Overlap between bars/columns in percent - var slotWidth = yWidth / slotSize; - var clusterWidth = slotWidth * 100 / (100 + ct.GapWidth); - var step = 1 - overlapPercent; - var barWidth = slotWidth / (1 + (count - 1) * step + gapPercent); - var halfGap = (barWidth * gapPercent) / 2; - if (isBar) - { - - coordinates.Add(valAxis.GetPositionInPlotarea(Coordinates[i].Y)); - coordinates.Add(catAxis.GetPositionInPlotarea(Coordinates[Coordinates.Count - 1].X - Coordinates[i].X) + halfGap + (_serieCount - _seriePos - 1) * barWidth * step); - } - else - { - coordinates.Add(catAxis.GetPositionInPlotarea(Coordinates[i].X)); - coordinates.Add(valAxis.GetPositionInPlotarea(Coordinates[i].Y)); - } - } - } - RenderCoordinates = coordinates.ToArray(); - } - - //Get the incremental x value for the trendline points based on the distance between the start and end point of the trendline. The goal is to have approximately 3 point per data point in the trendline. - private double GetXInc(double n) - { - int k = (int)Math.Round(Math.Log(n)/ Math.Log(3) - 1); - k = Math.Max(k, 0); - return n / Math.Pow(2, k); - } - internal List Coordinates { get; set; } = new List(); - internal double[] RenderCoordinates { get; set; } - List _ma = null; - private double GetMonthlyAverageAtPosition(double x) - { - if (_ma == null) - { - CalcMa(); - } - - int ix = (int)(x - _trendline.Period + 1); - return _ma[ix]; - } - - private void CalcMa() - { - _ma= new List(); - double sum = 0; - for (int i=0;i < _ySerie.Length;i++) - { - sum += _ySerie[i]; - if (i >= _trendline.Period-1) - { - - _ma.Add(sum / (i+1)); - } - } - } - - private double GetLinearValueAtPosition(int x) - { - return Coefficients[1] + Coefficients[0] * x; - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/Trendlines/SvgTrendlineDataLabel.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/Trendlines/SvgTrendlineDataLabel.cs deleted file mode 100644 index 2179533b59..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/ChartTypeDrawers/Trendlines/SvgTrendlineDataLabel.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -namespace EPPlus.Export.ImageRenderer.Svg.Chart.ChartTypeDrawers.Trendlines -{ - internal class SvgTrendlineDataLabel - { - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgAxisTextBoxes.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgAxisTextBoxes.cs deleted file mode 100644 index 281f9cc81c..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgAxisTextBoxes.cs +++ /dev/null @@ -1,43 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlusImageRenderer.RenderItems; -using System.Collections.Generic; - -namespace EPPlusImageRenderer.Svg -{ - internal class SvgAxisTextBoxes : SvgChartObject - { - internal SvgAxisTextBoxes(EPPlusImageRenderer.DrawingChart chart) : base(chart) - { - } - - internal List TextBoxes - { - get; - set; - }=new List(); - - internal override void AppendRenderItems(List renderItems) - { - if (TextBoxes != null && TextBoxes.Count > 0) - { - foreach (var tb in TextBoxes) - { - tb.AppendRenderItems(renderItems); - } - } - - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChart.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChart.cs deleted file mode 100644 index 0d468e572d..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChart.cs +++ /dev/null @@ -1,339 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlus.Export.ImageRenderer.Svg.Chart; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Utils; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.Export.HtmlExport.Exporters.Internal; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using OfficeOpenXml.Interfaces.Drawing.Text; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Runtime.InteropServices; -using System.Text; - -namespace EPPlusImageRenderer.Svg -{ - internal class SvgChart : EPPlusImageRenderer.DrawingChart - { - public SvgChart(ExcelChart chart) : base(chart) - { - SetChartArea(); - - if(chart.HasTitle && chart.Series.Count > 0) - { - Title = new ChartTitleRenderer(this, (ExcelChartTitleStandard)chart.Title, "Chart Title"); - } - else - { - Title = null; - } - - //We need to create the plotarea before the legend and axes, as the trendlines can affect the value axis and should be rendererd in the legend. - Plotarea = new SvgChartPlotarea(this); - Plotarea.ChartTypeDrawers = ChartTypeDrawer.Create(this); - - if (chart.HasLegend) - { - Legend = new SvgChartLegend(this); - } - else - { - Legend = null; - } - - if (Chart.Axis.Length != 0) - { - HorizontalAxis = GetAxis(false); - VerticalAxis = GetAxis(true); - } - - if(chart.Axis.Length > 2) - { - SecondVerticalAxis = GetAxis(true, 2); - SecondHorizontalAxis = GetAxis(false, 2); - } - - Plotarea.SetPlotAreaRectangle(this); - - //As we need the plotarea dimensions to calculate the axis positions we need to set the axis positions after creating the plotarea. - SetAxisPositionsFromPlotarea(this); - - Plotarea.DrawSeries(); - } - - private ChartAxisRenderer GetAxis(bool vertical, int offset=0) - { - var axis = (ExcelChartAxisStandard)Chart.Axis[offset]; - if(axis.IsVertical==vertical) - { - return new ChartAxisRenderer(this, axis); - } - else if(Chart.Axis.Length > offset + 1) - { - axis = (ExcelChartAxisStandard)Chart.Axis[offset + 1]; - if(axis.IsVertical==vertical) - { - return new ChartAxisRenderer(this, axis); - } - } - return null; - } - - private void SetAxisPositionsFromPlotarea(SvgChart sc) - { - if(Legend !=null && (sc.Chart.Legend.Position==eLegendPosition.Left || sc.Chart.Legend.Position == eLegendPosition.Right)) - { - Legend.Rectangle.Top = sc.Plotarea.Rectangle.Top + (sc.Plotarea.Rectangle.Height / 2) - (Legend.Rectangle.Height / 2); - } - if(VerticalAxis != null) - { - PlaceVerticalAxis(sc, VerticalAxis); - VerticalAxis.AddTickmarksAndValues(DefItems); - } - - if (HorizontalAxis!=null && HorizontalAxis.Rectangle != null) - { - PlaceHorizontalAxis(sc, HorizontalAxis); - - //Make sure the horizontal axis is moved up if the vertical axis has a negative minimum value, so that the 0 value is at the correct position. - if (VerticalAxis.Axis.AxisType == eAxisType.Val && VerticalAxis.Min < 0D) - { - var newtop = VerticalAxis.GetPositionInPlotarea(0D) + sc.Plotarea.Rectangle.Top; - var topDiff = HorizontalAxis.Rectangle.Top - newtop; - HorizontalAxis.Rectangle.Top = newtop; - HorizontalAxis.Rectangle.Height += topDiff; - HorizontalAxis.Line.Y1 = HorizontalAxis.Line.Y2 = newtop; - } - - HorizontalAxis.AddTickmarksAndValues(DefItems); - } - - if (SecondVerticalAxis!=null) - { - PlaceVerticalAxis(sc, SecondVerticalAxis); - SecondVerticalAxis.AddTickmarksAndValues(DefItems); - } - - if (SecondHorizontalAxis != null && SecondHorizontalAxis.Rectangle != null) - { - PlaceHorizontalAxis(sc, SecondHorizontalAxis); - SecondHorizontalAxis.AddTickmarksAndValues(DefItems); - } - } - - private void PlaceHorizontalAxis(SvgChart sc, ChartAxisRenderer horizontalAxis) - { - horizontalAxis.Rectangle.Width = Plotarea.Rectangle.Width; - horizontalAxis.Rectangle.Left = Plotarea.Rectangle.Left; - horizontalAxis.Line.X1 = (float)horizontalAxis.Rectangle.Left; - horizontalAxis.Line.X2 = (float)horizontalAxis.Rectangle.Right; - - if (horizontalAxis.Axis.AxisPosition == eAxisPosition.Bottom) - { - horizontalAxis.Rectangle.Top = Plotarea.Rectangle.Bottom; - horizontalAxis.Line.Y1 = horizontalAxis.Line.Y2 = (float)Plotarea.Rectangle.Bottom; - } - else - { - horizontalAxis.Rectangle.Top = Plotarea.Rectangle.Top - horizontalAxis.Rectangle.Height; - horizontalAxis.Line.Y1 = horizontalAxis.Line.Y2 = (float)Plotarea.Rectangle.Top; - } - - if (horizontalAxis.Title != null) - { - horizontalAxis.Title.Rectangle.Height = sc.Bounds.Height / 4; - horizontalAxis.Title.Rectangle.Width = horizontalAxis.Rectangle?.Width ?? sc.Plotarea.Rectangle.Width; - //horizontalAxis.Title.InitTextBox(); - if (horizontalAxis.Axis.AxisPosition == eAxisPosition.Bottom) - { - horizontalAxis.Title.TextBox.Top = horizontalAxis.Rectangle.Bottom; - } - else - { - horizontalAxis.Title.TextBox.Top = horizontalAxis.Rectangle.Top - horizontalAxis.Title.TextBox.Height; - } - horizontalAxis.Title.TextBox.Left = Plotarea.Rectangle.Left + (Plotarea.Rectangle.Width / 2) - (horizontalAxis.Title.TextBox.Width / 2); - } - } - - private void PlaceVerticalAxis(SvgChart sc, ChartAxisRenderer verticalAxis) - { - if (verticalAxis.Rectangle != null) - { - verticalAxis.Rectangle.Top = Plotarea.Rectangle.Top; - verticalAxis.Rectangle.Height = Plotarea.Rectangle.Height; - verticalAxis.Line.Y1 = (float)verticalAxis.Rectangle.Top; - verticalAxis.Line.Y2 = (float)verticalAxis.Rectangle.Bottom; - if (verticalAxis.Axis.AxisPosition == eAxisPosition.Left) - { - verticalAxis.Rectangle.Left = Plotarea.Rectangle.Left - verticalAxis.Rectangle.Width; - verticalAxis.Line.X1 = verticalAxis.Line.X2 = (float)Plotarea.Rectangle.Left; - } - else - { - verticalAxis.Rectangle.Left = Plotarea.Rectangle.Right; - verticalAxis.Line.X1 = verticalAxis.Line.X2 = (float)Plotarea.Rectangle.Right; - } - } - - if (verticalAxis.Title != null) - { - //verticalAxis.Title.Rectangle.Height = Plotarea.Rectangle.Height; - //verticalAxis.Title.Rectangle.Width = sc.Bounds.Width / 4; - //verticalAxis.Title.InitTextBox(); - var sinRot = Math.Abs(Math.Sin(MathHelper.Radians(verticalAxis.Title.TextBox.Rotation))); - var cosRot = Math.Abs(Math.Cos(MathHelper.Radians(verticalAxis.Title.TextBox.Rotation))); - verticalAxis.Title.TextBox.Top = Plotarea.Rectangle.Top + (Plotarea.Rectangle.Height / 2) + ((verticalAxis.Title.TextBox.Height * cosRot + verticalAxis.Title.TextBox.Width * sinRot) / 2); - - if (verticalAxis.Axis.AxisPosition == eAxisPosition.Left) - { - if (verticalAxis.Rectangle == null) - { - verticalAxis.Title.TextBox.Left = sc.Plotarea.Rectangle.Left - verticalAxis.Title.TextBox.GetActualWidth() - 1.5; - } - else - { - verticalAxis.Title.TextBox.Left = verticalAxis.Rectangle.Left - verticalAxis.Title.TextBox.GetActualWidth() - 1.5; - } - } - else - { - if (verticalAxis.Rectangle == null) - { - verticalAxis.Title.TextBox.Left = Plotarea.Rectangle.Right; - } - else - { - verticalAxis.Title.TextBox.Left = verticalAxis.Rectangle.Right; - } - } - //verticalAxis.Title.RenderTextbox.TextAnchor = eTextAnchor.Middle; - } - } - - internal SvgChartObject ChartArea { get; set; } - internal SvgChartLegend Legend { get; set; } - internal ChartTitleRenderer Title { get; set; } - internal SvgChartPlotarea Plotarea { get; set; } - internal ChartAxisRenderer VerticalAxis { get; set; } - internal ChartAxisRenderer HorizontalAxis { get; set; } - internal ChartAxisRenderer SecondVerticalAxis { get; set; } - internal ChartAxisRenderer SecondHorizontalAxis { get; set; } - private void SetChartArea() - { - var item = new SvgChartArea(this); - item.Rectangle.Width = Bounds.Width; - item.Rectangle.Height = Bounds.Height; - item.Rectangle.SetDrawingPropertiesFill(Chart.Fill, Chart.StyleManager.Style.ChartArea.FillReference.Color); - item.Rectangle.SetDrawingPropertiesBorder(Chart.Border, Chart.StyleManager.Style.ChartArea.BorderReference.Color, Chart.Border.Width > 0); - item.AppendRenderItems(RenderItems); - ChartArea = item; - } - internal List DefItems { get; } = new List(); - internal void AddDefs(RenderItem item) - { - DefItems.Add(item); - } - public void Render(StringBuilder sb) - { - Plotarea?.AppendRenderItems(RenderItems); - - HorizontalAxis?.AppendRenderItems(RenderItems); - VerticalAxis?.AppendRenderItems(RenderItems); - SecondHorizontalAxis?.AppendRenderItems(RenderItems); - SecondVerticalAxis?.AppendRenderItems(RenderItems); - - if (Plotarea != null) - { - foreach (var drawer in Plotarea?.ChartTypeDrawers) - { - drawer.AppendRenderItems(RenderItems); - } - } - - HorizontalAxis?.Textboxes?.AppendRenderItems(RenderItems); - VerticalAxis?.Textboxes?.AppendRenderItems(RenderItems); - SecondHorizontalAxis?.Textboxes?.AppendRenderItems(RenderItems); - SecondVerticalAxis?.Textboxes?.AppendRenderItems(RenderItems); - - Title?.AppendRenderItems(RenderItems); - Legend?.AppendRenderItems(RenderItems); - - sb.Append($""); - //Write defs used for gradient colors - var writer = new SvgDrawingWriter(this); - writer.WriteSvgDefs(sb, RenderItems); - - foreach (var item in RenderItems) - { - item.Render(sb); - } - - sb.Append(""); - } - - internal double GetPlotAreaTop() - { - var margin = 14D; - if(Legend!=null && Chart.Legend.Position==eLegendPosition.Top) - { - return Legend.Rectangle.Bottom + margin; - } - else if (Title != null) - { - return Title.Rectangle.Bottom + margin; - } - else - { - return margin; - } - - } - - internal SvgRenderLineItem GetSeriesIcon(ExcelChartStandardSerie s, int index, BoundingBox parentItem) - { - const float MarginExtra = 1.5f; - const float LineLength = 21; - - var item = new SvgRenderLineItem(this, parentItem); - item.SetDrawingPropertiesFill(s.Fill, this.Chart.StyleManager.Style.SeriesLine.FillReference.Color); - item.SetDrawingPropertiesBorder(s.Border, this.Chart.StyleManager.Style.SeriesLine.BorderReference.Color, s.Border.Fill.Style != eFillStyle.NoFill, 0.75); - - float y = (float)parentItem.Top + MarginExtra; - float x = 0; - item.X1 = x; - item.Y1 = y; - item.X2 = x + LineLength; - item.Y2 = y; - item.LineCap = eLineCap.Round; - - return item; - } - } - - internal interface IChartRenderer - { - DrawingObject ChartAreaRenderer { get; } - DrawingObject TitleRenderer { get; } - DrawingObject LegendRenderer { get; } - DrawingObject AxisRenderer { get; } - DrawingObject AxisTextboxRenderer { get; } - DrawingObject PlotareaRenderer { get; } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartLegend.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartLegend.cs deleted file mode 100644 index dddd89e2f2..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartLegend.cs +++ /dev/null @@ -1,698 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlus.Export.ImageRenderer.Svg; -using EPPlus.Fonts.OpenType; -using EPPlus.Fonts.OpenType.Integration; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml; -using OfficeOpenXml.ConditionalFormatting; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -using OfficeOpenXml.Interfaces.Drawing.Text; -using OfficeOpenXml.Style; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml; - -namespace EPPlusImageRenderer.Svg -{ - internal class SvgChartLegend : SvgChartObject - { - - List _seriesHeadersMeasure = new List(); - ITextMeasurer _ttMeasurer; - const float MarginIconText = 1.5f; - const float MarginHeight = 7.5f; - const float LineLength = 21; - const float MinBarLength = 4; - float _marginItemsWidth; - - double _maxWidth, _maxHeight; - internal SvgChartLegend(SvgChart sc, bool isDataLabelLegend = false) : base(sc) - { - var mf = sc.Chart.Font.GetMeasureFont(); - var shaper = OpenTypeFonts.GetShaperForFont(mf); - var _ttMeasurer = new OpenTypeFontTextMeasurer(shaper); - - if (sc.Chart.HasLegend == false && isDataLabelLegend == false || sc.Chart.Series.Count == 0) - { - return; - } - var l = ((ExcelChartStandard)sc.Chart).Legend; - - LeftMargin = RightMargin = 3; //4px - TopMargin = BottomMargin = 3; //4px - _marginItemsWidth = mf.Size; //We use the size of the font as margin between items. - switch (l.Position) - { - case eLegendPosition.Top: - case eLegendPosition.Bottom: - _maxWidth = sc.ChartArea.Rectangle.Width * 0.8; - _maxHeight = sc.ChartArea.Rectangle.Height * 0.6; - break; - default: - _maxWidth = sc.ChartArea.Rectangle.Width * 0.6; - _maxHeight = sc.ChartArea.Rectangle.Height * 0.8; - break; - } - double entryWidth, entryHeight; - - Rectangle = GetLegendRectangleAndEntrySize(sc, l, out entryWidth, out entryHeight); - - if (l.Layout.HasLayout) //Manual layout will override the position and size of legend, but not the entry size which is used for calculating the position of legend entries. - { - Rectangle = GetRectFromManualLayout(sc, l.Layout); - } - - Bounds.Left = Rectangle.Left; - Bounds.Top = Rectangle.Top; - Bounds.Width = Rectangle.Width; - Bounds.Height = Rectangle.Height; - Rectangle.Bounds.Left = Rectangle.Bounds.Top = 0; - - Rectangle.SetDrawingPropertiesFill(l.Fill, sc.Chart.StyleManager.Style.Title.FillReference.Color); - Rectangle.SetDrawingPropertiesBorder(l.Border, sc.Chart.StyleManager.Style.Title.BorderReference.Color, l.Border.Fill.Style != eFillStyle.NoFill, 0.75); - - var pSls = SetLegendSeries(sc, entryWidth, entryHeight); - SetLegendTrendlines(sc, entryWidth, entryHeight, pSls); - } - - - private SvgRenderRectItem GetLegendRectangleAndEntrySize(SvgChart sc, ExcelChartLegend l, out double entryWidth, out double entryHeight) - { - var rect = new SvgRenderRectItem(sc, Bounds); - - var widest = 0d; - var highest = 0d; - var index = 0; - - //Find the widest and hightest legend entry, and calculate the total width and hight of the legend based on the orientation. - foreach (var ct in sc.Chart.PlotArea.ChartTypes) - { - foreach (var s in ct.Series) - { - var text = s.GetHeaderText(index); - GetSerieSize(l, index, text, ref widest, ref highest); - index++; - } - } - - //Trendlines also get legend entries, but they should appear after the series name. - - - index += trIndex; - - var maxIconLength = GetMaxIconLenght(sc.Chart, highest); - entryWidth = maxIconLength + MarginIconText + widest; - entryHeight = highest; - - switch (l.Position) - { - case eLegendPosition.Top: - case eLegendPosition.Bottom: - var fullLength = LeftMargin + entryWidth * index + _marginItemsWidth * (index - 1) + RightMargin; - if(fullLength > _maxWidth) - { - var height = entryHeight * 0.25; - var widestLine = 0D; - var width = LeftMargin + entryWidth; - - for(int i = 0; i < index; i++) - { - if (width + entryWidth + RightMargin > _maxWidth) - { - height += entryHeight * 1.25; - if (width + RightMargin > widestLine) - { - widestLine = width + RightMargin; - } - width = RightMargin + widest; - } - else - { - width += entryWidth + _marginItemsWidth; - } - } - - //height+= BottomMargin; - rect.Width = Math.Max(widestLine, width); - rect.Height = height + entryHeight * 1.25; - } - else - { - rect.Width = fullLength; - rect.Height = entryHeight * 1.5; - } - rect.Left = (sc.ChartArea.Rectangle.Width - rect.Width) / 2; - if (l.Position == eLegendPosition.Top) - { - rect.Top = sc.Title.Rectangle.Bottom + MarginHeight; - } - else - { - rect.Top = sc.ChartArea.Rectangle.Height - rect.Height - BottomMargin; - } - break; - case eLegendPosition.Right: - case eLegendPosition.TopRight: - case eLegendPosition.Left: - rect.Width = LeftMargin + entryWidth + RightMargin; - rect.Height = (entryHeight * index) + ((index + 1) * entryHeight * 0.5); //use margin as 50% of the entry height and to the top and the bottom.; - - if (rect.Height > _maxHeight) - { - rect.Height = _maxHeight; - } - - if (l.Position == eLegendPosition.Right || - l.Position == eLegendPosition.TopRight) - { - rect.Left = sc.ChartArea.Rectangle.Width - rect.Width - TopMargin; - } - else - { - rect.Left = LeftMargin + 2; - } - if (l.Position == eLegendPosition.Left || - l.Position == eLegendPosition.Right) - { - //Will be set when the plotarea width is calculated. - //rect.Top = sc.ChartArea.Rectangle.Height / 2 - rect.Height / 2; - } - else - { - if (sc.Title == null) - { - rect.Top = 8 + 8; - } - else - { - rect.Top = sc.Title.Rectangle.Height + 8 + 8; //Height + Margin Top and Bottom Title - } - } - break; - } - - return rect; - } - - private void GetSerieSize(ExcelChartLegend l, int index, string text, ref double widest, ref double highest) - { - var entry = l.Entries.FirstOrDefault(x => x.Index == index); - ExcelTextFont font; - MeasurementFont mf; - if (entry == null || entry.Font.IsEmpty) - { - font = l.Font; - mf = l.Font.GetMeasureFont(); - } - else - { - font = entry.Font; - mf = entry.Font.GetMeasureFont(); - } - - if (_ttMeasurer == null) - { - _ttMeasurer = new OpenTypeFontTextMeasurer(OpenTypeFonts.GetShaperForFont(mf)); - } - - var tm = _ttMeasurer.MeasureText(text, mf); - _seriesHeadersMeasure.Add(tm); - - if (tm.Width > widest) - { - widest = tm.Width; - } - - if (tm.Height > highest) - { - highest = tm.Height; - } - } - - private double GetMaxIconLenght(ExcelChart ct, double heighestText) - { - var maxIconLength = 0D; - foreach(var c in ct.PlotArea.ChartTypes) - { - var il = GetIconLenght(c, heighestText); - if (il > maxIconLength) - { - maxIconLength = il; - } - } - return maxIconLength; - } - private double GetIconLenght(ExcelChart c, double heighestText) - { - return c.IsTypeLine() ? LineLength : Math.Max(MinBarLength, heighestText * 0.4); - } - - - internal SvgLegendSerie SetLegendSeries(SvgChart sc, double entryWidth, double entryHeight) - { - int index = 0; - SvgLegendSerie pSls=null; - var pos = Chart.Legend.Position; - var maxIconLength = GetMaxIconLenght(sc.Chart, entryHeight); - foreach (var ct in sc.Chart.PlotArea.ChartTypes) - { - int ix, end; - if(ct.IsTypeBar()) - { - ix = ct.Series.Count-1; - end = -1; - } - else - { - ix = 0; - end = ct.Series.Count; - } - - while(ix != end) - { - var s = ct.Series[ix]; - var sls = new SvgLegendSerie(); - switch (ct.ChartType) - { - case eChartType.Line: - case eChartType.LineMarkers: - case eChartType.LineMarkersStacked: - case eChartType.LineMarkersStacked100: - case eChartType.LineStacked: - case eChartType.LineStacked100: - SetLineLegend(sc, ct, index, pSls, pos, s, sls, entryWidth, entryHeight, maxIconLength); - break; - case eChartType.ColumnClustered: - case eChartType.ColumnStacked: - case eChartType.ColumnStacked100: - case eChartType.BarClustered: - case eChartType.BarStacked: - case eChartType.BarStacked100: - SetBarLegend(sc, ct, index, pSls, pos, s, sls, entryWidth, entryHeight, maxIconLength); - break; - case eChartType.Pie: - case eChartType.PieExploded: - SetPieLegend(sc, ct, index, pSls, pos, s, sls, entryWidth, entryHeight, maxIconLength); - break; - default: - break; - } - if (sc.Chart.Legend.Position == eLegendPosition.Top || - sc.Chart.Legend.Position == eLegendPosition.Bottom) - { - //if (sls.Textbox.Bounds.Bottom > Rectangle.Bottom) - //{ - // break; - //} - } - else - { - if (sls.Textbox.Bounds.Bottom > Rectangle.Bottom) - { - break; - } - } - SeriesIcon.Add(sls); - pSls = sls; - index++; - if(ix Rectangle.Bottom) - { - return; - } - - SeriesIcon.Add(sls); - pSls = sls; - index++; - - } - if (ix < end) - { - ix++; - } - else - { - ix--; - } - } - } - } - private void SetTrendlineLegend(SvgChart sc, ExcelChart ct, int serieIndex, int entryIndex, SvgLegendSerie pSls, eLegendPosition pos, ExcelChartTrendline tl, SvgLegendSerie sls, double entryWidth, double entryHeight) - { - - var si = GetTrendLineSeriesIcon(sc, ct, tl, pSls, entryWidth, entryHeight); - sls.SeriesIcon = si; - - var tbLeft = si.X1 + LineLength + MarginIconText; - var tbTop = si.Y2 - entryHeight * 0.5; //TODO:Should probably be font ascent - double tbWidth; - //if (pos == eLegendPosition.Left || pos == eLegendPosition.Right) - //{ - // tbWidth = Bounds.Width - tbLeft - RightMargin; - //} - //else - //{ - tbWidth = Bounds.Width - tbLeft - RightMargin; - //} - - var tbHeight = entryHeight; - sls.Textbox = new SvgTextBodyItem(ChartRenderer, Bounds, tbLeft, tbTop, tbWidth, tbHeight, false, true); - - var entry = Chart.Legend.Entries.FirstOrDefault(x => x.Index == entryIndex); - var headerText = tl.GetName(serieIndex); - if (entry == null || entry.Font.IsEmpty) - { - sls.Textbox.ImportParagraph(sc.Chart.Legend.TextBody.Paragraphs.FirstOrDefault(), 0, headerText); - } - else - { - sls.Textbox.ImportParagraph(entry.TextBody.Paragraphs.FirstOrDefault(), 0, headerText); - } - } - - - private void SetPieLegend(SvgChart sc, ExcelChart ct, int index, SvgLegendSerie pSls, eLegendPosition pos, ExcelChartSerie s, SvgLegendSerie sls, double entryWidth, double entryHeight, double maxIconLength) - { - var ps = (ExcelPieChartSerie)s; - - var si = GetLineSeriesIcon(sc, ct, ps, pSls, entryWidth, entryHeight); - sls.SeriesIcon = si; - - var tbLeft = si.X1 + maxIconLength + MarginIconText; - var tbTop = si.Y2 - entryHeight * 0.5; - var tbWidth = Bounds.Width - tbLeft; - - var tbHeight = entryHeight; - sls.Textbox = new SvgTextBodyItem(ChartRenderer, Rectangle.Bounds, tbLeft, tbTop, tbWidth, tbHeight, false, true); - - //Cat values are the header text - //They create a rect marker for each slice - - //var entry = Chart.Legend.Entries.FirstOrDefault(x => x.Index == index); - //var catValues = LoadSeriesValues(serie.XSeries, serie.NumberLiteralsX, serie.StringLiteralsX); - //for (int i = 0; i< ps.NumberOfItems; i++) - //{ - - // var headerText = ps.XSeries - //} - //var headerText = s.GetHeaderText(index); - //if (entry == null || entry.Font.IsEmpty) - //{ - // sls.Textbox.ImportParagraph(sc.Chart.Legend.TextBody.Paragraphs.FirstOrDefault(), 0, headerText); - //} - //else - //{ - // //sls.Textbox.AddText(s.GetHeaderText(), entry.Font); - // sls.Textbox.ImportParagraph(entry.TextBody.Paragraphs.FirstOrDefault(), 0, headerText); - //} - - //if (ps.DataPoints != null && ps.DataPoints.Count != null /*&& ps.Marker.Style != eMarkerStyle.None*/) - //{ - // var l = sls.SeriesIcon as SvgRenderLineItem; - // var x = l.X1 + (l.X2 - l.X1) / 2; - // var y = l.Y1; - - // //sls.MarkerIcon = LineMarkerHelper.GetMarkerItem(sc, ps, x, y, true); - // if ((ps.Marker.Style == eMarkerStyle.Plus || ps.Marker.Style == eMarkerStyle.X || ps.Marker.Style == eMarkerStyle.Star) && - // ps.Marker.Fill.IsEmpty == false) - // { - // sls.MarkerBackground = LineMarkerHelper.GetMarkerBackground(sc, ps, x, y, true); - // } - // else - // { - // sls.MarkerBackground = null; - // } - //} - } - - private void SetLineLegend(SvgChart sc, ExcelChart ct, int index, SvgLegendSerie pSls, eLegendPosition pos, ExcelChartSerie s, SvgLegendSerie sls, double entryWidth, double entryHeight, double maxIconLength) - { - var ls = (ExcelLineChartSerie)s; - - var si = GetLineSeriesIcon(sc, ct, ls, pSls, entryWidth, entryHeight); - sls.SeriesIcon = si; - - var tbLeft = si.X1 + maxIconLength + MarginIconText; - var tbTop = si.Y2 - entryHeight * 0.5; - var tbWidth = Bounds.Width - tbLeft; - - var tbHeight = entryHeight; - sls.Textbox = new SvgTextBodyItem(ChartRenderer, Rectangle.Bounds, tbLeft, tbTop, tbWidth, tbHeight, false, true); - - var entry = Chart.Legend.Entries.FirstOrDefault(x => x.Index == index); - var headerText = s.GetHeaderText(index); - if (entry == null || entry.Font.IsEmpty) - { - sls.Textbox.ImportParagraph(sc.Chart.Legend.TextBody.Paragraphs.FirstOrDefault(), 0, headerText); - } - else - { - //sls.Textbox.AddText(s.GetHeaderText(), entry.Font); - sls.Textbox.ImportParagraph(entry.TextBody.Paragraphs.FirstOrDefault(), 0, headerText); - } - - if (ls.HasMarker() && ls.Marker.Style != eMarkerStyle.None) - { - var l = sls.SeriesIcon as SvgRenderLineItem; - var x = l.X1 + (l.X2 - l.X1) / 2; - var y = l.Y1; - sls.MarkerIcon = LineMarkerHelper.GetMarkerItem(sc, ls, x, y, true); - if ((ls.Marker.Style == eMarkerStyle.Plus || ls.Marker.Style == eMarkerStyle.X || ls.Marker.Style == eMarkerStyle.Star) && - ls.Marker.Fill.IsEmpty == false) - { - sls.MarkerBackground = LineMarkerHelper.GetMarkerBackground(sc, ls, x, y, true); - } - else - { - sls.MarkerBackground = null; - } - } - } - - private void SetBarLegend(SvgChart sc, ExcelChart ct, int index, SvgLegendSerie pSls, eLegendPosition pos, ExcelChartSerie s, SvgLegendSerie sls, double entryWidth, double entryHeight, double maxIconLength) - { - var bs = (ExcelBarChartSerie)s; - var tm = _seriesHeadersMeasure[index]; - var si = GetBarSeriesIcon(sc, ct, bs, pSls, entryWidth, entryHeight); - sls.SeriesIcon = si; - - var tbLeft = si.Left + maxIconLength + MarginIconText; - var tbTop = si.Top - (entryHeight - si.Height) / 2; - double tbWidth; - - tbWidth = Bounds.Width - tbLeft; - - var tbHeight = tm.Height; - sls.Textbox = new SvgTextBodyItem(ChartRenderer, Bounds, tbLeft, tbTop, tbWidth, tbHeight, false, true); - //sls.Textbox.Bounds.Left = si.Bottom + MarginIconText; - - var entry = Chart.Legend.Entries.FirstOrDefault(x => x.Index == index); - var headerText = s.GetHeaderText(index); - if (entry == null || entry.Font.IsEmpty) - { - //sls.Textbox.AddText(s.GetHeaderText(), sc.Chart.Legend.Font); - sls.Textbox.ImportParagraph(sc.Chart.Legend.TextBody.Paragraphs.FirstOrDefault(), 0, headerText); - } - else - { - //sls.Textbox.AddText(s.GetHeaderText(), entry.Font); - sls.Textbox.ImportParagraph(entry.TextBody.Paragraphs.FirstOrDefault(), 0, headerText); - } - } - - private SvgRenderLineItem GetLineSeriesIcon(SvgChart sc, ExcelChart ct, ExcelChartStandardSerie cStandardSerie, SvgLegendSerie pSls, double entryWidth, double entryHeight) - { - var line = new SvgRenderLineItem(sc, Rectangle.Bounds); - line.SetDrawingPropertiesFill(cStandardSerie.Fill, sc.Chart.StyleManager.Style.SeriesLine.FillReference.Color); - line.SetDrawingPropertiesBorder(cStandardSerie.Border, sc.Chart.StyleManager.Style.SeriesLine.BorderReference.Color, cStandardSerie.Border.Fill.Style != eFillStyle.NoFill, 0.75); - var icon = pSls?.SeriesIcon as SvgRenderLineItem; - - GetItemPosition(sc, pSls, entryWidth, entryHeight, icon?.X1 ?? 0D, icon?.Y1 ?? 0D, out double x, out double y); - - line.X1 = x; - line.X2 = x + LineLength; - line.Y1 = y + entryHeight * 0.5; - line.Y2 = y + entryHeight * 0.5; - line.LineCap = eLineCap.Round; - - return line; - } - private SvgRenderLineItem GetTrendLineSeriesIcon(SvgChart sc, ExcelChart ct, ExcelChartTrendline tl, SvgLegendSerie pSls, double entryWidth, double entryHeight) - { - var line = new SvgRenderLineItem(sc, Rectangle.Bounds); - line.SetDrawingPropertiesFill(tl.Fill, sc.Chart.StyleManager.Style.Trendline.FillReference.Color); - line.SetDrawingPropertiesBorder(tl.Border, sc.Chart.StyleManager.Style.Trendline.BorderReference.Color, tl.Border.Fill.Style != eFillStyle.NoFill, 0.75); - var icon = pSls?.SeriesIcon as SvgRenderLineItem; - - GetItemPosition(sc, pSls, entryWidth, entryHeight, icon?.X1 ?? 0D, icon?.Y1 ?? 0D, out double x, out double y); - - line.X1 = x; - line.Y1 = y; - line.X2 = x + LineLength; - line.Y2 = y; - line.LineCap = eLineCap.Round; - - return line; - } - - private SvgRenderRectItem GetBarSeriesIcon(SvgChart sc, ExcelChart ct, ExcelChartStandardSerie cStandardSerie, SvgLegendSerie pSls, double entryWidth, double entryHeight) - { - var item = new SvgRenderRectItem(sc, Rectangle.Bounds); - var iconHeight = GetIconLenght(ct, entryHeight); - var icon = pSls?.SeriesIcon as SvgRenderRectItem; - - GetItemPosition(sc, pSls, entryWidth, entryHeight, icon?.Left ?? 0D, icon?.Top ?? 0D, out double x, out double y); - - item.LineCap = eLineCap.Round; - item.Left = x; - if(pSls !=null && (sc.Chart.Legend.Position == eLegendPosition.Left || sc.Chart.Legend.Position == eLegendPosition.Right)) - { - item.Top = y + (entryHeight - iconHeight) / 2; - } - else - { - item.Top = y; - } - //item.Top = y; - item.Width = iconHeight; - item.Height = iconHeight; - - item.SetDrawingPropertiesFill(cStandardSerie.Fill, sc.Chart.StyleManager.Style.SeriesLine.FillReference.Color); - item.SetDrawingPropertiesBorder(cStandardSerie.Border, sc.Chart.StyleManager.Style.SeriesLine.BorderReference.Color, cStandardSerie.Border.Fill.Style != eFillStyle.NoFill, 0.75); - - return item; - } - - private double GetItemPosition(SvgChart sc, SvgLegendSerie pSls, double entryWidth, double entryHeight, double iconLeft, double iconTop, out double x, out double y) - { - var topOffset = 0D; - if (sc.Chart.Legend.Position == eLegendPosition.Top || - sc.Chart.Legend.Position == eLegendPosition.Bottom) - { - if (pSls != null && pSls.Textbox.Bounds.Right + entryWidth + RightMargin > _maxWidth) - { - topOffset += entryHeight * 1.25; - x = Rectangle.Left + LeftMargin; - } - else - { - if (pSls == null) - { - x = Rectangle.Left + (float)LeftMargin; - } - else - { - x = iconLeft + entryWidth + _marginItemsWidth; - } - } - - if (pSls == null) - { - y = + entryHeight / 4; - } - else - { - y = iconTop + topOffset - (entryHeight / 2); - } - - - } - else - { - if (pSls == null) - { - y = entryHeight / 2; - } - else - { - y = pSls.Textbox.Bounds.Top + entryHeight * 1.50; - } - x = LeftMargin; - - } - - return topOffset; - } - - internal override void AppendRenderItems(List renderItems) - { - var groupItem = new SvgGroupItem(ChartRenderer, Rectangle.Bounds); - renderItems.Add(groupItem); - - //The rectangle is position using the group transform, so we need to set the rectangle position to 0,0 - Rectangle.Bounds.Top = 0; - Rectangle.Bounds.Left = 0; - - renderItems.Add(Rectangle); - foreach(var s in SeriesIcon) - { - if(s.SeriesIcon != null) renderItems.Add(s.SeriesIcon); - if(s.MarkerBackground != null) renderItems.Add(s.MarkerBackground); - if (s.MarkerIcon != null) renderItems.Add(s.MarkerIcon); - //renderItems.Add(s.Textbox); - if(s.Textbox != null) s.Textbox.AppendRenderItems(renderItems); - } - renderItems.Add(new SvgEndGroupItem(ChartRenderer, null)); - } - - public List SeriesIcon { get; } = new List(); - - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartLegendIcon.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartLegendIcon.cs deleted file mode 100644 index 97f4125a74..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartLegendIcon.cs +++ /dev/null @@ -1,117 +0,0 @@ -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.Interfaces.Drawing.Text; -using System; -using System.Collections.Generic; - -namespace EPPlus.Export.ImageRenderer.Svg.Chart -{ - internal class SvgChartLegendIcon : SvgRenderLineItem - { - const float MarginExtra = 1.5f; - const float MiddleMargin = 7.5f; - const float LineLength = 21; - - public SvgChartLegendIcon(SvgChart sc, SvgRenderRectItem Rectangle, ExcelChartStandardSerie s, TextMeasurement tm, double topMargin, double leftMargin, TextMeasurement sHM, SvgLegendSerie pSls) : base(sc, Rectangle.Bounds) - { - SetDrawingPropertiesFill(s.Fill, sc.Chart.StyleManager.Style.SeriesLine.FillReference.Color); - SetDrawingPropertiesBorder(s.Border, sc.Chart.StyleManager.Style.SeriesLine.BorderReference.Color, s.Border.Fill.Style != eFillStyle.NoFill, 0.75); - - if (sc.Chart.Legend.Position == eLegendPosition.Top || - sc.Chart.Legend.Position == eLegendPosition.Bottom) - { - float y = (float)Rectangle.Top + (float)topMargin + tm.Height / 2 + MarginExtra; - float x = 0; - if (pSls == null) - { - x = (float)Rectangle.Left + (float)leftMargin;// + MarginIconText; - } - else - { - x = (float)pSls.Textbox.Bounds.Right + MiddleMargin; - } - - X1 = x; - Y1 = y; - X2 = x + LineLength; - Y2 = y; - LineCap = eLineCap.Round; - } - else - { - double y; - if (pSls == null) - { - y = topMargin + tm.Height / 2 + MarginExtra; - } - else - { - var pTm = sHM; - y = ((SvgRenderLineItem)pSls.SeriesIcon).Y1 + pTm.Height / 2 + tm.Height / 2 + MiddleMargin; - } - - X1 = (float)leftMargin; //4 - Y1 = y; - X2 = (float)LineLength; - Y2 = y; - LineCap = eLineCap.Round; - } - } - - //internal override void AppendRenderItems(List renderItems) - //{ - // throw new NotImplementedException(); - //} - - //private SvgRenderLineItem GetLineSeriesIcon(DrawingChart sc, ExcelChartStandardSerie s, TextMeasurement sHM, TextMeasurement tm, SvgLegendSerie pSls) - //{ - // var item = new SvgRenderLineItem(sc, Rectangle.Bounds); - // item.SetDrawingPropertiesFill(s.Fill, sc.Chart.StyleManager.Style.SeriesLine.FillReference.Color); - // item.SetDrawingPropertiesBorder(s.Border, sc.Chart.StyleManager.Style.SeriesLine.BorderReference.Color, s.Border.Fill.Style != eFillStyle.NoFill, 0.75); - - // if (sc.Chart.Legend.Position == eLegendPosition.Top || - // sc.Chart.Legend.Position == eLegendPosition.Bottom) - // { - // float y = (float)Rectangle.Top + (float)TopMargin + tm.Height / 2 + MarginIconText; - // float x = 0; - // if (pSls == null) - // { - // x = (float)Rectangle.Left + (float)LeftMargin;// + MarginIconText; - // } - // else - // { - // x = (float)pSls.Textbox.Bounds.Right + MarginHeight; - // } - - // item.X1 = x; - // item.Y1 = y; - // item.X2 = x + LineLength; - // item.Y2 = y; - // item.LineCap = eLineCap.Round; - // } - // else - // { - // double y; - // if (pSls == null) - // { - // y = TopMargin + tm.Height / 2 + MarginIconText; - // } - // else - // { - // var pTm = sHM; - // y = ((SvgRenderLineItem)pSls.SeriesIcon).Y1 + pTm.Height / 2 + tm.Height / 2 + MarginHeight; - // } - - // item.X1 = (float)LeftMargin; //4 - // item.Y1 = y; - // item.X2 = (float)LineLength; - // item.Y2 = y; - // item.LineCap = eLineCap.Round; - // } - - // return item; - //} - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartObject.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartObject.cs deleted file mode 100644 index 1825777421..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartObject.cs +++ /dev/null @@ -1,114 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using System.Collections.Generic; - -namespace EPPlusImageRenderer.Svg -{ - internal abstract class DrawingObject - { - internal protected DrawingBase DrawingRenderer { get; } - internal virtual BoundingBox Bounds { get; set; } - - protected DrawingObject(DrawingBase renderer) - { - DrawingRenderer = renderer; - } - protected DrawingObject(DrawingBase renderer, BoundingBox parent) - { - DrawingRenderer = renderer; - Bounds = new BoundingBox() { Parent=parent}; - } - internal abstract void AppendRenderItems(List renderItems); - } - internal abstract class DrawingObjectNoBounds - { - internal protected DrawingBase DrawingRenderer { get; } - - protected DrawingObjectNoBounds(DrawingBase renderer) - { - DrawingRenderer = renderer; - } - internal abstract void AppendRenderItems(List renderItems); - } - internal class SvgChartArea : SvgChartObject - { - public SvgChartArea(SvgChart sc) : base(sc) - { - Rectangle = new SvgRenderRectItem(sc, sc.Bounds); - } - - internal override void AppendRenderItems(List renderItems) - { - renderItems.Add(Rectangle); - } - } - internal abstract class SvgChartObject : DrawingObject - { - internal EPPlusImageRenderer.DrawingChart ChartRenderer; - internal ExcelChart Chart => (ExcelChart)ChartRenderer.Drawing; - internal SvgChartObject(EPPlusImageRenderer.DrawingChart chart) : base(chart, chart.Bounds) - { - ChartRenderer = chart; - } - internal void SetMargins(ExcelTextBody tb) - { - tb.GetInsetsOrDefaults(out double l, out double r, out double t, out double b); - LeftMargin = l; - RightMargin = r; - TopMargin = t; - BottomMargin = b; - } - internal double LeftMargin { get; set; } - internal double RightMargin { get; set; } - internal double TopMargin { get; set; } - internal double BottomMargin { get; set; } - internal SvgRenderRectItem Rectangle { get; set; } - protected static SvgRenderRectItem GetRectFromManualLayout(SvgChart sc, ExcelLayout layout, BoundingBox parent=null) - { - var bounds = parent ?? sc.Bounds; - var rect = new SvgRenderRectItem(sc, sc.ChartArea.Rectangle.Bounds); - var ml = layout.ManualLayout; - if (ml.LeftMode == eLayoutMode.Edge) - { - rect.Left = bounds.Width * (float)(layout.ManualLayout.Left ?? 0D) / 100; - } - else - { - rect.Left = bounds.Width * (float)(ml.Left ?? 0D) / 100; - //TODO:Add factor from default position - } - - //Width is always factor. - rect.Width = bounds.Width * ml.GetWidth() / 100; - - if (ml.LeftMode == eLayoutMode.Edge) - { - rect.Top = bounds.Height * (float)(layout.ManualLayout.Top ?? 0D) / 100; - } - else - { - rect.Top = bounds.Height * (float)(ml.Top ?? 0D) / 100; - //TODO:Add factor from default position - } - //Height is always factor. - rect.Height = bounds.Height * ml.GetHeight() / 100; - return rect; - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartPlotarea.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartPlotarea.cs deleted file mode 100644 index 9c5f257937..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgChartPlotarea.cs +++ /dev/null @@ -1,185 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.Svg.Chart; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Chart; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace EPPlusImageRenderer.Svg -{ - internal class SvgChartPlotarea : SvgChartObject - { - public SvgChartPlotarea(SvgChart sc) : base(sc) - { - SvgChart = sc; - } - public SvgChart SvgChart { get; set; } - public List ChartTypeDrawers { get; set; } - internal void SetPlotAreaRectangle(SvgChart sc) - { - var pa = sc.Chart.PlotArea; - TopMargin = BottomMargin = LeftMargin = RightMargin = 10.5; //14px - var rect = new SvgRenderRectItem(sc, sc.Bounds); - if (pa.Layout.HasLayout) - { - rect = GetRectFromManualLayout(sc, pa.Layout); - } - else - { - rect.Top = GetPlotAreaTop(sc); - rect.Left = GetPlotAreaLeft(sc); - rect.Width = GetPlotAreaWidth(sc, rect); - rect.Height = GetPlotAreaHeight(sc, rect); - } - - rect.SetDrawingPropertiesFill(pa.Fill, sc.Chart.StyleManager.Style.PlotArea.FillReference.Color); - rect.SetDrawingPropertiesBorder(pa.Border, sc.Chart.StyleManager.Style.PlotArea.BorderReference.Color, pa.Border.Fill.Style != eFillStyle.NoFill, 0.75); - Rectangle = rect; - } - - private double GetPlotAreaHeight(SvgChart sc, SvgRenderRectItem rect) - { - var bottomAxis = GetAxisByPosition(sc, eAxisPosition.Bottom); - double vaHeight = 0; - if (bottomAxis!=null) - { - vaHeight = (bottomAxis.Rectangle?.Height ?? 0D) + (bottomAxis.Title?.TextBox?.GetActualHeight() ?? 0D); - } - if (sc.Chart.Legend?.Position == eLegendPosition.Bottom) - { - vaHeight += sc.Legend.Rectangle.Height + sc.Legend.TopMargin; - } - return sc.Bounds.Height - rect.GlobalTop - vaHeight - BottomMargin; - } - - private double GetPlotAreaWidth(SvgChart sc, SvgRenderRectItem rect) - { - var rightAxis = GetAxisByPosition(sc, eAxisPosition.Right); - var lp = sc.Chart.Legend?.Position; - var right = ((lp == eLegendPosition.Right || lp == eLegendPosition.TopRight) && sc.Legend != null ? - sc.Legend.Bounds.GlobalLeft - RightMargin : - sc.ChartArea.Rectangle.Width - RightMargin); - - - double rightAxisWidth; - if (rightAxis == null) - { - rightAxisWidth = 0; - } - else - { - rightAxisWidth = (rightAxis.Title?.TextBox.GetActualWidth() ?? 0D) + (rightAxis.Rectangle?.Width ?? 0D); - } - - var width = right - rightAxisWidth - rect.GlobalLeft; - //Reserve space for the last label that will be on the tick label instead of Middle of the category. - if (sc.HorizontalAxis != null && sc.VerticalAxis.Axis.CrossBetween == eCrossBetween.MidCat) - { - var minusPA = width / sc.HorizontalAxis.AxisValues.Count / 2; - if (minusPA > rightAxisWidth) - { - rightAxisWidth = minusPA; - } - } - if (sc.SecondHorizontalAxis != null && sc.SecondVerticalAxis.Axis.CrossBetween == eCrossBetween.MidCat) - { - var minusSA = width / sc.SecondHorizontalAxis.AxisValues.Count / 2; - if (minusSA > rightAxisWidth) - { - rightAxisWidth = minusSA; - } - } - - return right - rightAxisWidth-rect.GlobalLeft; - } - private double GetPlotAreaLeft(SvgChart sc) - { - var left = LeftMargin; - if(sc.Chart.Legend?.Position == eLegendPosition.Left) - { - left += sc.Legend.Bounds.Width + sc.Legend.RightMargin; - } - - var leftAxis = GetAxisByPosition(sc, eAxisPosition.Left); - if (leftAxis != null) - { - if(leftAxis.Title!=null) - { - left += leftAxis.Title.TextBox.GetActualWidth(); - } - if (leftAxis.Rectangle != null) - { - left += leftAxis.Rectangle.Width + 1.5; - } - } - return left; - } - private double GetPlotAreaTop(SvgChart sc) - { - double haHeight = 0; - var topAxis = GetAxisByPosition(sc, eAxisPosition.Top); - if (topAxis == null) - { - //var bottomAxis = GetAxisByPosition(sc, eAxisPosition.Bottom); - //if (bottomAxis != null && sc.Chart.XAxis.LabelPosition == eTickLabelPosition.High) - //{ - // top += bottomAxis.Rectangle.Height; - //} - //return top; - } - else - { - haHeight = (topAxis.Rectangle?.Height ?? 0D) + (topAxis.Title?.TextBox?.GetActualHeight() ?? 0D); - } - - return (sc.Chart.Legend?.Position == eLegendPosition.Top ? sc.Legend.Bounds.Bottom : sc.Title?.Rectangle?.GlobalBottom ?? 0d) + haHeight + TopMargin; - } - - private ChartAxisRenderer GetAxisByPosition(SvgChart sc, eAxisPosition pos) - { - if (sc.HorizontalAxis != null && sc.HorizontalAxis.Axis.AxisPosition == pos) - { - return sc.HorizontalAxis; - } - else if (sc.VerticalAxis != null && sc.VerticalAxis.Axis.AxisPosition == pos) - { - return sc.VerticalAxis; - } - else if (sc.SecondHorizontalAxis != null && sc.SecondHorizontalAxis.Axis.AxisPosition == pos) - { - return sc.SecondHorizontalAxis; - } - else if (sc.SecondVerticalAxis != null && sc.SecondVerticalAxis.Axis.AxisPosition == pos) - { - return sc.SecondVerticalAxis; - } - return null; - } - - internal override void AppendRenderItems(List renderItems) - { - renderItems.Add(Rectangle); - } - - internal void DrawSeries() - { - foreach (var drawer in ChartTypeDrawers) - { - drawer.DrawSeries(); - } - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgPieSlice.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgPieSlice.cs deleted file mode 100644 index 93681fd05c..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/SvgPieSlice.cs +++ /dev/null @@ -1,546 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlus.Export.ImageRenderer.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing.Chart; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using System; -using System.IO.Pipes; - -namespace EPPlus.Export.ImageRenderer.Svg.Chart -{ - internal class SvgPieSlice : SvgRenderItem - { - double _radius; - /// - /// The holder of transform-origin/translations - /// - SvgGroupItemNew _innerGroup; - /// - /// The holder of the actual items, AFTER origin/translations - /// - SvgGroupItemNew _innerItems; - Point _circleCenter; - - /// - /// How many percent of the pie this represents - /// - double _percent; - - internal double Degrees { get; private set; } - - Point _startPoint; - Point _midPoint; - Point _endPoint; - - /// - /// Get copy of start point of slice in local coordinates - /// - /// - internal Coordinate GetStartPointPositionLocal() - { - return new Coordinate(_startPoint.Left, _startPoint.Top); - } - /// - /// Get copy of mid point of slice in local coordinates - /// - /// - internal Coordinate GetMidPointLocal() - { - return new Coordinate(_midPoint.Left, _midPoint.Top); - } - /// - /// Get copy of end point of slice in local coordinates - /// - /// - internal Coordinate GetEndPointLocal() - { - return new Coordinate(_endPoint.Left, _endPoint.Top); - } - - SvgRenderPathItem _slicePath; - SvgRenderPathItem _debugBoundsPath; - - bool ExistWithinRange(double target, double min, double max) - { - if(min < target && target < max) - { - return true; - } - return false; - } - - internal BoundingBox ExtremePoints { get; set; } - - void CalculateWidthHeight(double prevSliceDegrees) - { - - var endPointDegrees = prevSliceDegrees + Degrees; - if(endPointDegrees < 0) - { - endPointDegrees = 360 + endPointDegrees; - } - - var startPointDegrees = prevSliceDegrees; - if(startPointDegrees < 0) - { - startPointDegrees = 360 + startPointDegrees; - } - - var circleSectorDegrees = endPointDegrees - startPointDegrees; - - double maxX; - double maxY; - double minY; - double minX; - - if (ExistWithinRange(90, startPointDegrees, endPointDegrees)) - { - maxY = _circleCenter.Top + _radius; - } - else - { - maxY = Math.Max(_startPoint.Top, _endPoint.Top); - } - - maxY = Math.Max(maxY, _circleCenter.Top); - - if (ExistWithinRange(180, startPointDegrees, endPointDegrees)) - { - minX = _circleCenter.Left - _radius; - } - else - { - minX = Math.Min(_startPoint.Left, _endPoint.Left); - } - - minX = Math.Min(minX, _circleCenter.Left); - - if(ExistWithinRange(270, startPointDegrees, endPointDegrees)) - { - minY = _circleCenter.Top - _radius; - } - else - { - minY = Math.Min(_startPoint.Top, _endPoint.Top); - } - - minY = Math.Min(minY, _circleCenter.Top); - - if (endPointDegrees < startPointDegrees || ExistWithinRange(0, startPointDegrees, endPointDegrees)) - { - if(endPointDegrees > 270) - { - maxX = _circleCenter.Left; - } - else - { - maxX = _circleCenter.Left + _radius; - } - } - else - { - maxX = Math.Max(_startPoint.Left, _endPoint.Left); - } - - maxX = Math.Max(_circleCenter.Left, maxX); - - - ExtremePoints = new BoundingBox(minX, minY, maxX - minX, maxY - minY); - ExtremePoints.Parent = Bounds; - //if(Degrees > 0) - //{ - - //} - //if(Degrees) - //double xMax = Math.Max(_startPoint.Left, _endPoint.Left); - //xMax = Math.Max(xMax, _midPoint.Left); - - //double yMax = Math.Max(_startPoint.Top, _endPoint.Top); - //yMax = Math.Max(yMax, _midPoint.Top); - //double yMax; - //double xMin; - //double yMin; - } - - private double _sliceScaleFactor = 1d; - private double _scaledRadius { get{ return _radius * _sliceScaleFactor; } } - - private void CalculateExplosionDir() - { - var transformOriginLocal = new Graphics.Math.Vector2(_innerGroup.TransformOrigin.X, _innerGroup.TransformOrigin.Y); - - //Get directional vector (in local coords but does not matter since we make it directional) - Graphics.Math.Vector2 pieDirection = transformOriginLocal - _circleCenter.LocalPosition; - - //normalize the pieDirection vector so that it is percentual and with lenght == 1 - pieDirection = pieDirection / pieDirection.Length; - - CtrToOuterMidDir = new Graphics.Math.Vector2(pieDirection.X, pieDirection.Y); - } - - //internal Graphics.Math.Vector2 GetVectorCtrToEnd() - //{ - // //_circleCenter + _sc - //} - - public SvgPieSlice(DrawingBase renderer, BoundingBox parent, Point circleCenter, double radius, double percentOfPie, double prevSliceDegrees) : base(renderer, parent) - { - _radius = radius; - _percent = percentOfPie; - //How many degrees that percentage is out of 360 - Degrees = _percent * 360d; - - _innerGroup = new SvgGroupItemNew(renderer, parent, 0, circleCenter); - _innerGroup.Bounds.Parent = _innerGroup.TranslationOffset; - _innerGroup.Bounds.Name = "InnerGroupChartDrawer"; - _circleCenter = circleCenter; - - _startPoint = CalculateLocalPointOnCircle(prevSliceDegrees); - - //The degrees of the midpoint - var halfDegrees = Degrees / 2; - - _endPoint = CalculateLocalPointOnCircle(Degrees + prevSliceDegrees); - - //We add prev at this point since we don't want to halve the previous angle only the current one - _midPoint = CalculateLocalPointOnCircle(halfDegrees + prevSliceDegrees); - - //We must calculate transforms from the outer midpoint. - //This is to ensure that point never leaves the parent container - _innerGroup.TransformOrigin = GetMidPointLocal(); - - CalculateExplosionDir(); - CalculateWidthHeight(prevSliceDegrees); - - - _innerItems = new SvgGroupItemNew(renderer, _innerGroup.Bounds, 0); - } - - internal void ImportPathData(BoundingBox plotAreaBounds, BoundingBox globalAreaBounds, double sliceScaleFactor, double explosionOfPoint, double pieExplosion, int position) - { - _slicePath = new SvgRenderPathItem(DrawingRenderer, plotAreaBounds); - - _slicePath.BorderWidth = 5; - - //Calculate path commands - var moveCenter = new PathCommands(PathCommandType.Move, _slicePath, _circleCenter.Left, _circleCenter.Top); - var lineToStart = new PathCommands(PathCommandType.Line, _slicePath, _startPoint.Left, _startPoint.Top); - - var arcCommand = new PathCommands(PathCommandType.Arc, _slicePath, new double[] { _radius, _radius, 0, Degrees > 180 ? 1 : 0, 1, _endPoint.Left, _endPoint.Top}); - var end = new PathCommands(PathCommandType.End, _slicePath, _endPoint.Left, _endPoint.Top); - - //Get max and min values - var localMax = GetTranslationMaxLocal(globalAreaBounds.Width, globalAreaBounds.Height); - var localMin = GetTranslationMinLocal(globalAreaBounds.Width, globalAreaBounds.Height); - - _sliceScaleFactor = sliceScaleFactor; - //Translate and scale path - _innerGroup.Scale = new Coordinate(_sliceScaleFactor, _sliceScaleFactor); - CalculatePointExplosion(explosionOfPoint, pieExplosion, localMax, localMin); - - //Add the actual commands - _slicePath.Commands.Add(moveCenter); - _slicePath.Commands.Add(lineToStart); - _slicePath.Commands.Add(arcCommand); - - if(position == -1) - { - //Visualize all points - AddDebugLines(moveCenter, plotAreaBounds); - } - - _slicePath.Commands.Add(end); - - } - - /// - /// Adds line from center to outer mid point (transform-origin) and to end point - /// AKA line along scale/explosion vector - /// - /// - private void AddDebugLines(PathCommands moveCenter, BoundingBox bounds) - { - //var lineToMidPoint = new PathCommands(PathCommandType.Line, _slicePath, _midPoint.Left, _midPoint.Top); - //var lineToEnd = new PathCommands(PathCommandType.Line, _slicePath, _endPoint.Left, _endPoint.Top); - //_slicePath.Commands.Add(moveCenter); - //_slicePath.Commands.Add(lineToMidPoint); - //_slicePath.Commands.Add(moveCenter); - //_slicePath.Commands.Add(lineToEnd); - _debugBoundsPath = new SvgRenderPathItem(DrawingRenderer, bounds); - _debugBoundsPath.BorderColor = "red"; - _debugBoundsPath.FillColor = "transparent"; - _debugBoundsPath.BorderWidth = 3; - var moveCenterDebug = new PathCommands(PathCommandType.Move, _debugBoundsPath, _circleCenter.Left, _circleCenter.Top); - _debugBoundsPath.Commands.Add(moveCenterDebug); - - //Draw extremes/bounds - var lineToTopLeft = new PathCommands(PathCommandType.Line, _debugBoundsPath, ExtremePoints.Left, ExtremePoints.Top); - var lineToTopRight = new PathCommands(PathCommandType.Line, _debugBoundsPath, ExtremePoints.Right, ExtremePoints.Top); - - //var sliceCenter = GetSliceShapeCenterLocal(); - //var lineToSliceCenter = new PathCommands(PathCommandType.Line, _debugBoundsPath, sliceCenter.Left, sliceCenter.Top); - - var lineToBottomRight = new PathCommands(PathCommandType.Line, _debugBoundsPath, ExtremePoints.Right, ExtremePoints.Bottom); - var lineToBottomLeft = new PathCommands(PathCommandType.Line, _debugBoundsPath, ExtremePoints.Left, ExtremePoints.Bottom); - var end = new PathCommands(PathCommandType.End, _debugBoundsPath, ExtremePoints.Left, ExtremePoints.Top); - - _debugBoundsPath.Commands.Add(lineToTopLeft); - _debugBoundsPath.Commands.Add(lineToTopRight); - ////_debugBoundsPath.Commands.Add(lineToSliceCenter); - _debugBoundsPath.Commands.Add(lineToBottomRight); - _debugBoundsPath.Commands.Add(lineToBottomLeft); - //_debugBoundsPath.Commands.Add(end); - - } - - internal void ImportStlyeInfo(ExcelChartDataPoint dp, ExcelPieChart chartType) - { - _slicePath.SetDrawingPropertiesFill(dp.Fill, chartType.StyleManager.Style.DataPoint.FillReference.Color); - _slicePath.SetDrawingPropertiesBorder(dp.Border, chartType.StyleManager.Style.DataPoint.BorderReference.Color, true); - _slicePath.SetDrawingPropertiesEffects(dp.Effect); - } - - internal void AppendGroupItem(SvgGroupItemNew group) - { - //The slice items post transform operations - _innerItems.AddChildItem(_slicePath); - //The bounds and translations of the slice - _innerGroup.AddChildItem(_innerItems); - - if (_debugBoundsPath != null) - { - //adding the debug lines - _innerGroup.AddChildItem(_debugBoundsPath); - } - - //The group containing all slices - group.AddChildItem(_innerGroup); - } - - Graphics.Math.Vector2 GetTranslationMaxLocal(double globalWidth, double globalHeight) - { - var worldPositionTransformOrigin = _midPoint.Position; - - //Calculate extremes - Point localMax = new Point( - globalWidth - worldPositionTransformOrigin.X, - globalHeight - worldPositionTransformOrigin.Y); - - return localMax.LocalPosition; - } - - Graphics.Math.Vector2 GetTranslationMinLocal(double globalWidth, double globalHeight) - { - var worldPositionTransformOrigin = _midPoint.Position; - //Calculate extremes - Point worldMin = new Point(-worldPositionTransformOrigin.X, -worldPositionTransformOrigin.Y); - - //var localMin = _innerGroup.Position.Parent.TransformPointToLocal(worldMin.Position); - return worldMin.LocalPosition; - } - - /// - /// The directional vector from center of circle to the outer midpoint of the pie slice - /// - internal Graphics.Math.Vector2 CtrToOuterMidDir { get; private set; } - - /// - /// Gets the whole vector with length to the end - /// - /// - internal Graphics.Math.Vector2 GetWholeVectorCenterToMid() - { - var translationVector = GetLocalTranslationVector(100); - var pt = translationVector; - return pt; - } - - /// - /// - /// - /// - internal Point GetSliceShapeCenterLocal() - { - var translationVector = GetLocalTranslationVector(50); - var pt = _circleCenter.LocalPosition + translationVector; - var SliceCenterLocal = new Point(pt.X, pt.Y); - - return SliceCenterLocal; - } - /// - /// Input must be between 0 and 100 - /// - /// - /// - Graphics.Math.Vector2 GetLocalTranslationVector(double percentTowardsEndPoint) - { - if(percentTowardsEndPoint < 0 || percentTowardsEndPoint > 100) - { - throw new InvalidOperationException($"input: '{percentTowardsEndPoint}' invalid. Must be between 0 and 100"); - } - - var moveFactor = (double)percentTowardsEndPoint / 100d; - var moveFactoredDirection = moveFactor * CtrToOuterMidDir; - - //Get distance/length to move along vector. We translate according to the scaled down radius - Graphics.Math.Vector2 LocalTranslationVector = moveFactoredDirection * _scaledRadius; - return LocalTranslationVector; - } - - Graphics.Math.Vector2 GetLocalTranslationVector(double explosionOfPoint, double pieExplosion) - { - //Get point explosion value - var pointExplosion = explosionOfPoint == int.MinValue ? 0 : explosionOfPoint; - - var pieDirection = new Graphics.Math.Vector2(CtrToOuterMidDir.X, CtrToOuterMidDir.Y); - - if (pointExplosion != 0 && pointExplosion < pieExplosion) - { - //Direction is inward rather than outward - pieDirection = (pieDirection * -1); - } - else if (pieExplosion != 0 && pointExplosion > pieExplosion) - { - //Scaling has already translated the slice partially. - //Remove that from the translation percent - pointExplosion -= pieExplosion; - } - - var ptExplodeFactor = (double)pointExplosion / 100d; - var ptFactoredDirection = ptExplodeFactor * pieDirection; - - //Get distance/length to move along vector. We translate according to the scaled down radius - Graphics.Math.Vector2 LocalTranslationVector = ptFactoredDirection * _scaledRadius; - return LocalTranslationVector; - } - - Coordinate GetFinalLocalTranslation(Graphics.Math.Vector2 LocalTranslationVector, Graphics.Math.Vector2 localMax, Graphics.Math.Vector2 localMin) - { - Coordinate lengthPoint = new Coordinate(0,0); - - //Check if local is above or below extremes in X axis - if (LocalTranslationVector.X != 0) - { - if (LocalTranslationVector.X > 0 && LocalTranslationVector.X > localMax.X) - { - lengthPoint.X = localMax.X; - } - else if (LocalTranslationVector.X < localMin.X) - { - lengthPoint.X = Math.Abs(localMin.X); - } - } - - //Check if local is above or below extremes in Y axis - if (LocalTranslationVector.Y != 0) - { - if (LocalTranslationVector.Y > 0 && LocalTranslationVector.Y > localMax.Y) - { - lengthPoint.Y = localMax.Y; - } - else if (LocalTranslationVector.Y < localMin.Y) - { - lengthPoint.Y = Math.Abs(localMin.Y); - } - } - - //Find the smallest length of a vector that goes beyond the extremes - //In case both do - var maxAllowedLength = Math.Min(lengthPoint.X, lengthPoint.Y); - if (maxAllowedLength == 0) - { - //Avoid issues if one axis is 0 and the vector that goes over is positive - maxAllowedLength = Math.Max(lengthPoint.X, lengthPoint.Y); - } - - double translationLeft; - double translationTop; - - if (maxAllowedLength != 0 && maxAllowedLength < LocalTranslationVector.Length) - { - //If the length is larger than maximum allowed length we stop applying translation - var normalizedVector = LocalTranslationVector / LocalTranslationVector.Length; - var appliedVector = normalizedVector * maxAllowedLength; - - translationLeft = appliedVector.X; - translationTop = appliedVector.Y; - } - else - { - //The length is within the bounds. No binding neccesary. - translationLeft = LocalTranslationVector.X; - translationTop = LocalTranslationVector.Y; - - //translationLeft = Math.Min(translateX, maxTranslationXWorld); - //translationTop = Math.Min(translateY, maxTranslationY); - - //translationLeft = Math.Max(translationLeft, minTranslationXWorld); - //translationTop = Math.Max(translationTop, minTranslationYWorld); - } - - return new Coordinate(translationLeft, translationTop); - } - - void CalculatePointExplosion(double explosionOfPoint, double pieExplosion, Graphics.Math.Vector2 localMax, Graphics.Math.Vector2 localMin) - { - //Get distance/length to move along vector - Graphics.Math.Vector2 LocalTranslationVector = GetLocalTranslationVector(explosionOfPoint, pieExplosion); - var finalTranslation = GetFinalLocalTranslation(LocalTranslationVector, localMax, localMin); - - _innerGroup.TranslationOffset.Left = finalTranslation.X; - _innerGroup.TranslationOffset.Top = finalTranslation.Y; - } - - Point CalculateLocalPointOnCircle(double degrees) - { - var angleRadians = MConverter.DegreesToRadians(degrees); - - var xPoint = _circleCenter.Left + ( _radius * Math.Cos(angleRadians)); - var yPoint = _circleCenter.Top + (_radius * Math.Sin(angleRadians)); - - var point = new Point(); - - //Ensure the cx/cy offset - point.Parent = _innerGroup.TranslationOffset.Parent; - point.Left = xPoint; - point.Top = yPoint; - - return point; - } - - internal Coordinate GetOuterMidpointInGlobalCoords() - { - return new Coordinate(_midPoint.Position.X, _midPoint.Position.Y); - } - - internal SvgGroupItemNew GetInnerItemGroup() - { - return _innerItems; - } - - /// - /// Transform origin in local coordinates - /// - /// - internal Coordinate GetInnerGroupTransformOrigin() - { - return new Coordinate(_innerGroup.TransformOrigin.X, _innerGroup.TransformOrigin.Y); - } - - //internal BoundingBox GetInnerGroupBounds() - //{ - // return _innerGroup.Bounds; - //} - - public override RenderItemType Type => RenderItemType.Group; - - internal override SvgRenderItem Clone(SvgShape svgDocument) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Chart/eTextOrientation.cs b/src/EPPlus.Export.ImageRenderer/Svg/Chart/eTextOrientation.cs deleted file mode 100644 index 232bae630a..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Chart/eTextOrientation.cs +++ /dev/null @@ -1,21 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -namespace EPPlus.Export.ImageRenderer -{ - internal enum eTextOrientation - { - Horizontal, - Diagonal, - Vertical, - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/ChartObjectMargins.cs b/src/EPPlus.Export.ImageRenderer/Svg/ChartObjectMargins.cs deleted file mode 100644 index e1e056fc2e..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/ChartObjectMargins.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace EPPlus.Export.ImageRenderer.Svg -{ - internal static class ChartObjectMargins - { - static int TitleToPlotarea = 14; - static int LegendToPlotarea = 14; - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/DefinitionGroup.cs b/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/DefinitionGroup.cs deleted file mode 100644 index e4301600f2..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/DefinitionGroup.cs +++ /dev/null @@ -1,32 +0,0 @@ -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.DefinitionUtils -{ - internal class DefinitionGroup : RenderItem - { - internal List Items = new List(); - - public DefinitionGroup(DrawingBase renderer) : base(renderer) - { - } - - public override RenderItemType Type => RenderItemType.Group; - - public override void Render(StringBuilder sb) - { - sb.Append(""); - - foreach(var item in Items) - { - item.Render(sb); - } - - sb.Append(""); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/LinePattern.cs b/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/LinePattern.cs deleted file mode 100644 index 460ff8c80c..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/LinePattern.cs +++ /dev/null @@ -1,76 +0,0 @@ -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Style; -using OfficeOpenXml.Utils; -using OfficeOpenXml.Utils.EnumUtils; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.DefinitionUtils -{ - enum LinePatternType - { - Vertical, - Horizontal - } - - internal class LinePattern : PatternItem - { - LinePatternType type; - - internal SvgRenderLineItem LineItem; - - public LinePattern(DrawingBase baseRend, string id, LinePatternType linesType) : base(baseRend, id) - { - type = linesType; - LineItem = new SvgRenderLineItem(baseRend, Bounds); - - LineItem.BorderWidth = 2; - LineItem.Suffix = "%"; - - LineItem.BorderColor = "#" + Color.DarkGoldenrod.ToColorString(); - - switch (type) - { - case LinePatternType.Vertical: - LineItem.X1 = 0; LineItem.X2 = 0; LineItem.Y1 = 0; LineItem.Y2 = 100; - break; - case LinePatternType.Horizontal: - LineItem.X1 = 0; LineItem.X2 = 100; LineItem.Y1 = 0; LineItem.Y2 = 0; - break; - } - - SetNumberOfLines(6); - } - - public override RenderItemType Type => RenderItemType.Reference; - - /// - /// Sets number of lines via the width or height percent - /// - internal void SetNumberOfLines(int numberOfLines) - { - var percentOf = 100d / (double)numberOfLines; - switch (type) - { - case LinePatternType.Vertical: - widthPercent = percentOf; - break; - case LinePatternType.Horizontal: - heightPercent = percentOf; - break; - } - } - - public override void Render(StringBuilder sb) - { - _items.Add(LineItem); - base.Render(sb); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/LinearGradient.cs b/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/LinearGradient.cs deleted file mode 100644 index 9c50b66e8a..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/LinearGradient.cs +++ /dev/null @@ -1,70 +0,0 @@ -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Utils; -using OfficeOpenXml.Drawing; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using EPPlus.Export.ImageRenderer.RenderItems; -using EPPlus.Fonts.OpenType.Utils; -using EPPlusImageRenderer.Constants; -using OfficeOpenXml.Drawing.Style.Coloring; -using OfficeOpenXml.Drawing.Style.Fill; -using OfficeOpenXml.Drawing.Theme; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using OfficeOpenXml.Utils; -using System.Drawing; -using System.Globalization; -using TypeConv = OfficeOpenXml.Utils.TypeConversion; - - -namespace EPPlus.Export.ImageRenderer.Svg.DefinitionUtils -{ - internal class LinearGradient : RenderItem - { - internal DrawGradientFill GradientFillExtra; - internal double Degrees; - - string _id; - bool userSpaceOnUse = false; - - public LinearGradient(DrawingBase renderer, string id) : base(renderer) - { - _id = id; - } - - public override RenderItemType Type => RenderItemType.Group; - - public override void Render(StringBuilder sb) - { - sb.Append($""); - SetStopColors(sb, GradientFillExtra, PathFillMode.Norm); - sb.Append(""); - } - - private string GetOpacity(ExcelDrawingColorManager c) - { - var opacityTransform = c.Transforms?.FirstOrDefault(x => x.Type == OfficeOpenXml.Drawing.Style.Coloring.eColorTransformType.Alpha); - if (opacityTransform == null) return ""; - - return $"stop-opacity=\"{opacityTransform.Value.ToString("0")}%\""; - } - - private void SetStopColors(StringBuilder defSb, DrawGradientFill gradientFill, PathFillMode fillMode) - { - int ix = 0; - - //Svg requires starting at 0 and moving towards 100% Excel sometimes starts at 100 - //Sort to get around that - var sortedGradientColors = gradientFill.Colors.OrderBy(x => x.Position); - - foreach (var c in sortedGradientColors) - { - var color = ColorUtils.GetAdjustedColor(fillMode, c.Color); - // TODO: check if ix should be increased...? - defSb.Append($""); - } - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/MaskGroup.cs b/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/MaskGroup.cs deleted file mode 100644 index 288095c0ea..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/MaskGroup.cs +++ /dev/null @@ -1,40 +0,0 @@ -using EPPlus.Fonts.OpenType.Utils; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.DefinitionUtils -{ - internal class MaskGroup : RenderItem - { - protected string _id = null; - - protected List _items = new List(); - - public MaskGroup(DrawingBase renderer, string id) : base(renderer) - { - _id = id; - } - - public override RenderItemType Type => RenderItemType.Group; - - public override void Render(StringBuilder sb) - { - sb.Append($""); - - foreach (var item in _items) - { - item.Render(sb); - } - - sb.Append(""); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/PatternItem.cs b/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/PatternItem.cs deleted file mode 100644 index a9a9c26931..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/PatternItem.cs +++ /dev/null @@ -1,40 +0,0 @@ -using EPPlus.Fonts.OpenType.Utils; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Globalization; - -namespace EPPlus.Export.ImageRenderer.Svg.DefinitionUtils -{ - internal class PatternItem : RenderItem - { - string _id = null; - - protected List _items = new List(); - - public PatternItem(DrawingBase baseRend, string id) : base(baseRend) - { - _id = id; - } - - protected double heightPercent = 100d; - protected double widthPercent = 100d; - - public override RenderItemType Type => RenderItemType.Group; - - public override void Render(StringBuilder sb) - { - sb.Append($""); - - foreach (var item in _items) - { - item.Render(sb); - } - - sb.Append(""); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/SymbolGroup.cs b/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/SymbolGroup.cs deleted file mode 100644 index da2ffff6c4..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/SymbolGroup.cs +++ /dev/null @@ -1,48 +0,0 @@ -using EPPlus.Fonts.OpenType.Utils; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.DefinitionUtils -{ - internal class SymbolGroup : RenderItem - { - protected const string _urlRef = "url(#{0})"; - - protected string _id = null; - - protected List _items = new List(); - - internal string Mask = null; - - public SymbolGroup(DrawingBase renderer, string id) : base(renderer) - { - _id = id; - } - - public override RenderItemType Type => RenderItemType.Group; - - public override void Render(StringBuilder sb) - { - sb.Append($""); - - foreach (var item in _items) - { - item.Render(sb); - } - - sb.Append(""); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/UtillNodes/DynamicGridDefGroup.cs b/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/UtillNodes/DynamicGridDefGroup.cs deleted file mode 100644 index 2321bc63fc..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/UtillNodes/DynamicGridDefGroup.cs +++ /dev/null @@ -1,96 +0,0 @@ -using EPPlus.Fonts.OpenType.Utils; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.DefinitionUtils.UtillNodes -{ - internal class DynamicGridDefGroup : RenderItem - { - protected const string _urlRef = "\"url(#{0})\""; - - internal string Id { get; private set; } - - internal List Items = new List(); - - internal LinePattern LnPatternHorizontal { get; private set; } - internal LinePattern LnPatternVertical { get; private set; } - - FadeOutMask maskItem; - - LinearGradient fadeOutGradient; - - DynamicGridItem gridItem; - - internal string TopId { get; private set; } - - public DynamicGridDefGroup(DrawingBase renderer, string id, int linesX, int linesY): base(renderer) - { - Id = id; - - string lnGradId = id + "_lnGradFade"; - string maskId = id + "_maskFade"; - string horzId = id + "_lnHorizontal"; - string verId = id + "_lnVertical"; - - fadeOutGradient = CreateFadeOutGradient(lnGradId); - Items.Add(fadeOutGradient); - - maskItem = new FadeOutMask(renderer, maskId, string.Format("url(#{0})", lnGradId)); - - Items.Add(maskItem); - - LnPatternHorizontal = new LinePattern(renderer, horzId, LinePatternType.Horizontal); - LnPatternVertical = new LinePattern(renderer, verId, LinePatternType.Vertical); - - SetNumLines(linesX, linesY); - - Items.Add(LnPatternHorizontal); - Items.Add(LnPatternVertical); - - gridItem = new DynamicGridItem(renderer, id, maskId, horzId, verId); - Items.Add(gridItem); - } - - internal void SetNumLines(int numLinesHorizontal, int numLinesVertical) - { - LnPatternHorizontal.SetNumberOfLines(numLinesHorizontal); - LnPatternVertical.SetNumberOfLines(numLinesVertical); - } - - LinearGradient CreateFadeOutGradient(string id) - { - var colors = new List(); - colors.Add(Color.Black); - colors.Add(Color.White); - - var stops = new List(); - stops.Add(0); - stops.Add(100); - - fadeOutGradient = new LinearGradient(DrawingRenderer, id); - fadeOutGradient.GradientFillExtra = new DrawGradientFill(colors, stops); - - return fadeOutGradient; - } - - public override RenderItemType Type => RenderItemType.Group; - - public override void Render(StringBuilder sb) - { - sb.Append($""); - - foreach (var item in Items) - { - item.Render(sb); - } - - sb.Append(""); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/UtillNodes/DynamicGridItem.cs b/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/UtillNodes/DynamicGridItem.cs deleted file mode 100644 index bfd4837d0d..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/UtillNodes/DynamicGridItem.cs +++ /dev/null @@ -1,43 +0,0 @@ -using EPPlus.Export.ImageRenderer.RenderItems; -using EPPlus.Export.ImageRenderer.RenderItems.Interfaces; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml.Drawing.Style.Fill; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.DefinitionUtils.UtillNodes -{ - internal class DynamicGridItem : SymbolGroup - { - SvgRenderRectItem HorizontalLines = null; - SvgRenderRectItem VerticalLines = null; - - public DynamicGridItem(DrawingBase renderer, string id, string maskId, string linesHorizontalId, string linesVerticalId) : base(renderer, id) - { - Mask = string.Format(_urlRef, maskId); - - HorizontalLines = IntitalizeRect(); - HorizontalLines.FillColor = string.Format(_urlRef, linesHorizontalId); - - VerticalLines = IntitalizeRect(); - VerticalLines.FillColor = string.Format(_urlRef, linesVerticalId); - - _items.Add(HorizontalLines); - _items.Add(VerticalLines); - } - - SvgRenderRectItem IntitalizeRect() - { - var rect = new SvgRenderRectItem(DrawingRenderer, Bounds); - rect.Width = 100; - rect.Height = 100; - rect.Suffix = "%"; - - return rect; - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/UtillNodes/FadeOutMask.cs b/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/UtillNodes/FadeOutMask.cs deleted file mode 100644 index 6ede276a63..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DefinitionUtils/UtillNodes/FadeOutMask.cs +++ /dev/null @@ -1,26 +0,0 @@ -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.DefinitionUtils.UtillNodes -{ - internal class FadeOutMask : MaskGroup - { - public FadeOutMask(DrawingBase renderer, string id, string rectFillId) : base(renderer, id) - { - var rect = new SvgRenderRectItem(DrawingRenderer, Bounds); - rect.Width = 100; - rect.Height = 100; - rect.Suffix = "%"; - - rect.FillColor = rectFillId; - - _items.Add(rect); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DrawingChartSettings.cs b/src/EPPlus.Export.ImageRenderer/Svg/DrawingChartSettings.cs deleted file mode 100644 index 3141abf349..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DrawingChartSettings.cs +++ /dev/null @@ -1,18 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -namespace EPPlusImageRenderer.Svg -{ - internal class DrawingChartSettings - { - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DrawingChartTitle.cs b/src/EPPlus.Export.ImageRenderer/Svg/DrawingChartTitle.cs deleted file mode 100644 index b4fd54dabb..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DrawingChartTitle.cs +++ /dev/null @@ -1,20 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -namespace EPPlusImageRenderer.Svg -{ - internal class DrawingChartTitleSettings : DrawingChartSettings - { - - } - -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DrawingContext.cs b/src/EPPlus.Export.ImageRenderer/Svg/DrawingContext.cs deleted file mode 100644 index fd2dacd785..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DrawingContext.cs +++ /dev/null @@ -1,17 +0,0 @@ -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; - -namespace EPPlus.Export.ImageRenderer.Svg -{ - internal class DrawingContext - { - public DrawingContext(T topDrawingHandler,ExcelDrawing drawing) - { - TopDrawingHandler = topDrawingHandler; - Drawing = drawing; - } - public T TopDrawingHandler { get; set; } - public ExcelDrawing Drawing { get; set; } - public ExcelTheme Theme { get; set; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/DrawingItemForTesting.cs b/src/EPPlus.Export.ImageRenderer/Svg/DrawingItemForTesting.cs deleted file mode 100644 index 4035e650b2..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/DrawingItemForTesting.cs +++ /dev/null @@ -1,58 +0,0 @@ -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - - -namespace EPPlus.Export.ImageRenderer.Svg -{ - internal class DrawingItemForTesting : DrawingBase - { - /// - /// List of items to be added to this basic container - /// - internal List ExternalRenderItems = new List(); - internal List ExternalRenderItemsNoBounds = new List(); - - internal DrawingItemForTesting(BoundingBox bounds) : base() - { - Bounds = bounds; - SvgRenderRectItem bg = new SvgRenderRectItem(this, Bounds); - - bg.Width = Bounds.Width; - bg.Height = Bounds.Height; - bg.FillColor = "blue"; - bg.FillOpacity = 0.2d; - - RenderItems.Add(bg); - } - - public void Render(StringBuilder sb) - { - RenderItems.Add(new SvgGroupItem(this)); - foreach(var item in ExternalRenderItemsNoBounds) - { - item.AppendRenderItems(RenderItems); - } - foreach(var item in ExternalRenderItems) - { - item.AppendRenderItems(RenderItems); - } - RenderItems.Add(new SvgEndGroupItem(this, Bounds)); - - sb.Append($""); - - foreach (var item in RenderItems) - { - item.Render(sb); - } - - sb.Append(""); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/IDrawingChartAxis.cs b/src/EPPlus.Export.ImageRenderer/Svg/IDrawingChartAxis.cs deleted file mode 100644 index 9ac62a9c9d..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/IDrawingChartAxis.cs +++ /dev/null @@ -1,25 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -using System.Collections.Generic; - -namespace EPPlusImageRenderer.Svg -{ - internal interface IDrawingChartAxis - { - List Values - { - get; - } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/LineMarkerHelper.cs b/src/EPPlus.Export.ImageRenderer/Svg/LineMarkerHelper.cs deleted file mode 100644 index 4542fdf01e..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/LineMarkerHelper.cs +++ /dev/null @@ -1,178 +0,0 @@ -using EPPlus.Export.ImageRenderer.Utils; -using EPPlusImageRenderer; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.Svg; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Chart; -using System; -using System.Collections.Generic; - -namespace EPPlus.Export.ImageRenderer.Svg -{ - internal class LineMarkerHelper - { - internal static RenderItem GetMarkerItem(SvgChart sc, ExcelLineChartSerie ls, double x, double y, bool isLegend) - { - SvgRenderItem item; - var m = ls.Marker; - float maxSize = isLegend ? 7f : float.MaxValue; - var size = m.Size > maxSize ? maxSize : m.Size; - var halfSize = size / 2; - var xPath = x / sc.ChartArea.Rectangle.Width; - var yPath = y / sc.ChartArea.Rectangle.Height; - var halfY = halfSize / sc.ChartArea.Rectangle.Height; - var halfX = halfSize / sc.ChartArea.Rectangle.Width; - switch (m.Style) - { - case eMarkerStyle.Circle: - item = new SvgRenderEllipseItem(sc, sc.Bounds) - { - Rx = halfSize, - Ry = halfSize, - Cx = x, - Cy = y - }; - break; - case eMarkerStyle.Triangle: - item = new SvgRenderPathItem(sc, sc.Bounds) - { - Commands = new List() - }; - var cmd = new PathCommands(PathCommandType.Move, item, new double[] { xPath + halfX, yPath + halfY, xPath, yPath - halfY, xPath - halfX, yPath + halfY }); - ((SvgRenderPathItem)item).Commands.Add(cmd); - ((SvgRenderPathItem)item).Commands.Add(new PathCommands(PathCommandType.End, item)); - break; - case eMarkerStyle.Diamond: - item = new SvgRenderPathItem(sc, sc.Drawing.GetBoundingBox()) - { - Commands = new List() - }; - - cmd = new PathCommands(PathCommandType.Move, item, new double[] { (xPath - halfX), yPath, xPath, yPath + halfY, xPath + halfX, yPath, xPath, yPath - halfY }); - ((SvgRenderPathItem)item).Commands.Add(cmd); - ((SvgRenderPathItem)item).Commands.Add(new PathCommands(PathCommandType.End, item)); - break; - case eMarkerStyle.Dot: - case eMarkerStyle.Dash: - if (isLegend) - { - item = null; - } - else - { - if(m.Style == eMarkerStyle.Dot) - { - item = new SvgRenderRectItem(sc, sc.Bounds) - { - Left = x, - Top = y - size / 8, - Width = size / 2, - Height = size / 4 - }; - } - else //Dash - { - item = new SvgRenderRectItem(sc, sc.Bounds) - { - Left = x - size / 2, - Top = y - size / 8, - Width = size, - Height = size / 4 - }; - } - } - break; - case eMarkerStyle.Square: - item = new SvgRenderRectItem(sc, sc.Bounds) - { - Left = x - size / 2, - Top = y - size / 2, - Width = size, - Height = size - }; - break; - case eMarkerStyle.Plus: - case eMarkerStyle.Star: - case eMarkerStyle.X: - var pathItem = new SvgRenderPathItem(sc, sc.Bounds) - { - Commands = new List() - }; - if (m.Style == eMarkerStyle.Star) - { - pathItem.Commands.Add(new PathCommands(PathCommandType.Move, pathItem, new double[] { xPath - halfX, yPath - halfY, xPath + halfX, yPath + halfY })); - - pathItem.Commands.Add(new PathCommands(PathCommandType.Move, pathItem, new double[] { xPath, yPath + halfY, xPath, yPath - halfY })); - - pathItem.Commands.Add(new PathCommands(PathCommandType.Move, pathItem, new double[] { xPath + halfX, yPath - halfY, xPath - halfX, yPath + halfY })); - pathItem.Commands.Add(new PathCommands(PathCommandType.End, pathItem)); - - } - else if (m.Style == eMarkerStyle.X) - { - pathItem.Commands.Add(new PathCommands(PathCommandType.Move, pathItem, new double[] { xPath - halfX, yPath - halfY, xPath + halfX, yPath + halfY })); - pathItem.Commands.Add(new PathCommands(PathCommandType.End, pathItem)); - - pathItem.Commands.Add(new PathCommands(PathCommandType.Move, pathItem, new double[] { xPath - halfX, yPath + halfY, xPath + halfX, yPath - halfY })); - pathItem.Commands.Add(new PathCommands(PathCommandType.End, pathItem)); - } - else - { - pathItem.Commands.Add(new PathCommands(PathCommandType.Move, pathItem, new double[] { xPath, yPath - halfY, xPath, yPath + halfY })); - pathItem.Commands.Add(new PathCommands(PathCommandType.End, pathItem)); - - pathItem.Commands.Add(new PathCommands(PathCommandType.Move, pathItem, new double[] { xPath - halfX, yPath, xPath + halfX, yPath })); - pathItem.Commands.Add(new PathCommands(PathCommandType.End, pathItem)); - } - item = pathItem; - break; - default: - item = null; - break; - } - if (ls.Marker.Fill.IsEmpty == false) - { - item?.SetDrawingPropertiesFill(ls.Marker.Fill, sc.Chart.StyleManager.Style.DataPointMarker.FillReference.Color); - } - else if (ls.Fill.IsEmpty) - { - item?.SetDrawingPropertiesFill(ls.Border.Fill, sc.Chart.StyleManager.Style.DataPointMarker.FillReference.Color); - } - else - { - item?.SetDrawingPropertiesFill(ls.Fill, sc.Chart.StyleManager.Style.DataPointMarker.FillReference.Color); - } - - if (ls.Marker.Border.Width > 0) - { - if (ls.Marker.Border.Fill.IsEmpty) - { - item?.SetDrawingPropertiesBorder(ls.Border, sc.Chart.StyleManager.Style.DataPointMarker.BorderReference.Color, ls.Border.Fill.Style != eFillStyle.NoFill, 0.75); - } - else - { - item?.SetDrawingPropertiesBorder(ls.Marker.Border, sc.Chart.StyleManager.Style.DataPointMarker.BorderReference.Color, ls.Marker.Border.Fill.Style != eFillStyle.NoFill, 0.75); - } - } - return item; - } - internal static RenderItem GetMarkerBackground(SvgChart sc, ExcelLineChartSerie ls, double x, double y, bool isLegend) - { - SvgRenderItem item; - var m = ls.Marker; - float maxSize = isLegend ? 7f : float.MaxValue; - var size = m.Size > maxSize ? maxSize : m.Size; - //var line = sls.SeriesIcon as SvgRenderLineItem; - item = new SvgRenderRectItem(sc, sc.Bounds) - { - Left = x - (size / 2),// line.X1 + (line.X2 - line.X1 - size) / 2, - Top = y - (size / 2), - Width = size, - Height = size - }; - item?.SetDrawingPropertiesFill(ls.Marker.Fill, sc.Chart.StyleManager.Style.DataPointMarker.FillReference.Color); - return item; - } - - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/BaseAttribute.cs b/src/EPPlus.Export.ImageRenderer/Svg/Nodes/BaseAttribute.cs deleted file mode 100644 index 6082ac7c8d..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/BaseAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.NodeAttributes -{ - internal abstract class BaseAttribute - { - public string Name { get; internal protected set; } - - internal protected string Value { get; protected set; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/BaseElement.cs b/src/EPPlus.Export.ImageRenderer/Svg/Nodes/BaseElement.cs deleted file mode 100644 index 0fbe3b05db..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/BaseElement.cs +++ /dev/null @@ -1,19 +0,0 @@ -using EPPlus.Export.ImageRenderer.Svg.NodeAttributes; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.Nodes -{ - internal class BaseElement - { - internal readonly List _attributes = new List(); - - internal string ElementName { get; set; } - - internal string Content { get; set; } - - //internal B - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/OverflowAttribute.cs b/src/EPPlus.Export.ImageRenderer/Svg/Nodes/OverflowAttribute.cs deleted file mode 100644 index 24de17d804..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/OverflowAttribute.cs +++ /dev/null @@ -1,56 +0,0 @@ -using EPPlus.Export.ImageRenderer.Svg.NodeAttributes; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.Nodes -{ - enum eOverFlowValues - { - Visible, - Hidden, - Scroll, - Auto - } - - internal class OverflowAttribute : SvgAttributeBase - { - //Default for attribute is visible: https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/overflow - private eOverFlowValues _overflowVal; - - internal eOverFlowValues OverFlowValue - { - get { return _overflowVal; } - set - { - switch (value) - { - case eOverFlowValues.Visible: - Value = "Visible"; - break; - case eOverFlowValues.Hidden: - Value = "Hidden"; - break; - case eOverFlowValues.Scroll: - Value = "Scroll"; - break; - case eOverFlowValues.Auto: - Value = "Auto"; - break; - } - _overflowVal = value; - } - } - - internal OverflowAttribute(eOverFlowValues overflowValue = eOverFlowValues.Visible) : base("Overflow") - { - OverFlowValue = overflowValue; - } - - //internal override void SetName(string newName) - //{ - // throw new System.Exception("A Type Specific Attribute cannot be set to a different name!!"); - //} - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/RenderAttribute.cs b/src/EPPlus.Export.ImageRenderer/Svg/Nodes/RenderAttribute.cs deleted file mode 100644 index 4404e4247a..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/RenderAttribute.cs +++ /dev/null @@ -1,22 +0,0 @@ -using EPPlus.Export.ImageRenderer.Svg.NodeAttributes; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.Nodes -{ - internal abstract class RenderAttribute : BaseAttribute - { - internal string Render() - { - //TODO; Make static const string to only replace minor values in? - string renderedString = $" {Name}"; - if (string.IsNullOrEmpty(Value) == false) - { - renderedString += $"=\"{Value}\""; - } - return renderedString; - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/SvgAttributeBase.cs b/src/EPPlus.Export.ImageRenderer/Svg/Nodes/SvgAttributeBase.cs deleted file mode 100644 index 8a102b75d3..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/SvgAttributeBase.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.Nodes -{ - internal class SvgAttributeBase : RenderAttribute - { - internal SvgAttributeBase(string name, string strValue = "") - { - SetName(name); - if (string.IsNullOrEmpty(strValue) == false) - { - SetValue(strValue); - } - } - - internal virtual void SetName(string newName) - { - Name = newName; - } - protected virtual void SetValue(string strValue) - { - Value = strValue; - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/SvgElement.cs b/src/EPPlus.Export.ImageRenderer/Svg/Nodes/SvgElement.cs deleted file mode 100644 index 98a3d75f5c..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/SvgElement.cs +++ /dev/null @@ -1,79 +0,0 @@ -using EPPlus.Export.ImageRenderer.Svg.Nodes; -using OfficeOpenXml.Core.CellStore; -using OfficeOpenXml.Utils; -using OfficeOpenXml.Utils.TypeConversion; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.NodeAttributes -{ - internal class SvgElement - { - internal readonly List _attributes = new List(); - - internal List _childElements = new List(); - - internal string ElementName { get; set; } - - internal string Content { get; set; } - - /// - /// If true element cannot ever have content - /// - internal bool IsVoidElement { get; private set; } - - internal SvgElement(string elementName) - { - ElementName = elementName; - } - - /// - /// Attempt to add attribute value of unknown type - /// - /// - /// - public void AddAttribute(string attributeName, object attributeValue) - { - if(attributeValue != null) - { - //var objStr = string.Format(CultureInfo.InvariantCulture, attributeValue.ToString()); - var objStr = ConvertUtil.GetValueForXml(attributeValue, true); - AddAttribute(attributeName, objStr); - } - } - - /// - /// Add attribute with a value - /// - /// - /// - public void AddAttribute(string attributeName, string attributeValue) - { - Require.Argument(attributeName).IsNotNullOrEmpty("attributeName"); - Require.Argument(attributeValue).IsNotNullOrEmpty("attributeValue"); - _attributes.Add(new SvgAttributeBase(attributeName, attributeValue)); - } - - /// - /// Add attribute without it having a value - /// - /// - public void AddAttributeValueLess(string attributeName) - { - Require.Argument(attributeName).IsNotNullOrEmpty("attributeName"); - _attributes.Add(new SvgAttributeBase(attributeName)); - } - - /// - /// Add child element - /// - /// - public void AddChildElement(SvgElement element) - { - _childElements.Add(element); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/SvgElements.cs b/src/EPPlus.Export.ImageRenderer/Svg/Nodes/SvgElements.cs deleted file mode 100644 index 8312d6eb9b..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Nodes/SvgElements.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.Nodes -{ - internal static class SvgElements - { - internal static readonly HashSet VoidElements - = new HashSet - { - //{ "col" }, - //{ Img }, - //{ "input" } - }; - - internal static readonly HashSet NoIndentElements - = new HashSet - { - //{ TableData }, - //{ TFoot }, - //{ TableHeader }, - //{ A }, - //{ Img } - }; - - //public const string Body = "body"; - //public const string Table = "table"; - //public const string Thead = "thead"; - //public const string TFoot = "tfoot"; - //public const string Tbody = "tbody"; - //public const string TableRow = "tr"; - //public const string TableHeader = "th"; - //public const string TableData = "td"; - //public const string A = "a"; - //public const string Span = "span"; - //public const string ColGroup = "colgroup"; - //public const string Img = "img"; - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/SvgLegendSerie.cs b/src/EPPlus.Export.ImageRenderer/Svg/SvgLegendSerie.cs deleted file mode 100644 index ef933486e2..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/SvgLegendSerie.cs +++ /dev/null @@ -1,22 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlusImageRenderer.RenderItems; - -namespace EPPlusImageRenderer.Svg -{ - internal class SvgLegendSerie : SvgLegendSeriesIcon - { - internal TextBodyItem Textbox { get; set; } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/SvgLegendSeriesIcon.cs b/src/EPPlus.Export.ImageRenderer/Svg/SvgLegendSeriesIcon.cs deleted file mode 100644 index c361eb47f5..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/SvgLegendSeriesIcon.cs +++ /dev/null @@ -1,15 +0,0 @@ -using EPPlusImageRenderer.RenderItems; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlusImageRenderer.Svg -{ - internal class SvgLegendSeriesIcon - { - internal RenderItem SeriesIcon { get; set; } - internal RenderItem MarkerIcon { get; set; } - internal RenderItem MarkerBackground { get; set; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Svg/SvgRange.cs b/src/EPPlus.Export.ImageRenderer/Svg/SvgRange.cs deleted file mode 100644 index a7cc43b622..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/SvgRange.cs +++ /dev/null @@ -1,89 +0,0 @@ -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg -{ - //internal class SvgRange - //{ - // List renderItems = new List(); - // //List textBoxes = new List(); - - // internal SvgRange(ExcelRange range, double totalWidth, double totalHeight) - // { - // SvgRenderRectItem rangeBB = new SvgRenderRectItem(); - - // //To pixel multiplier - // float mult = 96f / 72f; - - // rangeBB.Width = (float)totalWidth; - // rangeBB.Height = (float)totalHeight * mult; - - // rangeBB.BorderColor = "yellow"; - - // rangeBB.BorderWidth = 1; - - // renderItems.Add(rangeBB); - - // float currentWidth = 0f; - - // for (int i = 0; i < range.Columns; i++) - // { - // float currentHeight = 0f; - // var currCol = range.Worksheet.GetColumn(range._fromCol + i); - // var colWidth = currCol == null ? range.Worksheet.DefaultColWidth : currCol.Width; - // colWidth = ExcelColumn.ColumnWidthToPixels(colWidth, range.Worksheet.Workbook.MaxFontWidth); - - // for (int j = 0; j < range.Rows; j++) - // { - // var cell = range.Offset(j, i); - - // var cellContent = range.Offset(j, i).TextForWidth; - // float heightAlt = (float)cell.Worksheet.GetRowHeight(cell._fromRow + j); - // float heightAltPixels = heightAlt * mult; - // float height = (float)cell.Worksheet.Rows[cell._fromRow].Height * mult; - - // SvgRenderRectItem cellBB = new SvgRenderRectItem(); - // cellBB.Left = currentWidth; - // cellBB.Top = currentHeight; - - // cellBB.Width = (float)colWidth; - // cellBB.Height = (float)height; - - // cellBB.BorderWidth = 1; - - // cellBB.BorderColor = "black"; - // cellBB.FillColor = "gray"; - - // renderItems.Add(cellBB); - - // var cellTextBox = new RenderTextbox(null, currentWidth, currentHeight, colWidth, height); - // cellTextBox.AddCellTextRun(cell); - - // var deltaHeight = (float)cellTextBox.Bounds.Height; - // cellBB.Height = cellBB.Height < deltaHeight ? deltaHeight : cellBB.Height; - // textBoxes.Add(cellTextBox); - - // currentHeight += height; - // } - // currentWidth += (float)colWidth; - // } - - // } - - // internal void Render(StringBuilder sb) - // { - // foreach (var item in renderItems) - // { - // item.Render(sb); - // } - // foreach (var item in textBoxes) - // { - // item.RenderTextRuns(sb); - // } - // } - //} -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/SvgShape.cs b/src/EPPlus.Export.ImageRenderer/Svg/SvgShape.cs deleted file mode 100644 index 934cd56d9a..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/SvgShape.cs +++ /dev/null @@ -1,465 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.RenderItems.Shared; -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; -using EPPlus.Export.ImageRenderer.Utils; -using EPPlus.Fonts.OpenType; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using EPPlusImageRenderer.RenderItems; -using EPPlusImageRenderer.ShapeDefinitions; -using EPPlusImageRenderer.Utils; -using OfficeOpenXml; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Theme; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -using OfficeOpenXml.Style; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using TypeConv = OfficeOpenXml.Utils.TypeConversion; - -namespace EPPlusImageRenderer.Svg -{ - internal class SvgShape : DrawingShape - { - /// - /// Calculated shape textbox - /// - SvgRenderRectItem InsetTextBox; - - SvgRenderRectItem MarginTextBox; - - /// - /// Textbox from memory - /// - public SvgTextBodyItem TextBodySvg { get; internal set; } - - public SvgShape(ExcelShape shape) : base(shape) - { - var style = shape.Style; - - if (style==eShapeStyle.CustomShape) - { - foreach (var path in shape.CustomGeom.DrawingPaths) - { - AddFromPaths(path); - } - } - else - { - var shapeDef = PresetShapeDefinitions.ShapeDefinitions[style].Clone(); - shapeDef.Calculate(shape); - - RenderItems.Add(new SvgGroupItem(this, shape.GetBoundingBox(), shape.Rotation)); - - //Draw Filled path's - foreach (var path in shapeDef.ShapePaths) - { - if (path.Fill != PathFillMode.None) - { - AddFromPaths(path, true, false); - } - } - - //Draw border path's - foreach (var path in shapeDef.ShapePaths) - { - if (path.Stroke) - { - AddFromPaths(path, false, true); - } - } - - RenderItems.Add(new SvgEndGroupItem(this, Bounds)); - - if (_shape.Text != null) - { - if (shapeDef.TextBoxRect != null) - { - InsetTextBox = new SvgRenderRectItem(this, Bounds); - InsetTextBox.Bounds.Left = (float)shapeDef.TextBoxRect.LeftValue.PixelToPoint(); - InsetTextBox.Bounds.Top = (float)shapeDef.TextBoxRect.TopValue.PixelToPoint(); - InsetTextBox.Bounds.Left = (float)shapeDef.TextBoxRect.LeftValue.PixelToPoint(); - InsetTextBox.Bounds.Top = (float)shapeDef.TextBoxRect.TopValue.PixelToPoint(); - InsetTextBox.FillOpacity = 0.3d; - - if (shape.TextBody.TextAutofit != eTextAutofit.ShapeAutofit) - { - InsetTextBox.Width = ((double)((float)shapeDef.TextBoxRect.RightValue - (float)shapeDef.TextBoxRect.LeftValue)).PixelToPoint(); - InsetTextBox.Height = ((double)((float)shapeDef.TextBoxRect.BottomValue - (float)shapeDef.TextBoxRect.TopValue)).PixelToPoint(); - InsetTextBox.Width = ((double)((float)shapeDef.TextBoxRect.RightValue - (float)shapeDef.TextBoxRect.LeftValue)).PixelToPoint(); - InsetTextBox.Height = ((double)((float)shapeDef.TextBoxRect.BottomValue - (float)shapeDef.TextBoxRect.TopValue)).PixelToPoint(); - } - else - { - InsetTextBox.Width = (float)shapeDef.TextBoxRect.RightValue.PixelToPoint(); - InsetTextBox.Height = (float)shapeDef.TextBoxRect.BottomValue.PixelToPoint(); - InsetTextBox.Width = (float)shapeDef.TextBoxRect.RightValue.PixelToPoint(); - InsetTextBox.Height = (float)shapeDef.TextBoxRect.BottomValue.PixelToPoint(); - } - } - else - { - InsetTextBox = null; - } - - InsetTextBox.FillOpacity = 0.3d; - - TextBodySvg = CreateTextBodyItem(_shape.TextBody); - } - } - } - - protected void AddFromPaths(DrawingPath path, bool drawFill = true, bool drawBorder = true) - { - var pi = new SvgRenderPathItem(this, _shape.GetBoundingBox()); - var coordinates = new List(); - PathCommands cmd = null; - PathsBase pCmd = null; - double cx = 0, cy = 0; - foreach (var p in path.Paths) - { - switch (p.Type) - { - case PathDrawingType.MoveTo: - AddCmd(pi, path, coordinates, ref cmd, pCmd, p, PathCommandType.Move); - break; - case PathDrawingType.LineTo: - AddCmd(pi, path, coordinates, ref cmd, pCmd, p, PathCommandType.Line); - break; - case PathDrawingType.CubicBezierTo: - AddCmd(pi, path, coordinates, ref cmd, pCmd, p, PathCommandType.CubicBézier); - break; - case PathDrawingType.QuadBezierTo: - AddCmd(pi, path, coordinates, ref cmd, pCmd, p, PathCommandType.QuadraticBézier); - break; - case PathDrawingType.ArcTo: - SetCmdCoordinats(cmd, p, coordinates); - AddArc(pi, path, coordinates, pCmd, out cx, out cy, p); - cmd = null; - break; - case PathDrawingType.Close: - if (pi.Commands[pi.Commands.Count - 1].Type != PathCommandType.Arc) - { - pi.Commands[pi.Commands.Count - 1].Coordinates = coordinates.ToArray(); - coordinates.Clear(); - } - pi.Commands.Add(new PathCommands(PathCommandType.End, pi)); - cmd = null; - break; - } - //p.TranslateCoordiantesToPointsAndDegrees(ExcelDrawing.EMU_PER_POINT, 1); - pCmd = p; - } - if (coordinates.Count > 0) - { - pi.Commands[pi.Commands.Count - 1].Coordinates = coordinates.ToArray(); - } - if (drawFill) - { - pi.FillColorSource = path.Fill; - pi.SetDrawingPropertiesFill(_shape.Fill, _shape.ThemeStyles.FillReference.Color); - } - else - { - pi.FillColorSource = PathFillMode.None; - pi.FillColor = "none"; - } - - if (drawBorder) - { - pi.BorderColorSource = path.Stroke ? PathFillMode.Norm : PathFillMode.None; - pi.SetDrawingPropertiesBorder(_shape.Border, _shape.ThemeStyles.BorderReference.Color, path.Stroke); - } - else - { - pi.BorderColorSource = PathFillMode.None; - pi.BorderColor = "none"; - } - - RenderItems.Add(pi); - } - public string ViewBox - { - get - { - double l=0, t=0, r=1, b=1; - foreach(var item in RenderItems) - { - item.GetBounds(out var il, out var it, out var ir, out var ib); - if(ilr) - { - r = ir; - } - if(ib>b) - { - b = ib; - } - } - return $"{(Bounds.Left).PointToPixelString()},{Bounds.Top.PointToPixelString()},{Bounds.Right.PointToPixelString()},{Bounds.Bottom.PointToPixelString()}"; - } - } - - public void Render(StringBuilder sb) - { - sb.Append($""); - - //Write defs used for gradient colors - var writer = new SvgDrawingWriter(this); - writer.WriteSvgDefs(sb, RenderItems); - - - //SvgGroupItem gItemTest = null; - foreach (var item in RenderItems) - { - item.Render(sb); - //if(item.Type == RenderItemType.Group && gItemTest == null) - //{ - // gItemTest = (SvgGroupItem)item; - //} - //if (item.IsEndOfGroup && gItemTest != null) - //{ - // gItemTest.RenderEndGroup(sb); - //} - } - //if (!string.IsNullOrEmpty(_shape.Text))t - //if (!string.IsNullOrEmpty(_shape.Text))t - //{ - // //RenderText(sb); - //} - - //if (gItemTest != null) - //{ - // gItemTest.RenderEndGroup(sb); - //} - - //RenderDebugTextBox(sb); - sb.AppendLine(""); - } - - SvgTextBodyItem CreateTextBodyItem(ExcelTextBody bodyOrig) - { - if (InsetTextBox == null) - { - GetShapeInnerBound(out double x, out double y, out double width, out double height); - InsetTextBox = new SvgRenderRectItem(this, Bounds); - InsetTextBox.Bounds.Left = x.PixelToPoint(); - InsetTextBox.Bounds.Top = y.PixelToPoint(); - InsetTextBox.Width = width.PixelToPoint(); - InsetTextBox.Height = height.PixelToPoint(); - //InsetTextBox.Bounds.Parent = RenderTextbox.Parent; //TODO:Check that textBody is correct. - InsetTextBox.Bounds.Left = x.PixelToPoint(); - InsetTextBox.Bounds.Top = y.PixelToPoint(); - InsetTextBox.Width = width.PixelToPoint(); - InsetTextBox.Height = height.PixelToPoint(); - //InsetTextBox.Bounds.Parent = RenderTextbox.Parent; //TODO:Check that textBody is correct. - } - - double l, r, t, b; - bodyOrig.GetInsetsOrDefaults(out l, out t, out r, out b); - - MarginTextBox = new SvgRenderRectItem(this, this.Bounds); - - MarginTextBox.Top = t + InsetTextBox.Top; - MarginTextBox.Left = l + InsetTextBox.Left; - MarginTextBox.Width = InsetTextBox.Width - r - l; - MarginTextBox.Height = InsetTextBox.Height - b - t; - - RenderItems.Add(new SvgGroupItem(this, MarginTextBox.Bounds)); - - var txtBodyItem = new SvgTextBodyItem(this, MarginTextBox.Bounds, 0, 0, MarginTextBox.Width, MarginTextBox.Height); - txtBodyItem.ImportTextBody(bodyOrig); - - txtBodyItem.AppendRenderItems(RenderItems); - - RenderItems.Add(new SvgEndGroupItem(this, Bounds)); - //txtBodyItem.Width = InsetTextBox.Width; - - return txtBodyItem; - } - - //private void RenderText(StringBuilder sb) - //{ - // //RenderDebugTextBox(sb); - // textBody.Render(sb); - //} - - private void RenderDebugTextBox(StringBuilder sb) - { - InsetTextBox.FillOpacity = 0.3d; - InsetTextBox.FillColor = "green"; - InsetTextBox.Render(sb); - - MarginTextBox.FillColor = "red"; - MarginTextBox.FillOpacity = 0.3; - MarginTextBox.Render(sb); - //InsetTextBox.GetBounds(out double l, out double t, out double r, out double b); - - //var area = textBody.Bounds; - - ////Temporarily set as child bounds - //insetTextBox.Bounds.Left = (float)area.Left + l; - //insetTextBox.Bounds.Top = (float)area.Top + t; - //insetTextBox.Bounds.Width = (float)area.Width; - //insetTextBox.Bounds.Height = (float)area.Height; - - //insetTextBox.FillColor = "blue"; - - ////Render the inner area - //insetTextBox.Render(sb); - - ////Reset variables so that the rendering of children later aren't affected - //insetTextBox.Bounds.Left = l; - //insetTextBox.Bounds.Top = t; - //insetTextBox.Bounds.Right = r; - //insetTextBox.Bounds.Bottom = b; - } - - private void GetShapeInnerBound(out double x, out double y, out double width, out double height) - { - double currentX = 0, currentY = 0, xe, ye; - x = y = 0; - width = xe = Bounds.Width; - height = ye = Bounds.Height; - foreach (var ri in RenderItems) - { - switch (ri.Type) - { - case RenderItemType.Rect: - var rectItem = (SvgRenderRectItem)ri; - x = rectItem.Left; - y = rectItem.Top; - width = rectItem.Width; - height = rectItem.Height; - break; - case RenderItemType.Path: - var pathItem = (SvgRenderPathItem)ri; - foreach (var cmd in pathItem.Commands) - { - var cmdCoordinates = new List(); - for (int i = 0; i < cmd.Coordinates.Length; i++) - { - switch (cmd.Type) - { - case PathCommandType.Move: - if (i == 0) - { - currentX = cmd.Coordinates[i]; - currentY = cmd.Coordinates[++i]; - } - else - { - HandleLine(ref currentX, ref currentY, ref xe, ref ye, cmd, cmdCoordinates, ref i); - } - break; - case PathCommandType.VerticalLine: - HandleVertical(y, ref currentY, ref xe, cmd.Coordinates[i]); - break; - case PathCommandType.HorizontalLine: - HandleHorizontal(x, ref currentX, ref xe, cmd.Coordinates[i]); - break; - case PathCommandType.Line: - HandleLine(ref currentX, ref currentY, ref xe, ref ye, cmd, cmdCoordinates, ref i); - break; - case PathCommandType.CubicBézier: - if (currentX > x) - { - x = currentX; - } - if (currentY > y) - { - y = currentY; - } - i += 4; - break; - - } - } - } - break; - } - } - if (xe != double.MinValue) - { - width = xe - x; - } - if (ye != double.MinValue) - { - height = ye - y; - } - } - - private static void HandleLine(ref double currentX, ref double currentY, ref double xe, ref double ye, PathCommands cmd, List cmdCoordinates, ref int i) - { - xe = cmd.Coordinates[i]; - ye = cmd.Coordinates[++i]; - if (xe == currentX || ye == currentY) - { - if (cmdCoordinates.Count == 0) - { - cmdCoordinates.Add(new Coordinate(currentX, currentY)); - } - cmdCoordinates.Add(new Coordinate(xe, ye)); - } - else - { - var w = Math.Abs(xe - currentX); - var h = Math.Abs(ye - currentY); - cmdCoordinates.Add(new Coordinate((Math.Min(xe, currentX) + w) / 2, (Math.Min(ye, currentY) + h) / 2)); - } - currentX = xe; - currentY = ye; - } - - - private static void HandleVertical(double y, ref double currentY, ref double ye, double yec) - { - if (currentY < y || currentY == double.MinValue) - { - currentY = y; - } - if (ye > yec || ye == double.MinValue) - { - ye = yec; - } - } - private static void HandleHorizontal(double x, ref double currentX, ref double xe, double xec) - { - if (currentX < x || currentX == double.MinValue) - { - currentX = x; - } - if (xe > xec || xe == double.MinValue) - { - xe = xec; - } - } - - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Svg/Writer/SvgWriter.cs b/src/EPPlus.Export.ImageRenderer/Svg/Writer/SvgWriter.cs deleted file mode 100644 index 152cbb4b25..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Svg/Writer/SvgWriter.cs +++ /dev/null @@ -1,144 +0,0 @@ -using EPPlus.Export.ImageRenderer.Svg.NodeAttributes; -using EPPlus.Export.ImageRenderer.Svg.Nodes; -using OfficeOpenXml.Export.HtmlExport.Writers; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Svg.Writer -{ - internal class SvgWriter : BaseWriter - { - internal SvgWriter(Stream stream, Encoding encoding) : base(stream, encoding) - { - } - - public void RenderEndTag(string elementName) - { - if (_newLine) - { - WriteIndent(); - } - - _writer.Write($""); - _writer.Flush(); - } - - public void RenderBeginTag(string elementName, List attributes = null, bool closeElement = false) - { - _newLine = false; - - WriteIndent(); - //// avoid writing indent characters for a hyperlinks or images inside a td element - //if (elementName != HtmlElements.A && elementName != HtmlElements.Img) - //{ - // WriteIndent(); - //} - _writer.Write($"<{elementName}"); - - - if (attributes != null) - { - foreach (var attribute in attributes) - { - _writer.Write($" {attribute.Name}=\"{attribute.Value}\""); - } - attributes.Clear(); - } - - if (closeElement) - { - _writer.Write("/>"); - _writer.Flush(); - } - else - { - _writer.Write(">"); - } - } - - public void RenderSvgElementWithoutEndNode(SvgElement element, bool minify) - { - RenderBeginTag(element.ElementName, element._attributes, element.IsVoidElement); - - if (element.IsVoidElement) - { - ApplyFormat(minify); - //if (element.ElementName != SvgElements.Img) - //{ - //ApplyFormat(minify); - // } - return; - } - - if (element._childElements.Count > 0) - { - var name = element.ElementName; - bool noIndent = minify == true ? true : SvgElements.NoIndentElements.Contains(name); - - ApplyFormatIncreaseIndent(noIndent); - - foreach (var child in element._childElements) - { - RenderSvgElement(child, minify); - } - - if (noIndent == false) - { - Indent--; - } - } - - Write(element.Content); - - if (element.ElementName != "a") - { - ApplyFormat(minify); - } - } - - public void RenderSvgElement(SvgElement element, bool minify) - { - //RenderSvgElementWithoutEndNode(element, minify); - RenderBeginTag(element.ElementName, element._attributes, element.IsVoidElement); - - if (element.IsVoidElement) - { - ApplyFormat(minify); - //if (element.ElementName != SvgElements.Img) - //{ - //ApplyFormat(minify); - // } - return; - } - - if (element._childElements.Count > 0) - { - var name = element.ElementName; - bool noIndent = minify == true ? true : SvgElements.NoIndentElements.Contains(name); - - ApplyFormatIncreaseIndent(noIndent); - - foreach (var child in element._childElements) - { - RenderSvgElement(child, minify); - } - - if (noIndent == false) - { - Indent--; - } - } - - Write(element.Content); - - RenderEndTag(element.ElementName); - if (element.ElementName != "a") - { - ApplyFormat(minify); - } - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/SvgAdjusmentPoints.cs b/src/EPPlus.Export.ImageRenderer/SvgAdjusmentPoints.cs deleted file mode 100644 index a2408e22fd..0000000000 --- a/src/EPPlus.Export.ImageRenderer/SvgAdjusmentPoints.cs +++ /dev/null @@ -1,24 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -using System.Collections.Generic; - -namespace EPPlusImageRenderer -{ - internal class SvgAdjustmentPoint - { - public int ItemIndex { get; set; } - public List Commands { get; set; } = null; - public AdjustmentType AdjustmentType { get; set; } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/SvgCommand.cs b/src/EPPlus.Export.ImageRenderer/SvgCommand.cs deleted file mode 100644 index ddd40b7bc3..0000000000 --- a/src/EPPlus.Export.ImageRenderer/SvgCommand.cs +++ /dev/null @@ -1,34 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -using System.Collections.Generic; -using System.Linq; - -namespace EPPlusImageRenderer -{ - internal struct SvgCommand - { - public SvgCommand(int itemIndex) - { - Index = itemIndex; - Coordinates = new Dictionary(); - } - public SvgCommand(int itemIndex, params SvgCoordinate[] coordinates) - { - Index = itemIndex; - Coordinates = coordinates.ToDictionary(x=>x.Value, y=>y); - } - public int Index { get; set; } - public Dictionary Coordinates { get; set; } - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/SvgCoordinate.cs b/src/EPPlus.Export.ImageRenderer/SvgCoordinate.cs deleted file mode 100644 index a5a36ed8c3..0000000000 --- a/src/EPPlus.Export.ImageRenderer/SvgCoordinate.cs +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ - -namespace EPPlusImageRenderer -{ - public struct SvgCoordinate - { - public static implicit operator SvgCoordinate(short value) - { - return new SvgCoordinate(value); - } - public static implicit operator short(SvgCoordinate value) - { - return value.Value; - } - public SvgCoordinate(short value) - { - Origin = default; - Value = value; - PointName = default; - Type = default; - } - internal short Origin { get; set; } - internal short Value { get; set; } - internal string PointName { get; set; } - internal AdjustmentPointType Type { get; set; } - public override int GetHashCode() - { - return Value.GetHashCode(); - } - public override bool Equals(object obj) - { - if (obj is SvgCoordinate c) - { - return Value.Equals(c.Value); - } - return base.Equals(obj); - } - - } -} \ No newline at end of file diff --git a/src/EPPlus.Export.ImageRenderer/Utils/ColorUtils.cs b/src/EPPlus.Export.ImageRenderer/Utils/ColorUtils.cs deleted file mode 100644 index 432ea57544..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Utils/ColorUtils.cs +++ /dev/null @@ -1,43 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.DrawingRenderer; -using OfficeOpenXml.Drawing; -using System.Drawing; -using TC = OfficeOpenXml.Utils.TypeConversion; - -namespace EPPlusImageRenderer.Utils -{ - internal static class ColorUtils - { - internal static Color GetAdjustedColor(PathFillMode fillColorSource, Color fc) - { - switch (fillColorSource) - { - case PathFillMode.Darken: - fc = TC.ColorConverter.ApplyBlend(fc, Color.Black, 0.4); - break; - case PathFillMode.DarkenLess: - fc = TC.ColorConverter.ApplyBlend(fc, Color.Black, 50D/255D); - break; - case PathFillMode.LightenLess: - fc = TC.ColorConverter.ApplyBlend(fc, Color.White, 50D / 255D); - break; - case PathFillMode.Lighten: - fc = TC.ColorConverter.ApplyBlend(fc, Color.White, 0.4); - break; - } - - return fc; - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Utils/ConvertUtil.cs b/src/EPPlus.Export.ImageRenderer/Utils/ConvertUtil.cs deleted file mode 100644 index 2687999062..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Utils/ConvertUtil.cs +++ /dev/null @@ -1,972 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 01/27/2020 EPPlus Software AB Initial release EPPlus 5 - *************************************************************************************************/ -using System; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.IO; -using OfficeOpenXml.Compatibility; -using System.Xml; -using OfficeOpenXml.FormulaParsing.Excel.Functions; -using OfficeOpenXml.FormulaParsing.Exceptions; -using OfficeOpenXml.FormulaParsing; -using OfficeOpenXml.FormulaParsing.Utilities; -using OfficeOpenXml.FormulaParsing.Excel.Operators; - -namespace OfficeOpenXml.Utils.TypeConversion -{ - internal static class ConvertUtil - { - static class ParseArguments - { - public static NumberStyles Number = NumberStyles.Float | NumberStyles.AllowThousands; - public static DateTimeStyles DateTime = DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal; - } - internal static bool IsNumericOrDate(object candidate) - { - if (candidate == null) return false; - var t = candidate.GetType(); - if (t.IsPrimitive) return true; - return t == typeof(decimal) || t == typeof(DateTime) || t == typeof(TimeSpan); - } - internal static bool IsNumeric(object candidate) - { - if (candidate == null) return false; - var t = candidate.GetType(); - if (t.IsPrimitive) return true; - return t == typeof(decimal); - } - internal static bool IsExcelNumeric(object candidate) - { - if (candidate == null) return false; - var t = candidate.GetType(); - if (t.IsPrimitive && t != typeof(bool)) return true; - return t == typeof(decimal) || t == typeof(DateTime) || t == typeof(TimeSpan); - } - - internal static bool IsNumericOrDate(object candidate, bool includeNumericString, bool includePercentageString) - { - if (IsNumericOrDate(candidate)) return true; - if (candidate != null) - { - if (includeNumericString && TryParseNumericString(candidate.ToString(), out double d, CultureInfo.CurrentCulture)) - { - return true; - } - else if (includePercentageString && IsPercentageString(candidate.ToString())) - { - return true; - } - } - return false; - } - - internal static bool IsPercentageString(string s) - { - if (string.IsNullOrEmpty(s)) return false; - s = s.Trim(); - if (s.Trim().EndsWith("%")) - { - var tmp = s; - int n = 0; - while (tmp.Length > 0 && tmp.Last() == '%') - { - tmp = tmp.Substring(0, tmp.Length - 1); - n++; - if (n > 1) - { - return false; - } - } - return double.TryParse(tmp, out double n2); - } - return false; - } - /// - /// Tries to parse a double from the specified which is expected to be a string value. - /// - /// The string value. - /// The double value parsed from the specified . - /// Other than Current culture - /// True if could be parsed to a double; otherwise, false. - internal static bool TryParseNumericString(string candidateString, out double numericValue, CultureInfo cultureInfo = null) - { - if (!string.IsNullOrEmpty(candidateString)) - { - return double.TryParse(candidateString, ParseArguments.Number, cultureInfo ?? CultureInfo.CurrentCulture, out numericValue); - } - numericValue = 0; - return false; - } - - internal static bool TryParsePercentageString(string s, out double numericValue, CultureInfo cultureInfo = null) - { - numericValue = 0; - if (string.IsNullOrEmpty(s)) return false; - s = s.Trim(); - if (s.Trim().EndsWith("%")) - { - var tmp = s; - while (tmp.Length > 0 && tmp.Last() == '%') - { - tmp = tmp.Substring(0, tmp.Length - 1); - } - if (TryParseNumericString(tmp, out numericValue, cultureInfo)) - { - numericValue /= 100d; - return true; - } - } - return false; - } - - /// - /// Tries to parse a boolean value from the specificed . - /// - /// The value to check for boolean-ness. - /// The boolean value parsed from the specified . - /// True if could be parsed - internal static bool TryParseBooleanString(string candidateString, out bool result) - { - if (!string.IsNullOrEmpty(candidateString)) - { - if (candidateString == "-1" || candidateString == "1") - { - result = true; - return true; - } - else if (candidateString == "0") - { - result = false; - return true; - } - else - { - return bool.TryParse(candidateString, out result); - } - } - result = false; - return false; - } - internal static bool ToBooleanString(string candidateString, bool defaultValue = false) - { - if (!string.IsNullOrEmpty(candidateString)) - { - if (candidateString == "-1" || candidateString == "1") - { - return true; - } - else if (candidateString == "0") - { - return false; - } - else - { - if (bool.TryParse(candidateString, out bool result)) - { - return result; - } - } - } - return defaultValue; - } - - /// - /// Tries to parse an int value from the specificed . - /// - /// The value to check for boolean-ness. - /// The boolean value parsed from the specified . - /// True if could be parsed - internal static bool TryParseIntString(string candidateString, out int result) - { - if (!string.IsNullOrEmpty(candidateString)) - return int.TryParse(candidateString, out result); - result = 0; - return false; - } - internal static int? GetValueIntNull(string s) - { - if (string.IsNullOrEmpty(s)) return null; - return int.Parse(s, CultureInfo.InvariantCulture); - } - internal static long? GetValueLongNull(string s) - { - if (string.IsNullOrEmpty(s)) return null; - return long.Parse(s, CultureInfo.InvariantCulture); - } - /// - /// Tries to parse a from the specified which is expected to be a string value. - /// - /// The string value. - /// The double value parsed from the specified . - /// True if could be parsed to a double; otherwise, false. - internal static bool TryParseDateString(string candidateString, out DateTime result) - { - if (!string.IsNullOrEmpty(candidateString)) - { - return DateTime.TryParse(candidateString, CultureInfo.CurrentCulture, ParseArguments.DateTime, out result); - } - result = DateTime.MinValue; - return false; - } - /// - /// Convert an object value to a double - /// - /// - /// - /// Return NaN if invalid double otherwise 0 - /// - internal static double GetValueDouble(object v, bool ignoreBool = false, bool retNaN = false) - { - double d; - try - { - if (ignoreBool && v is bool) - { - return 0; - } - if (IsNumericOrDate(v)) - { - if (v is DateTime) - { - d = ((DateTime)v).ToOADate(); - } - else if (v is TimeSpan) - { - d = DateTime.FromOADate(0).Add((TimeSpan)v).ToOADate(); - } - else - { - d = Convert.ToDouble(v, CultureInfo.InvariantCulture); - } - } - else - { - if (v is KahanSum ks) - { - d = ks.Get(); - } - else - { - d = retNaN ? double.NaN : 0; - } - } - } - - catch - { - d = retNaN ? double.NaN : 0; - } - return d; - } - - internal static double GetValueDouble(object v, bool ignoreBool, bool retNaN, bool includeStrings) - { - double d; - try - { - if (ignoreBool && v is bool) - { - return 0; - } - if (IsNumericOrDate(v)) - { - if (v is DateTime) - { - d = ((DateTime)v).ToOADate(); - } - else if (v is TimeSpan) - { - d = DateTime.FromOADate(0).Add((TimeSpan)v).ToOADate(); - } - else - { - d = Convert.ToDouble(v, CultureInfo.InvariantCulture); - } - } - else if (includeStrings && v != null) - { - if (TryParseNumericString(v.ToString(), out double val, CultureInfo.CurrentCulture)) - { - d = val; - } - else if (TryParsePercentageString(v.ToString(), out double percentage, CultureInfo.CurrentCulture)) - { - d = percentage; - } - else - { - d = retNaN ? double.NaN : 0; - } - } - else - { - d = retNaN ? double.NaN : 0; - } - } - - catch - { - d = retNaN ? double.NaN : 0; - } - return d; - } - internal static bool? GetValueBool(object v) - { - if (v is IRangeInfo) - { - var r = ((IRangeInfo)v).FirstOrDefault(); - v = r == null ? null : r.Value; - } - if (v == null) return false; - if (v is bool) return (bool)v; - if (v.IsNumeric()) return Convert.ToBoolean(v); - bool result; - if (bool.TryParse(v.ToString(), out result)) - { - return result; - } - return null; - } - internal static DateTime? GetValueDate(object v) - { - if (v is DateTime d) - { - return d; - } - else - { - try - { - if (IsNumericOrDate(v)) - { - var n = GetValueDouble(v); - if (double.IsNaN(n)) - { - return null; - } - else - { - return DateTime.FromOADate(n); - } - } - } - catch - { - return null; - } - } - return null; - } - internal static string ExcelEscapeString(string s) - { - return s.Replace("&", "&"). - Replace("<", "<"). - Replace(">", ">"); - } - internal static string HtmlEscapeString(string s) - { - return s.Replace("&", "&"). - Replace("<", "<"). - Replace(">", ">"). - Replace("\"", """). - Replace("'", "'"); - } - /// - /// Return true if preserve space attribute is set. - /// - /// - /// - /// - internal static void ExcelEncodeString(StreamWriter sw, string t) - { - if (Regex.IsMatch(t, "(_x[0-9A-Fa-f]{4,4}_)")) - { - var match = Regex.Match(t, "(_x[0-9A-Fa-f]{4,4}_)"); - int indexAdd = 0; - while (match.Success) - { - t = t.Insert(match.Index + indexAdd, "_x005F"); - indexAdd += 6; - match = match.NextMatch(); - } - } - for (int i = 0; i < t.Length; i++) - { - if (t[i] <= 0x1f && t[i] != '\t' && t[i] != '\n' && t[i] != '\r') //Not Tab, CR or LF - { - sw.Write("_x00{0}_", (t[i] < 0xf ? "0" : "") + ((int)t[i]).ToString("X")); - } - else if (t[i] > 0xFFFD) - { - sw.Write($"_x{((int)t[i]).ToString("X")}_"); - } - else - { - sw.Write(t[i]); - } - } - - } - /// - /// Return true if preserve space attribute is set. - /// - /// - /// - /// - /// - internal static void ExcelEncodeString(StringBuilder sb, string t, bool encodeTabLF = false) - { - if (Regex.IsMatch(t, "(_x[0-9A-Fa-f]{4,4}_)")) - { - var matches = Regex.Matches(t, "(_x[0-9A-Fa-f]{4,4})"); - int indexAdd = 0; - foreach (Match m in matches) - { - t = t.Insert(m.Index + indexAdd, "_x005F"); - indexAdd += 6; - } - } - for (int i = 0; i < t.Length; i++) - { - if (t[i] <= 0x1f && - (t[i] != '\n' && t[i] != '\r' && t[i] != '\t' && encodeTabLF == false || //Not Tab, CR or LF - encodeTabLF)) - { - sb.AppendFormat("_x00{0}_", (t[i] <= 0xf ? "0" : "") + ((int)t[i]).ToString("X")); - } - else if (t[i] > 0xFFFD) - { - sb.Append($"_x{((int)t[i]).ToString("X")}_"); - } - else - { - sb.Append(t[i]); - } - } - } - internal static string ExcelEscapeAndEncodeString(string t, bool crLfEncode = true) - { - if (string.IsNullOrEmpty(t)) - { - return t; - } - return ExcelEncodeString(ExcelEscapeString(t), crLfEncode); - } - /// - /// Return true if preserve space attribute is set. - /// - /// - /// - /// - internal static string ExcelEncodeString(string t, bool crLfEncode = true) - { - StringBuilder sb = new StringBuilder(); - t = t.Replace("\r\n", "\n"); //For some reason can't table name have cr in them. Replace with nl - ExcelEncodeString(sb, t, crLfEncode); - return sb.ToString(); - } - internal static string ExcelDecodeString(string t) - { - if (string.IsNullOrEmpty(t)) return t; - var ret = new StringBuilder(); - - var ix = 0; - for (var i = 0; i < t.Length; i++) - { - var c = t[i]; - if (c == '\r') - { - ret.Append('\n'); - if (i + 1 < t.Length && t[i + 1] == '\n') - { - i++; - } - } - else - { - if (Matches(c, ref ix)) - { - if (ix == 7) - { - var encoded = t.Substring(i - 4, 4); - ret.Append((char)int.Parse(encoded, NumberStyles.AllowHexSpecifier)); - ix = 0; - } - } - else - { - if (ix > 0) - { - //If two underscores in a row the last one should count as potentially in matches - if (c == '_' && ix == 1) - { - ret.Append(t.Substring(i - ix, ix)); - } - else - { - ret.Append(t.Substring(i - ix, ix + 1)); - ix = 0; - } - } - else - { - ret.Append(c); - } - } - } - } - - if (ix > 0) - { - ret.Append(t.Substring(t.Length - ix, ix)); - } - - return ret.ToString(); - } - - private static bool Matches(char c, ref int ix) - { - if (ix == 0 && c == '_' || - ix == 1 && c == 'x' || - ix >= 2 && ix <= 5 && (c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f') || - c == '_' && ix == 6) - { - ix++; - return true; - } - else - { - return false; - } - } - - /// - /// Convert cell value to desired type, including nullable structs. - /// When converting blank string to nullable struct (e.g. ' ' to int?) null is returned. - /// When attempted conversion fails exception is passed through. - /// - /// - /// The type to convert to. - /// - /// - /// The converted to . - /// - /// - /// If input is string, parsing is performed for output types of DateTime and TimeSpan, which if fails throws . - /// Another special case for output types of DateTime and TimeSpan is when input is double, in which case - /// is used for conversion. This special case does not work through other types convertible to double (e.g. integer or string with number). - /// In all other cases 'direct' conversion is performed. - /// - /// - /// is string and its format is invalid for conversion (parsing fails) - /// - /// - /// is not string and direct conversion fails - /// - public static T GetTypedCellValue(object value) - { - return GetTypedCellValueInner(value, false); - } - internal static T GetTypedCellValueInner(object value, bool returnDefaultIfException) - { - var conversion = new TypeConvertUtil(value); - - if (value == null || (conversion.ReturnType.IsNullable && conversion.Value.IsEmptyString)) - { - return default; - } - else if (value.GetType() == conversion.ReturnType.Type) - { - return (T)value; - } - else if ((conversion.Value.IsString || conversion.Value.IsNumeric) && conversion.ReturnType.IsNumeric) - { - return (T)conversion.ConvertToReturnType(); - } - else if (conversion.ReturnType.IsDateTime) - { - DateTime? dt = null; - if (conversion.TryGetDateTime(out object returnDate)) - { - dt = (DateTime)returnDate; - } -#if (NET8_0_OR_GREATER) - else if (value is DateOnly dateOnly) - { - dt = dateOnly.ToDateTime(TimeOnly.MinValue); - } - - if (conversion.ReturnType.Type == typeof(DateOnly)) - { - if (dt.HasValue) - { - return (T)(object)DateOnly.FromDateTime(dt.Value); - } - else - { - return (T)(object)DateOnly.FromDateTime((DateTime)value); - } - } -#endif - if (dt != null) - { - return (T)(object)dt; - } - } - else if (conversion.ReturnType.IsTimeSpan) - { - TimeSpan? ts = null; - if (value is DateTime dt) - { - ts = new TimeSpan((long)(dt.ToOADate() * TimeSpan.TicksPerDay)); - } -#if (NET8_0_OR_GREATER) - else if (value is TimeOnly timeOnly) - { - ts = timeOnly.ToTimeSpan(); - } - else if (value is DateOnly dateOnly) - { - ts=new TimeSpan((long)dateOnly.ToDateTime(TimeOnly.MinValue).ToOADate() * TimeSpan.TicksPerDay); - } -#endif - else if (conversion.TryGetTimeSpan(out object tso)) - { - ts = (TimeSpan)tso; - } - -#if (NET8_0_OR_GREATER) - if (conversion.ReturnType.Type == typeof(TimeOnly)) - { - if (ts.HasValue == false && value is TimeSpan tsc) - { - ts = tsc; - } - return (T)(object)TimeOnly.FromTimeSpan(new TimeSpan(ts.Value.Ticks - ts.Value.Days*TimeSpan.TicksPerDay)); - } -#endif - if (ts.HasValue) - { - return (T)(object)ts; - } - } - if (returnDefaultIfException) - { - try - { - return (T)Convert.ChangeType(value, conversion.ReturnType.Type); - } - catch - { - return default; - } - } - else - { - if (value is IConvertible) - { - return (T)Convert.ChangeType(value, conversion.ReturnType.Type); - } - else - { - if (conversion.ReturnType.Type == typeof(object)) - { - return (T)value; - } - else - { - return default; - } - } - } - } - internal static string GetValueForXml(object v, bool date1904) - { - string s; - try - { - if (IsDate(v, out DateTime dt)) - { - double sdv = dt.ToOADate(); - - if (date1904) - { - sdv -= ExcelWorkbook.date1904Offset; - } - - s = sdv.ToString(CultureInfo.InvariantCulture); - } - else if (IsTimeSpan(v, out TimeSpan ts)) - { - s = ((double)ts.Ticks / TimeSpan.TicksPerDay).ToString(CultureInfo.InvariantCulture); - } - else if (TypeCompat.IsPrimitive(v) || v is double || v is decimal) - { - if (v is double d) - { - if (double.IsNaN(d)) - { - s = ""; - } - else if (double.IsInfinity(d)) - { - s = "#NUM!"; - } - else - { - s = d.ToString("R15", CultureInfo.InvariantCulture); - } - } - else if (v is float f) - { - if (float.IsNaN(f)) - { - s = ""; - } - else if (float.IsInfinity(f)) - { - s = "#NUM!"; - - } - else - { - s = f.ToString("R15", CultureInfo.InvariantCulture); - } - } - else - { - s = Convert.ToDouble(v, CultureInfo.InvariantCulture).ToString("R15", CultureInfo.InvariantCulture); - } - } - else - { - s = v.ToString(); - } - } - - catch - { - s = "0"; - } - return s; - } - private static bool IsDate(object v, out DateTime date) - { - if (v is DateTime dt) - { - date = dt; - return true; - } -#if (NET6_0_OR_GREATER) - if (v is DateOnly dto) - { - date = dto.ToDateTime(TimeOnly.MinValue); - return true; - } -#endif - date = default; - return false; - } - private static bool IsTimeSpan(object v, out TimeSpan timeSpan) - { - if (v is TimeSpan ts) - { - timeSpan = ts; - return true; - } -#if (NET6_0_OR_GREATER) - if (v is TimeOnly to) - { - timeSpan = to.ToTimeSpan(); - return true; - } -#endif - timeSpan = default; - return false; - } - - - internal static string CropString(string s, int maxLength) - { - if (s == null) return s; - return s.Length > maxLength ? s.Substring(0, maxLength) : s; - } - internal static object GetValueFromType(XmlReader xr, string type, int styleId, ExcelWorkbook workbook) - { - if (type == "s") - { - var s = xr.ReadElementContentAsString(); - if (int.TryParse(s, out int sId)) - { - return sId; - } - return s; - } - else if (type == "str") - { - return ExcelDecodeString(xr.ReadElementContentAsString()); - } - else if (type == "b") - { - return xr.ReadElementContentAsString() != "0"; - } - else if (type == "e") - { - var v = xr.ReadElementContentAsString(); - return ExcelErrorValue.Parse(_invariantTextInfo.ToUpper(v)); - } - else - { - string v = xr.ReadElementContentAsString(); - var nf = workbook.Styles.CellXfs[styleId].NumberFormatId; - if (nf >= 14 && nf <= 22 || nf >= 45 && nf <= 47) - { - if (double.TryParse(v, NumberStyles.Any, CultureInfo.InvariantCulture, out double res)) - { - if (workbook.Date1904) - { - res += ExcelWorkbook.date1904Offset; - } - if (res >= -657435.0 && res < 2958465.9999999) - { - return DateTime.FromOADate(res); - } - else - { - return res; - } - } - else - { - return v; - } - } - else - { - if (double.TryParse(v, NumberStyles.Any, CultureInfo.InvariantCulture, out double d)) - { - return d; - } - else - { - return double.NaN; - } - } - } - } - internal static string GetCellType(object v, bool allowStr = false) - { - if (v is bool) - { - return " t=\"b\""; - } - else if (v is double && double.IsInfinity((double)v) || v is ExcelErrorValue) - { - return " t=\"e\""; - } - else if (allowStr && v != null && !IsNumericOrDateDatatype(v)) - { - return " t=\"str\""; - } - else - { - return ""; - } - } - internal static bool IsNumericOrDateDatatype(object v) - { -#if (NET6_0_OR_GREATER) - return (TypeCompat.IsPrimitive(v) || v is double || v is decimal || v is DateTime || v is TimeSpan || v is DateOnly || v is TimeOnly) && v is not char; -#else - return (TypeCompat.IsPrimitive(v) || v is double || v is decimal || v is DateTime || v is TimeSpan) && !(v is char); -#endif - } - - internal static string ParseXmlString(this string xmlString) - { - //Remove start and end " - xmlString = xmlString.Substring(1, xmlString.Length - 2); - - xmlString = ExcelDecodeString(xmlString); - - //Unescape string - xmlString = xmlString.Replace("\"\"", "\""); - return xmlString; - } - - internal static int ParseInt(object obj, RoundingMethod roundingMethod) - { - int result; - if (obj is IRangeInfo) - { - var r = ((IRangeInfo)obj).FirstOrDefault(); - return r == null ? 0 : ConvertToInt(r.ValueDouble, roundingMethod); - } - var objType = obj.GetType(); - if (objType == typeof(int)) - { - return (int)obj; - } - if (objType == typeof(double) || objType == typeof(decimal) || objType == typeof(bool)) - { - return ConvertToInt(obj, roundingMethod); - } - if (!int.TryParse(obj.ToString(), out result)) - { - throw new ExcelErrorValueException(ExcelErrorValue.Create(eErrorType.Value)); - } - return result; - } - - private static int ConvertToInt(object obj, RoundingMethod roundingMethod) - { - var objType = obj.GetType(); - if (roundingMethod == RoundingMethod.Convert) - { - return Convert.ToInt32(obj); - } - else if (objType == typeof(double)) - { - return Convert.ToInt32(Math.Floor((double)obj)); - } - else - { - return Convert.ToInt32(Math.Floor(Convert.ToDecimal(obj))); - } - } - /// - /// Handles the issue with Excel having the incorrect values before 1900-03-01. Excel has 1900-02-29 as a valid value, but it does not exists in the calendar. Dates between 1900-02-28 and 1900-01-01 shuld be subtracted added 1 to the value. 0 in Excel is 1900-01-00 which is not valid in .NET. - /// - /// - /// - internal static DateTime FromOADateExcel(double d) - { - if (d < 0) - { - return DateTime.MinValue; - } - else if (d < 60) - { - d++; - } - return DateTime.FromOADate(d); - } - - #region internal cache objects - internal static TextInfo _invariantTextInfo = CultureInfo.InvariantCulture.TextInfo; - internal static CompareInfo _invariantCompareInfo = CompareInfo.GetCompareInfo(CultureInfo.InvariantCulture.Name); //TODO:Check that it works - #endregion - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Utils/DrawingExtensions.cs b/src/EPPlus.Export.ImageRenderer/Utils/DrawingExtensions.cs deleted file mode 100644 index ac62618b81..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Utils/DrawingExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using EPPlus.Fonts.OpenType.Utils; -using EPPlus.Graphics; -using OfficeOpenXml.Drawing; - -namespace EPPlus.Export.ImageRenderer.Utils -{ - internal static class DrawingExtensions - { - internal static BoundingBox GetBoundingBox(this ExcelDrawing drawing) - { - return new BoundingBox() - { - Left = 0, - Top = 0, - Width = drawing.GetPixelWidth().PixelToPoint(), - Height = drawing.GetPixelHeight().PixelToPoint() - }; - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Utils/MConverter.cs b/src/EPPlus.Export.ImageRenderer/Utils/MConverter.cs deleted file mode 100644 index fd2ccd93ab..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Utils/MConverter.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Utils -{ - /// - /// Math converter utils - /// - internal static class MConverter - { - internal static double DegreesToRadians(double degree) - { - return degree * (Math.Round((double)System.Math.PI, 14) / 180); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Utils/SvgDrawingWriter.cs b/src/EPPlus.Export.ImageRenderer/Utils/SvgDrawingWriter.cs deleted file mode 100644 index 6780d8bbe5..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Utils/SvgDrawingWriter.cs +++ /dev/null @@ -1,540 +0,0 @@ -/************************************************************************************************* - Required Notice: Copyright (C) EPPlus Software AB. - This software is licensed under PolyForm Noncommercial License 1.0.0 - and may only be used for noncommercial purposes - https://polyformproject.org/licenses/noncommercial/1.0.0/ - - A commercial license to use this software can be purchased at https://epplussoftware.com - ************************************************************************************************* - Date Author Change - ************************************************************************************************* - 27/11/2025 EPPlus Software AB EPPlus 9 - *************************************************************************************************/ -using EPPlus.Export.ImageRenderer.RenderItems; -using EPPlus.Fonts.OpenType.Utils; -using EPPlusImageRenderer.Constants; -using EPPlusImageRenderer.RenderItems; -using OfficeOpenXml.Drawing; -using OfficeOpenXml.Drawing.Style.Coloring; -using OfficeOpenXml.Drawing.Style.Fill; -using OfficeOpenXml.Drawing.Theme; -using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions; -using OfficeOpenXml.Utils; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Globalization; -using System.Linq; -using System.Text; -using TypeConv = OfficeOpenXml.Utils.TypeConversion; - -namespace EPPlusImageRenderer.Utils -{ - internal class SvgDrawingWriter - { - ExcelTheme _theme; - DrawingBase _svgDrawing; - - public SvgDrawingWriter(DrawingBase svgDrawing) - { - _svgDrawing = svgDrawing; - var wb = svgDrawing.Drawing._drawings.Worksheet.Workbook; - _theme = wb.ThemeManager.GetOrCreateTheme(); - } - internal void WriteSvgDefs(StringBuilder sb, List renderItems) - { - var defSb = new StringBuilder(); - var hs = new HashSet(); - var ix = 1; - - foreach (RenderItem item in renderItems) - { - string filter = ""; - if (item.GradientFill != null) - { - string name = WriteGradient($"Gradient{ix}", defSb, hs, item.GradientFill, item.FillColorSource, true); - item.FillColor = $"Url(#{name})"; - } - else if (item.PatternFill != null) - { - string name = WritePattern($"Pattern{item.PatternFill.PatternType}{ix}", defSb, hs, item.PatternFill, item.FillColorSource); - item.FillColor = $"Url(#{name})"; - } - else if (item.BlipFill != null) - { - if (item.FillColorSource != PathFillMode.Norm) - { - item.FilterName = GetFilterName(ix); - } - - string name = WriteBlip("Blip", defSb, hs, item, ref filter); - item.FillColor = $"Url(#{name})"; - } - if (item.BorderGradientFill != null) - { - string name = WriteGradient($"StrokeGradient{ix}", defSb, hs, item.BorderGradientFill, item.BorderColorSource, true); - item.BorderColor = $"Url(#{name})"; - } - if(item.GlowColor!=null) - { - if(string.IsNullOrEmpty(item.FilterName)) - { - var filterName = GetFilterName(ix); - item.FilterName = $"Url(#{filterName})"; - filter = $""; - } - - filter += $"" + - $"" + - $"" + - $""; - } - if(item.OuterShadowEffect != null) - { - if (string.IsNullOrEmpty(item.FilterName)) - { - var filterName = GetFilterName(ix); - item.FilterName = $"Url(#{filterName})"; - filter = $""; - } - item.GetOuterShadowColor(out string shadowColor, out double opacity); - var dx = Math.Round(item.OuterShadowEffect.Distance * Math.Cos(MathHelper.Radians(item.OuterShadowEffect.Direction ?? 0D)), 2); - var dy = Math.Round(item.OuterShadowEffect.Distance * Math.Sin(MathHelper.Radians(item.OuterShadowEffect.Direction ?? 0D)), 2); - var blurRadius = item.OuterShadowEffect.BlurRadius??0D / 2; - filter += $""; - } - if(string.IsNullOrEmpty(filter)==false) - { - defSb.Append(filter+""); - } - - if(item is SvgRenderItem svgItem) - { - if(string.IsNullOrEmpty(svgItem.DefId) == false) - { - svgItem.Render(defSb); - } - } - ix++; - } - if (defSb.Length > 0) - { - sb.Append(""); - sb.Append(defSb); - sb.Append(""); - } - - //Remove all items that have already been rendered - renderItems.RemoveAll(x => x is SvgRenderItem svgX && string.IsNullOrEmpty(svgX.DefId) == false); - } - - private static string GetFilterName(int ix) - { - return $"item{ix}Filter"; - } - - private string WriteBlip(string namePrefix, StringBuilder defSb, HashSet hs, RenderItem item, ref string filter) - { - //, item.BlipFill, item.FillColorSource - var name = $"{namePrefix}"; - var fillMode = item.FillColorSource; - if (fillMode != PathFillMode.Norm) - { - if (hs.Contains(item.FilterName) == false) - { - switch (fillMode) - { - case PathFillMode.Lighten: - filter = $""; - break; - case PathFillMode.LightenLess: - filter = $""; - break; - case PathFillMode.DarkenLess: - filter = $""; - break; - case PathFillMode.Darken: - filter = $""; - break; - } - } - hs.Add(item.FilterName); - } - - if (hs.Contains(name)) return name; - hs.Add(name); - - defSb.Append($""); - defSb.Append($""); - defSb.Append($""); - return name; - } - private string WriteGradient(string namePrefix, StringBuilder defSb, HashSet hs, DrawGradientFill gradientFill, PathFillMode fillMode, bool userSpaceOnUse) - { - var gs = gradientFill.Settings; - var name = $"{namePrefix}{fillMode}"; - var grUnits = userSpaceOnUse ? " gradientUnits=\"userSpaceOnUse\"" : ""; - if (gs.ShadePath == eShadePath.Linear && hs.Contains(name) == false) - { - hs.Add(name); - var xy = GetXy(gs.LinearSettings?.Angle); - defSb.Append($""); - SetStopColors(defSb, gradientFill, fillMode); - defSb.Append(""); - } - else if (hs.Contains(name) == false) - { - hs.Add(name); - defSb.Append($""); - SetStopColors(defSb, gradientFill, fillMode); - defSb.Append($""); - } - - return name; - } - private string WritePattern(string namePrefix, StringBuilder defSb, HashSet hs, ExcelDrawingPatternFill patternFill, PathFillMode fillMode) - { - var name = $"{namePrefix}{fillMode}"; - var fc = TypeConv.ColorConverter.GetThemeColor(_theme, patternFill.ForegroundColor); - var bc = TypeConv.ColorConverter.GetThemeColor(_theme, patternFill.BackgroundColor); - var afc = ColorUtils.GetAdjustedColor(fillMode, fc); - var abc = ColorUtils.GetAdjustedColor(fillMode, bc); - switch (patternFill.PatternType) - { - case eFillPatternStyle.Pct5: - SetPatternHalf(defSb, name, afc, abc, 10, 10); - break; - case eFillPatternStyle.Pct10: - SetPatternHalf(defSb, name, afc, abc, 10, 5); - break; - case eFillPatternStyle.Pct20: - SetPatternHalf(defSb, name, afc, abc, 4, 4); - break; - case eFillPatternStyle.Pct25: - SetPatternHalf(defSb, name, afc, abc, 4, 2); - break; - case eFillPatternStyle.Pct30: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Pct30); - break; - case eFillPatternStyle.Pct40: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Pct40); - break; - case eFillPatternStyle.Pct50: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Pct50); - break; - case eFillPatternStyle.Pct60: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Pct60); - break; - case eFillPatternStyle.Pct70: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Pct70); - break; - case eFillPatternStyle.Pct75: - SetPatternHalf(defSb, name, abc, afc, 4, 2); - break; - case eFillPatternStyle.Pct80: - SetPatternHalf(defSb, name, abc, afc, 4, 4); - break; - case eFillPatternStyle.Pct90: - SetPatternHalf(defSb, name, abc, afc, 10, 5); - break; - case eFillPatternStyle.LtHorz: - SetPatternArray(defSb, name, afc, abc, PatternArrays.LtHorz); - break; - case eFillPatternStyle.LtVert: - SetPatternArray(defSb, name, afc, abc, PatternArrays.LtVert); - break; - case eFillPatternStyle.LtUpDiag: - SetPatternArray(defSb, name, afc, abc, PatternArrays.LtUpDiag); - break; - case eFillPatternStyle.LtDnDiag: - SetPatternArray(defSb, name, afc, abc, PatternArrays.LtDnDiag); - break; - case eFillPatternStyle.DkVert: - SetPatternArray(defSb, name, afc, abc, PatternArrays.DkVert); - break; - case eFillPatternStyle.DkHorz: - SetPatternArray(defSb, name, afc, abc, PatternArrays.DkHorz); - break; - case eFillPatternStyle.DkUpDiag: - SetPatternArray(defSb, name, afc, abc, PatternArrays.DkUpDiag); - break; - case eFillPatternStyle.DkDnDiag: - SetPatternArray(defSb, name, afc, abc, PatternArrays.DkDnDiag); - break; - case eFillPatternStyle.WdUpDiag: - SetPatternArray(defSb, name, afc, abc, PatternArrays.WdUpDiag); - break; - case eFillPatternStyle.WdDnDiag: - SetPatternArray(defSb, name, afc, abc, PatternArrays.WdDnDiag); - break; - case eFillPatternStyle.NarVert: - SetPatternArray(defSb, name, afc, abc, PatternArrays.NarVert); - break; - case eFillPatternStyle.NarHorz: - SetPatternArray(defSb, name, afc, abc, PatternArrays.NarHorz); - break; - case eFillPatternStyle.Vert: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Vert); - break; - case eFillPatternStyle.Horz: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Horz); - break; - case eFillPatternStyle.DashDnDiag: - SetPatternArray(defSb, name, afc, abc, PatternArrays.DashDnDiag); - break; - case eFillPatternStyle.DashUpDiag: - SetPatternArray(defSb, name, afc, abc, PatternArrays.DashUpDiag); - break; - case eFillPatternStyle.DashHorz: - SetPatternArray(defSb, name, afc, abc, PatternArrays.DashHorz); - break; - case eFillPatternStyle.DashVert: - SetPatternArray(defSb, name, afc, abc, PatternArrays.DashVert); - break; - case eFillPatternStyle.SmConfetti: - SetPatternArray(defSb, name, afc, abc, PatternArrays.SmConfetti); - break; - case eFillPatternStyle.LgConfetti: - SetPatternArray(defSb, name, afc, abc, PatternArrays.LgConfetti); - break; - case eFillPatternStyle.ZigZag: - SetPatternArray(defSb, name, afc, abc, PatternArrays.ZigZag); - break; - case eFillPatternStyle.Wave: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Wave); - break; - case eFillPatternStyle.DiagBrick: - SetPatternArray(defSb, name, afc, abc, PatternArrays.DiagBrick); - break; - case eFillPatternStyle.HorzBrick: - SetPatternArray(defSb, name, afc, abc, PatternArrays.HorzBrick); - break; - case eFillPatternStyle.Weave: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Weave); - break; - case eFillPatternStyle.Plaid: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Plaid); - break; - case eFillPatternStyle.Divot: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Divot); - break; - case eFillPatternStyle.DotGrid: - SetPatternArray(defSb, name, afc, abc, PatternArrays.DotGrid); - break; - case eFillPatternStyle.DotDmnd: - SetPatternArray(defSb, name, afc, abc, PatternArrays.DotDmnd); - break; - case eFillPatternStyle.Shingle: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Shingle); - break; - case eFillPatternStyle.Trellis: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Trellis); - break; - case eFillPatternStyle.Sphere: - SetPatternArray(defSb, name, afc, abc, PatternArrays.Sphere); - break; - case eFillPatternStyle.SmGrid: - SetPatternArray(defSb, name, afc, abc, PatternArrays.SmGrid); - break; - case eFillPatternStyle.LgGrid: - SetPatternArray(defSb, name, afc, abc, PatternArrays.LgGrid); - break; - case eFillPatternStyle.SmCheck: - SetPatternArray(defSb, name, afc, abc, PatternArrays.SmCheck); - break; - case eFillPatternStyle.LgCheck: - SetPatternArray(defSb, name, afc, abc, PatternArrays.LgCheck); - break; - case eFillPatternStyle.OpenDmnd: - SetPatternArray(defSb, name, afc, abc, PatternArrays.OpenDmnd); - break; - case eFillPatternStyle.SolidDmnd: - SetPatternArray(defSb, name, afc, abc, PatternArrays.SolidDmnd); - break; - default: - break; - } - return name; - } - /// - /// Sets the pattern to the size with one point top-left and on point middle (x/y). - /// - /// - /// - /// - /// - /// - /// - private static void SetPatternHalf(StringBuilder defSb, string name, Color afc, Color abc, int width, int height) - { - defSb.Append($""); - defSb.Append($""); - defSb.Append($""); - defSb.Append($""); - defSb.Append($""); - } - private static void SetPatternArray(StringBuilder defSb, string name, Color afc, Color abc, short[][] pathArray) - { - var height = pathArray.GetLength(0); - var width = pathArray[0].GetLength(0); - defSb.Append($""); - defSb.Append($""); - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - if (pathArray[y][x] == 1) - { - defSb.Append($""); - } - } - } - defSb.Append($""); - } - - private string GetScaling(DrawGradientFill gradientFill) - { - var sb = new StringBuilder(); - var t = gradientFill.Settings.FocusPoint.TopOffset / 100; - var b = gradientFill.Settings.FocusPoint.BottomOffset / 100; - var l = gradientFill.Settings.FocusPoint.LeftOffset / 100; - var r = gradientFill.Settings.FocusPoint.RightOffset / 100; - - var tt = gradientFill.Settings.TileRectangle.TopOffset / 100; - var tb = gradientFill.Settings.TileRectangle.BottomOffset / 100; - var tl = gradientFill.Settings.TileRectangle.LeftOffset / 100; - var tr = gradientFill.Settings.TileRectangle.RightOffset / 100; - - var dy = Math.Abs(t - b); - var dx = Math.Abs(l - r); - //var scaleTo = Math.Min(dy, dx); - //var mult = 0.5 + (scaleTo / 2); - var cx = _svgDrawing.Bounds.Width * l; - var cy = _svgDrawing.Bounds.Height * t; - //var radX = Math.Abs((t - b )) * _svgDrawing.FontSize.Item1; - //var radY = Math.Abs((l - r)) * _svgDrawing.FontSize.Item2; - var rx = _svgDrawing.Bounds.Width * 0.5 * (Math.Abs(t - tb) + Math.Abs(b - tt)); - var ry = _svgDrawing.Bounds.Height * 0.5 * (Math.Abs(l - tr) + Math.Abs(r - tl)); - - var rad = Math.Sqrt(rx * rx + ry * ry); - - sb.Append($"cx=\"{cx.ToString(CultureInfo.InvariantCulture)}\" cy=\"{cy.ToString(CultureInfo.InvariantCulture)}\" "); - - //if(tt-tb != 0) - //{ - // sb.Append($"fy=\"{fy.ToString(CultureInfo.InvariantCulture)}\" "); - //} - if (dy > 0 && dy < 1) - { - var fy = cy * (1 + dy); - sb.Append($"fy=\"{fy.ToString(CultureInfo.InvariantCulture)}\" "); - } - - if (dx > 0 && dx < 1) - { - var fx = cx * (1 + dx); - sb.Append($"fx=\"{fx.ToString(CultureInfo.InvariantCulture)}\" "); - } - sb.Append($"r=\"{rad.ToString(CultureInfo.InvariantCulture)}\" "); - sb.Append($"gradientUnits=\"userSpaceOnUse\" spreadMethod=\"pad\" gradientTransform=\"matrix(1 0 0 1 1 1)\" "); - - return sb.ToString(); - } - - private void SetStopColors(StringBuilder defSb, DrawGradientFill gradientFill, PathFillMode fillMode) - { - int ix = 0; - - //Svg requires starting at 0 and moving towards 100% Excel sometimes starts at 100 - //Sort to get around that - var sortedGradientColors = gradientFill.Colors.OrderBy(x => x.Position); - - foreach (var c in sortedGradientColors) - { - var color = ColorUtils.GetAdjustedColor(fillMode, c.Color); - // TODO: check if ix should be increased...? - defSb.Append($""); - } - } - - private string GetXy(double? angle) - { - 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 ""; - } - - private string GetOpacity(ExcelDrawingColorManager c) - { - var opacityTransform = c.Transforms?.FirstOrDefault(x => x.Type == OfficeOpenXml.Drawing.Style.Coloring.eColorTransformType.Alpha); - if (opacityTransform == null) return ""; - - return $"stop-opacity=\"{opacityTransform.Value.ToString("0")}%\""; - } - - private string SetStretchTileProps(ExcelDrawingBlipFill blipFill) - { - if (blipFill.Stretch) - { - var x = _svgDrawing.Bounds.Width * blipFill.StretchOffset.LeftOffset / 100; - var y = _svgDrawing.Bounds.Height * blipFill.StretchOffset.TopOffset / 100; - var width = _svgDrawing.Bounds.Width - x - _svgDrawing.Bounds.Width * blipFill.StretchOffset.RightOffset / 100; - var height = _svgDrawing.Bounds.Height - x - _svgDrawing.Bounds.Height * blipFill.StretchOffset.BottomOffset / 100; - return $" preserveAspectRatio=\"none\" x=\"{x.ToString(CultureInfo.InvariantCulture)}\" y=\"{y.ToString(CultureInfo.InvariantCulture)}\" width=\"{width.ToString(CultureInfo.InvariantCulture)}\" height=\"{height.ToString(CultureInfo.InvariantCulture)}\" "; - } - else if (!(blipFill.Tile.HorizontalOffset == 0 && blipFill.Tile.VerticalOffset == 0 && - blipFill.Tile.HorizontalRatio == 100 && blipFill.Tile.VerticalRatio == 100 && blipFill.Tile.FlipMode == eTileFlipMode.None)) - { - var flip = ""; - switch (blipFill.Tile.FlipMode) - { - case eTileFlipMode.X: - flip = $" transform=\"translate({_svgDrawing.Bounds.Width.ToString(CultureInfo.InvariantCulture)}, 0) scale(-1, 1)\""; - break; - case eTileFlipMode.Y: - flip = $" transform=\"translate(0, {_svgDrawing.Bounds.Height.ToString(CultureInfo.InvariantCulture)}) scale(1, -1)\""; - break; - case eTileFlipMode.XY: - flip = $" transform=\"translate({_svgDrawing.Bounds.Width.ToString(CultureInfo.InvariantCulture)}, {_svgDrawing.Bounds.Height.ToString(CultureInfo.InvariantCulture)}) scale(-1, -1)\""; - break; - } - return $"{flip}"; - } - - return ""; - } - - private object GetImageAsHref(ExcelDrawingBlipFill blipFill) - { - return $"data:{blipFill.ContentType};base64," + Convert.ToBase64String(blipFill.Image.ImageBytes); - } - } -} diff --git a/src/EPPlus.Export.ImageRenderer/Utils/TransformCommand.cs b/src/EPPlus.Export.ImageRenderer/Utils/TransformCommand.cs deleted file mode 100644 index ecfedb6328..0000000000 --- a/src/EPPlus.Export.ImageRenderer/Utils/TransformCommand.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace EPPlus.Export.ImageRenderer.Utils -{ - internal class TransformCommand - { - - } -} diff --git a/src/EPPlus.Export.ImageRenderer/resource/presetShapeDefinitions.xml b/src/EPPlus.Export.ImageRenderer/resource/presetShapeDefinitions.xml deleted file mode 100644 index d712b23625..0000000000 --- a/src/EPPlus.Export.ImageRenderer/resource/presetShapeDefinitions.xml +++ /dev/null @@ -1,19870 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/EPPlus.Fonts.OpenType.Benchmarks/RichTextBenchmarks.cs b/src/EPPlus.Fonts.OpenType.Benchmarks/RichTextBenchmarks.cs index c5195f9d04..3f4dc9003f 100644 --- a/src/EPPlus.Fonts.OpenType.Benchmarks/RichTextBenchmarks.cs +++ b/src/EPPlus.Fonts.OpenType.Benchmarks/RichTextBenchmarks.cs @@ -146,7 +146,7 @@ private void PrewarmFontCache() new TextFragment { Text = "warmup", - Font = new OpenTypeFontInfoBase + Font = new FontFormatBase { Family = FontFamily, Size = FontSize, @@ -156,7 +156,7 @@ private void PrewarmFontCache() new TextFragment { Text = "warmup", - Font = new OpenTypeFontInfoBase + Font = new FontFormatBase { Family = FontFamily, Size = 12f, @@ -166,7 +166,7 @@ private void PrewarmFontCache() new TextFragment { Text = "warmup", - Font = new OpenTypeFontInfoBase + Font = new FontFormatBase { Family = FontFamily, Size = FontSize, @@ -206,27 +206,27 @@ private List TestBenchmark2() new TextFragment { Text = text.Substring(0, chunkSize), - Font = new OpenTypeFontInfoBase { Family = FontFamily, Size = FontSize } + Font = new FontFormatBase { Family = FontFamily, Size = FontSize } }, new TextFragment { Text = text.Substring(chunkSize, chunkSize), - Font = new OpenTypeFontInfoBase { Family = FontFamily, Size = 12f, SubFamily = FontSubFamily.Bold } + Font = new FontFormatBase { Family = FontFamily, Size = 12f, SubFamily = FontSubFamily.Bold } }, new TextFragment { Text = text.Substring(chunkSize * 2, chunkSize), - Font = new OpenTypeFontInfoBase { Family = FontFamily, Size = FontSize, SubFamily = FontSubFamily.Italic } + Font = new FontFormatBase { Family = FontFamily, Size = FontSize, SubFamily = FontSubFamily.Italic } }, new TextFragment { Text = text.Substring(chunkSize * 3, chunkSize), - Font = new OpenTypeFontInfoBase { Family = FontFamily, Size = FontSize } + Font = new FontFormatBase { Family = FontFamily, Size = FontSize } }, new TextFragment { Text = text.Substring(chunkSize * 4), - Font = new OpenTypeFontInfoBase { Family = FontFamily, Size = 10f } + Font = new FontFormatBase { Family = FontFamily, Size = 10f } } }; @@ -261,27 +261,27 @@ public List WrapRichText_MixedFonts_LongText() new TextFragment { Text = text.Substring(0, chunkSize), - Font = new OpenTypeFontInfoBase { Family = FontFamily, Size = FontSize } + Font = new FontFormatBase { Family = FontFamily, Size = FontSize } }, new TextFragment { Text = text.Substring(chunkSize, chunkSize), - Font = new OpenTypeFontInfoBase { Family = FontFamily, Size = 12f, SubFamily = FontSubFamily.Bold } + Font = new FontFormatBase { Family = FontFamily, Size = 12f, SubFamily = FontSubFamily.Bold } }, new TextFragment { Text = text.Substring(chunkSize * 2, chunkSize), - Font = new OpenTypeFontInfoBase { Family = FontFamily, Size = FontSize, SubFamily = FontSubFamily.Italic } + Font = new FontFormatBase { Family = FontFamily, Size = FontSize, SubFamily = FontSubFamily.Italic } }, new TextFragment { Text = text.Substring(chunkSize * 3, chunkSize), - Font = new OpenTypeFontInfoBase { Family = FontFamily, Size = FontSize } + Font = new FontFormatBase { Family = FontFamily, Size = FontSize } }, new TextFragment { Text = text.Substring(chunkSize * 4), - Font = new OpenTypeFontInfoBase { Family = FontFamily, Size = 10f } + Font = new FontFormatBase { Family = FontFamily, Size = 10f } } }; diff --git a/src/EPPlus.Fonts.OpenType.Benchmarks/TextLayoutEngineBenchmarks.cs b/src/EPPlus.Fonts.OpenType.Benchmarks/TextLayoutEngineBenchmarks.cs index b62236c64e..0502e29325 100644 --- a/src/EPPlus.Fonts.OpenType.Benchmarks/TextLayoutEngineBenchmarks.cs +++ b/src/EPPlus.Fonts.OpenType.Benchmarks/TextLayoutEngineBenchmarks.cs @@ -42,7 +42,7 @@ public class TextLayoutEngineBenchmarks private List _fragments10; private List _texts10; - private List _fonts10; + private List _fonts10; //[GlobalSetup] //public void Setup() @@ -213,17 +213,17 @@ public List New_WrapRichText_MixedFonts_ShortText() new TextFragment { Text = "Lorem ipsum ", - Font = new OpenTypeFontInfoBase { Family = Family, Size = FontSize } + Font = new FontFormatBase { Family = Family, Size = FontSize } }, new TextFragment { Text = "dolor sit amet, ", - Font = new OpenTypeFontInfoBase { Family = Family, Size = 12f, SubFamily = FontSubFamily.Bold } + Font = new FontFormatBase { Family = Family, Size = 12f, SubFamily = FontSubFamily.Bold } }, new TextFragment { Text = "consectetur adipiscing elit.", - Font = new OpenTypeFontInfoBase { Family = Family, Size = FontSize } + Font = new FontFormatBase { Family = Family, Size = FontSize } } }; @@ -242,27 +242,27 @@ public List New_WrapRichText_MixedFonts_LongText() new TextFragment { Text = text.Substring(0, chunkSize), - Font = new OpenTypeFontInfoBase { Family = Family, Size = FontSize } + Font = new FontFormatBase { Family = Family, Size = FontSize } }, new TextFragment { Text = text.Substring(chunkSize, chunkSize), - Font = new OpenTypeFontInfoBase { Family = Family, Size = 12f, SubFamily = FontSubFamily.Bold } + Font = new FontFormatBase { Family = Family, Size = 12f, SubFamily = FontSubFamily.Bold } }, new TextFragment { Text = text.Substring(chunkSize * 2, chunkSize), - Font = new OpenTypeFontInfoBase { Family = Family, Size = FontSize, SubFamily = FontSubFamily.Italic } + Font = new FontFormatBase { Family = Family, Size = FontSize, SubFamily = FontSubFamily.Italic } }, new TextFragment { Text = text.Substring(chunkSize * 3, chunkSize), - Font = new OpenTypeFontInfoBase { Family = Family, Size = FontSize } + Font = new FontFormatBase { Family = Family, Size = FontSize } }, new TextFragment { Text = text.Substring(chunkSize * 4), - Font = new OpenTypeFontInfoBase { Family = Family, Size = 10f } + Font = new FontFormatBase { Family = Family, Size = 10f } } }; diff --git a/src/EPPlus.Fonts.OpenType.Tests/Integration/LayoutSystemTests.cs b/src/EPPlus.Fonts.OpenType.Tests/Integration/LayoutSystemTests.cs index 881db87800..68e01cc160 100644 --- a/src/EPPlus.Fonts.OpenType.Tests/Integration/LayoutSystemTests.cs +++ b/src/EPPlus.Fonts.OpenType.Tests/Integration/LayoutSystemTests.cs @@ -73,7 +73,7 @@ public void TestLayoutSystemParagraphChars() "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit amet consectetur adipisci[ng] velit, sed quia non numquam [do] eius modi tempora inci[di]dunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum[d] exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? [D]Quis autem vel eum i[r]ure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur?\u2029 " + "At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem reru[d]um facilis est e[r]t expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellend[a]us. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.\u2029 " + "Let's see if we can recognize unicode paragraph separators" }; - var font = new OpenTypeFontInfoBase() + var font = new FontFormatBase() { Family = "Aptos Narrow", Size = 11, @@ -265,14 +265,14 @@ public void EnsureWrappingSimplePlainTextCorrectly() public void EnsureWrappingRichTextAndGettingLineSpacing() { List txtLst = new List() { "Hi ", "I am rich ", "But I am Even Richer " }; - var rt = new OpenTypeRichTextBase(txtLst[0], "Roboto", 12f); - var rtSecond = new OpenTypeRichTextBase(txtLst[1], "Archivo Narrow", 11f); - var rtThird = new OpenTypeRichTextBase(txtLst[2], "Roboto", 18f); + var rt = new RichTextFormatBase(txtLst[0], "Roboto", 12f); + var rtSecond = new RichTextFormatBase(txtLst[1], "Archivo Narrow", 11f); + var rtThird = new RichTextFormatBase(txtLst[2], "Roboto", 18f); rtThird.Italic = true; rtThird.Bold = true; - List rtLst = new List() { rt, rtSecond, rtThird}; + List rtLst = new List() { rt, rtSecond, rtThird}; var paragraph = new LayoutSystem(rtLst); @@ -292,26 +292,26 @@ public void EnsureWrappingRichTextAndGettingLineSpacing() Assert.AreEqual(1, lines[2].InternalLineFragments.Count); //Assert correct fragment in correct spot - Assert.AreEqual(rtLst[0], (IRichTextFormatBase)lines[0].LineFragments[0].OriginalTextFragment.RichTextOptions); - Assert.AreEqual(rtLst[1], (IRichTextFormatBase)lines[0].LineFragments[1].OriginalTextFragment.RichTextOptions); - Assert.AreEqual(rtLst[2], (IRichTextFormatBase)lines[0].LineFragments[2].OriginalTextFragment.RichTextOptions); + Assert.AreEqual(rtLst[0], (IRichTextFormatEssential)lines[0].LineFragments[0].OriginalTextFragment.RichTextOptions); + Assert.AreEqual(rtLst[1], (IRichTextFormatEssential)lines[0].LineFragments[1].OriginalTextFragment.RichTextOptions); + Assert.AreEqual(rtLst[2], (IRichTextFormatEssential)lines[0].LineFragments[2].OriginalTextFragment.RichTextOptions); - Assert.AreEqual(rtLst[2], (IRichTextFormatBase)lines[1].LineFragments[0].OriginalTextFragment.RichTextOptions); - Assert.AreEqual(rtLst[2], (IRichTextFormatBase)lines[2].LineFragments[0].OriginalTextFragment.RichTextOptions); + Assert.AreEqual(rtLst[2], (IRichTextFormatEssential)lines[1].LineFragments[0].OriginalTextFragment.RichTextOptions); + Assert.AreEqual(rtLst[2], (IRichTextFormatEssential)lines[2].LineFragments[0].OriginalTextFragment.RichTextOptions); } [TestMethod] public void TestGetSection() { List txtLst = new List() { "Hi ", "I am rich ", "But I am Even Richer " }; - var rt = new OpenTypeRichTextBase(txtLst[0], "Roboto", 12f); - var rtSecond = new OpenTypeRichTextBase(txtLst[1], "Archivo Narrow", 11f); - var rtThird = new OpenTypeRichTextBase(txtLst[2], "Roboto", 18f); + var rt = new RichTextFormatBase(txtLst[0], "Roboto", 12f); + var rtSecond = new RichTextFormatBase(txtLst[1], "Archivo Narrow", 11f); + var rtThird = new RichTextFormatBase(txtLst[2], "Roboto", 18f); rtThird.Italic = true; rtThird.Bold = true; - List rtLst = new List() { rt, rtSecond, rtThird }; + List rtLst = new List() { rt, rtSecond, rtThird }; var paragraph = new LayoutSystem(rtLst); @@ -329,13 +329,13 @@ public void TestGetSection() public void TestGetSectionWithIndividualChars() { List txtLst = new List() { "A", "B", "C", "D" }; - var rt = new OpenTypeRichTextBase(txtLst[0], "Roboto", 12f); - var rtSecond = new OpenTypeRichTextBase(txtLst[1], "Archivo Narrow", 11f); - var rtThird = new OpenTypeRichTextBase(txtLst[2], "Roboto", 18f); - var rtFourth = new OpenTypeRichTextBase(txtLst[3], "Archivo Narrow", 18f); + var rt = new RichTextFormatBase(txtLst[0], "Roboto", 12f); + var rtSecond = new RichTextFormatBase(txtLst[1], "Archivo Narrow", 11f); + var rtThird = new RichTextFormatBase(txtLst[2], "Roboto", 18f); + var rtFourth = new RichTextFormatBase(txtLst[3], "Archivo Narrow", 18f); - List rtLst = new List() { rt, rtSecond, rtThird, rtFourth }; + List rtLst = new List() { rt, rtSecond, rtThird, rtFourth }; var pIndividual = new LayoutSystem(rtLst); @@ -350,12 +350,12 @@ public void TestGetSectionWithIndividualChars() public void TestGetSectionMixed() { var txtLstMixed = new List() { "Abc", "D", "Efg", "H" }; - var rt = new OpenTypeRichTextBase(txtLstMixed[0], "Roboto", 12f); - var rtSecond = new OpenTypeRichTextBase(txtLstMixed[1], "Archivo Narrow", 11f); - var rtThird = new OpenTypeRichTextBase(txtLstMixed[2], "Roboto", 18f); - var rtFourth = new OpenTypeRichTextBase(txtLstMixed[3], "Archivo Narrow", 18f); + var rt = new RichTextFormatBase(txtLstMixed[0], "Roboto", 12f); + var rtSecond = new RichTextFormatBase(txtLstMixed[1], "Archivo Narrow", 11f); + var rtThird = new RichTextFormatBase(txtLstMixed[2], "Roboto", 18f); + var rtFourth = new RichTextFormatBase(txtLstMixed[3], "Archivo Narrow", 18f); - var rtLstMixed = new List() { rt, rtSecond, rtThird, rtFourth }; + var rtLstMixed = new List() { rt, rtSecond, rtThird, rtFourth }; var pMixed = new LayoutSystem(rtLstMixed); var InMix = string.Join("", txtLstMixed.ToArray()); @@ -368,9 +368,9 @@ public void TestGetSectionMixed() public void TestGetSectionNumber2() { var txtLstMixed = new List() { "10"}; - var rt = new OpenTypeRichTextBase(txtLstMixed[0], "Roboto", 12f); + var rt = new RichTextFormatBase(txtLstMixed[0], "Roboto", 12f); - var rtLstMixed = new List() { rt }; + var rtLstMixed = new List() { rt }; var pMixed = new LayoutSystem(rtLstMixed); var InMix = string.Join("", txtLstMixed.ToArray()); diff --git a/src/EPPlus.Fonts.OpenType.Tests/Integration/TextLayoutEngineTests.cs b/src/EPPlus.Fonts.OpenType.Tests/Integration/TextLayoutEngineTests.cs index c37ceb2f6a..62758d02ef 100644 --- a/src/EPPlus.Fonts.OpenType.Tests/Integration/TextLayoutEngineTests.cs +++ b/src/EPPlus.Fonts.OpenType.Tests/Integration/TextLayoutEngineTests.cs @@ -269,7 +269,7 @@ public void WrapRichText_SingleFragment_BehavesLikeSingleFont() new TextFragment { Text = "Hello world", - Font = new OpenTypeFontInfoBase { Family = "Calibri", Size = 11 } + Font = new FontFormatBase { Family = "Calibri", Size = 11 } } }; @@ -297,12 +297,12 @@ public void WrapRichText_MultipleFragments_ConcatenatesCorrectly() new TextFragment { Text = "Hello ", - Font = new OpenTypeFontInfoBase { Family = "Calibri", Size = 11 } + Font = new FontFormatBase { Family = "Calibri", Size = 11 } }, new TextFragment { Text = "world", - Font = new OpenTypeFontInfoBase { Family = "Arial", Size = 12, SubFamily = FontSubFamily.Bold } + Font = new FontFormatBase { Family = "Arial", Size = 12, SubFamily = FontSubFamily.Bold } } }; @@ -328,17 +328,17 @@ public void WrapRichText_DifferentFonts_WrapsCorrectly() new TextFragment { Text = "This is ", - Font = new OpenTypeFontInfoBase { Family = "Calibri", Size = 11 } + Font = new FontFormatBase { Family = "Calibri", Size = 11 } }, new TextFragment { Text = "mixed ", - Font = new OpenTypeFontInfoBase { Family = "Arial", Size = 14, SubFamily = FontSubFamily.Bold } + Font = new FontFormatBase { Family = "Arial", Size = 14, SubFamily = FontSubFamily.Bold } }, new TextFragment { Text = "fonts", - Font = new OpenTypeFontInfoBase { Family = "Calibri", Size = 11 } + Font = new FontFormatBase { Family = "Calibri", Size = 11 } } }; @@ -380,7 +380,7 @@ private FontSubFamily GetFontSubType(MeasurementFontStyles Style) public void WrapLongRichTextWord() { RequireFont(SystemFontsEngine, "Aptos Narrow", FontSubFamily.Regular); - var mFont = new OpenTypeFontInfoBase() + var mFont = new FontFormatBase() { Family = "Aptos Narrow", Size = 11, @@ -566,7 +566,7 @@ public void EnsureRichTextLineWrappingSameAsNonRichWhenNoWrap() var comparatorFragments = new List(); - var font11 = new RichTextDefaults() + var font11 = new RichTextFormatSimple() { Family = "Aptos Narrow", Size = 11, @@ -575,7 +575,7 @@ public void EnsureRichTextLineWrappingSameAsNonRichWhenNoWrap() - var font22 = new RichTextDefaults() + var font22 = new RichTextFormatSimple() { Family = "Goudy Stout", Size = 16, @@ -620,14 +620,14 @@ public void EnsureRichTextLineWrappingSameAsNonRichWhenNoWrapAndSpaceTrail() var comparatorFragments = new List(); - var font11 = new RichTextDefaults() + var font11 = new RichTextFormatSimple() { Family = "Aptos Narrow", Size = 11, StrikeType = 1 }; - var font22 = new RichTextDefaults() + var font22 = new RichTextFormatSimple() { Family = "Goudy Stout", Size = 16, @@ -793,7 +793,7 @@ public void TestLayoutSystemParagraphChars() "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit amet consectetur adipisci[ng] velit, sed quia non numquam [do] eius modi tempora inci[di]dunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum[d] exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? [D]Quis autem vel eum i[r]ure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur?\u2029 " + "At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem reru[d]um facilis est e[r]t expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellend[a]us. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.\u2029 " + "Let's see if we can recognize unicode paragraph separators" }; - var font = new OpenTypeFontInfoBase() + var font = new FontFormatBase() { Family = "Aptos Narrow", Size = 11, @@ -1041,12 +1041,12 @@ public void WrapRichText_WordSpanningFragments_MeasuresCorrectly() new TextFragment { Text = "Hel", - Font = new OpenTypeFontInfoBase { Family = "Calibri", Size = 11 } + Font = new FontFormatBase { Family = "Calibri", Size = 11 } }, new TextFragment { Text = "lo world", - Font = new OpenTypeFontInfoBase { Family = "Arial", Size = 11 } + Font = new FontFormatBase { Family = "Arial", Size = 11 } } }; @@ -1073,12 +1073,12 @@ public void WrapRichText_WithLineBreaks_PreservesBreaks() new TextFragment { Text = "Line 1\n", - Font = new OpenTypeFontInfoBase { Family = "Calibri", Size = 11 } + Font = new FontFormatBase { Family = "Calibri", Size = 11 } }, new TextFragment { Text = "Line 2", - Font = new OpenTypeFontInfoBase { Family = "Arial", Size = 11 } + Font = new FontFormatBase { Family = "Arial", Size = 11 } } }; @@ -1107,17 +1107,17 @@ public void WrapRichText_EmptyFragments_HandlesGracefully() new TextFragment { Text = "", - Font = new OpenTypeFontInfoBase { Family = "Calibri", Size = 11 } + Font = new FontFormatBase { Family = "Calibri", Size = 11 } }, new TextFragment { Text = "Hello", - Font = new OpenTypeFontInfoBase { Family = "Arial", Size = 11 } + Font = new FontFormatBase { Family = "Arial", Size = 11 } }, new TextFragment { Text = "", - Font = new OpenTypeFontInfoBase { Family = "Calibri", Size = 11 } + Font = new FontFormatBase { Family = "Calibri", Size = 11 } } }; @@ -1164,17 +1164,17 @@ public void WrapRichText_SameFontMultipleTimes_UsesCache() new TextFragment { Text = "First ", - Font = new OpenTypeFontInfoBase { Family = "Arial", Size = 11 } + Font = new FontFormatBase { Family = "Arial", Size = 11 } }, new TextFragment { Text = "second ", - Font = new OpenTypeFontInfoBase { Family = "Calibri", Size = 11 } + Font = new FontFormatBase { Family = "Calibri", Size = 11 } }, new TextFragment { Text = "third", - Font = new OpenTypeFontInfoBase { Family = "Arial", Size = 11 } // Same as first + Font = new FontFormatBase { Family = "Arial", Size = 11 } // Same as first } }; @@ -1227,7 +1227,7 @@ public void WrapRichText_MeasureCorrectly() new TextFragment { Text = "value, 1", - Font = new OpenTypeFontInfoBase { Family = "Aptos Narrow", Size = 9 } + Font = new FontFormatBase { Family = "Aptos Narrow", Size = 9 } }, }; @@ -1247,7 +1247,7 @@ public void VerifyWrappingSingleChar() RequireFont(SystemFontsEngine, "Aptos Narrow"); - var font1 = new OpenTypeFontInfoBase() + var font1 = new FontFormatBase() { Family = "Aptos Narrow", Size = 11, diff --git a/src/EPPlus.Fonts.OpenType/Integration/DataHolders/OpenTypeFontInfoBase.cs b/src/EPPlus.Fonts.OpenType/Integration/DataHolders/FontFormatBase.cs similarity index 90% rename from src/EPPlus.Fonts.OpenType/Integration/DataHolders/OpenTypeFontInfoBase.cs rename to src/EPPlus.Fonts.OpenType/Integration/DataHolders/FontFormatBase.cs index 17479a8466..e0f4814102 100644 --- a/src/EPPlus.Fonts.OpenType/Integration/DataHolders/OpenTypeFontInfoBase.cs +++ b/src/EPPlus.Fonts.OpenType/Integration/DataHolders/FontFormatBase.cs @@ -9,25 +9,25 @@ namespace EPPlus.Fonts.OpenType.Integration.DataHolders { - public class OpenTypeFontInfoBase : IFontFormatBase + public class FontFormatBase : IFontFormatBase { public virtual string Family { get; set; } = "Archivo Narrow"; public virtual FontSubFamily SubFamily { get; set; } = FontSubFamily.Regular; public virtual float Size { get; set; } = 11f; - public OpenTypeFontInfoBase(string fontFamily, FontSubFamily subFamily, float fontSize) + public FontFormatBase(string fontFamily, FontSubFamily subFamily, float fontSize) { Family = fontFamily; SubFamily = subFamily; Size = fontSize; } - public OpenTypeFontInfoBase() { } + public FontFormatBase() { } /// /// Legacy constructor. Prefer to avoid with new implementations. To be removed after refactor /// /// - public OpenTypeFontInfoBase(MeasurementFont font) + public FontFormatBase(MeasurementFont font) { Family = font.FontFamily; SubFamily = GetFontSubFamily(font.Style); diff --git a/src/EPPlus.Fonts.OpenType/Integration/DataHolders/IRichTextInfoBase.cs b/src/EPPlus.Fonts.OpenType/Integration/DataHolders/IRichTextFormatSimple.cs similarity index 79% rename from src/EPPlus.Fonts.OpenType/Integration/DataHolders/IRichTextInfoBase.cs rename to src/EPPlus.Fonts.OpenType/Integration/DataHolders/IRichTextFormatSimple.cs index 630f9eb080..9d84d24ec1 100644 --- a/src/EPPlus.Fonts.OpenType/Integration/DataHolders/IRichTextInfoBase.cs +++ b/src/EPPlus.Fonts.OpenType/Integration/DataHolders/IRichTextFormatSimple.cs @@ -1,18 +1,15 @@ -using OfficeOpenXml.Interfaces.Fonts; -using OfficeOpenXml.Interfaces.RichText; -using System; -using System.Collections.Generic; +using OfficeOpenXml.Interfaces.RichText; using System.Drawing; -using System.Linq; -using System.Text; namespace EPPlus.Fonts.OpenType.Integration.DataHolders { /// + /// TODO: Move this to interfaces. Only here in order to not break existing references in PDF + /// /// Interface for pdf/svg/future richtext users to unify richtext styling /// - public interface IRichTextInfoBase : IRichTextFormatBase + public interface IRichTextFormatSimple : IRichTextFormatEssential { bool SubScript { get; set; } bool SuperScript { get; set; } diff --git a/src/EPPlus.Fonts.OpenType/Integration/DataHolders/RichTextCollectionBase.cs b/src/EPPlus.Fonts.OpenType/Integration/DataHolders/RichTextCollectionBase.cs new file mode 100644 index 0000000000..a1c0104f8e --- /dev/null +++ b/src/EPPlus.Fonts.OpenType/Integration/DataHolders/RichTextCollectionBase.cs @@ -0,0 +1,99 @@ +using OfficeOpenXml.Interfaces.RichText; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace EPPlus.Fonts.OpenType.Integration.DataHolders +{ + public class RichTextCollectionBase : IRichTextCollection + { + List _list = new List(); + + public IRichTextFormatEssential DefaultRichText = new RichTextFormatSimple(); + + /// + /// Initalizes using hard-coded defaults + /// + public RichTextCollectionBase() : this(new RichTextFormatSimple()) + { + } + + /// + /// Initializes with user supplied defaults + /// + /// Default Fallback Font Options + /// Default RichText Options + public RichTextCollectionBase(IRichTextFormatEssential defaultRichTextOptions) + { + DefaultRichText = defaultRichTextOptions; + } + + public IRichTextFormatEssential this[int index] => _list[index]; + + public string Text + { + get + { + StringBuilder sb = new StringBuilder(); + foreach (var item in _list) + { + sb.Append(item.Text); + } + return sb.ToString(); + } + } + + public int Count => _list.Count; + + public void Add(IRichTextFormatEssential rt) + { + Insert(_list.Count, rt); + } + public IRichTextFormatEssential Insert(int index, IRichTextFormatEssential rt) + { + _list.Insert(index, rt); + return rt; + } + + public IRichTextFormatEssential Add(string Text, bool NewParagraph = false) + { + return Insert(_list.Count, Text, NewParagraph); + } + + public IRichTextFormatEssential Insert(int index, string Text, bool NewParagraph = false) + { + var rt = new RichTextFormatBase(); + rt.Text = Text; + rt.SetFont(DefaultRichText); + _list.Insert(index, rt); + return rt; + } + + public IEnumerator GetEnumerator() + { + return _list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _list.GetEnumerator(); + } + + public void Clear() + { + _list.Clear(); + } + + public void RemoveAt(int Index) + { + _list.RemoveAt(Index); + } + + public void Remove(IRichTextFormatEssential Item) + { + _list.Remove(Item); + } + } +} diff --git a/src/EPPlus.Fonts.OpenType/Integration/DataHolders/OpenTypeRichTextBase.cs b/src/EPPlus.Fonts.OpenType/Integration/DataHolders/RichTextFormatBase.cs similarity index 79% rename from src/EPPlus.Fonts.OpenType/Integration/DataHolders/OpenTypeRichTextBase.cs rename to src/EPPlus.Fonts.OpenType/Integration/DataHolders/RichTextFormatBase.cs index c317849243..a962186357 100644 --- a/src/EPPlus.Fonts.OpenType/Integration/DataHolders/OpenTypeRichTextBase.cs +++ b/src/EPPlus.Fonts.OpenType/Integration/DataHolders/RichTextFormatBase.cs @@ -7,15 +7,19 @@ namespace EPPlus.Fonts.OpenType.Integration.DataHolders { - public class OpenTypeRichTextBase : OpenTypeFontInfoBase, IRichTextFormatBase + /// + /// The most basic rich text format + /// The only properties that belong in this class are those that are absolutely neccesary for Measuring the text correctly + /// + public class RichTextFormatBase : FontFormatBase, IRichTextFormatEssential { - internal OpenTypeRichTextBase() + internal RichTextFormatBase() { Italic = false; Bold = false; } - public OpenTypeRichTextBase(string text, string fontFamily, float size, bool bold = false, bool italic = false) + public RichTextFormatBase(string text, string fontFamily, float size, bool bold = false, bool italic = false) { Text = text; Family = fontFamily; diff --git a/src/EPPlus.Fonts.OpenType/Integration/DataHolders/RichTextDefaults.cs b/src/EPPlus.Fonts.OpenType/Integration/DataHolders/RichTextFormatSimple.cs similarity index 57% rename from src/EPPlus.Fonts.OpenType/Integration/DataHolders/RichTextDefaults.cs rename to src/EPPlus.Fonts.OpenType/Integration/DataHolders/RichTextFormatSimple.cs index 1faf588dec..498c35d4bb 100644 --- a/src/EPPlus.Fonts.OpenType/Integration/DataHolders/RichTextDefaults.cs +++ b/src/EPPlus.Fonts.OpenType/Integration/DataHolders/RichTextFormatSimple.cs @@ -7,8 +7,21 @@ namespace EPPlus.Fonts.OpenType.Integration.DataHolders { - internal class RichTextDefaults : OpenTypeRichTextBase, IRichTextInfoBase + /// + /// Defaults class mainly intended for Cells + /// + public class RichTextFormatSimple : RichTextFormatBase, IRichTextFormatSimple { + public RichTextFormatSimple() : base() + { + + } + + public RichTextFormatSimple(string text, string fontFamily, float size, bool bold = false, bool italic = false) : base(text, fontFamily, size, bold, italic) + { + + } + public bool SubScript { get; set; } = false; public bool SuperScript { get; set; } = false; diff --git a/src/EPPlus.Fonts.OpenType/Integration/RichText/LayoutSystem.cs b/src/EPPlus.Fonts.OpenType/Integration/RichText/LayoutSystem.cs index 8bea44ca46..6cabaf5c45 100644 --- a/src/EPPlus.Fonts.OpenType/Integration/RichText/LayoutSystem.cs +++ b/src/EPPlus.Fonts.OpenType/Integration/RichText/LayoutSystem.cs @@ -20,21 +20,22 @@ public class LayoutSystem //The text of the entire paragraph //regardless of linebreaking or style runs string FullText; - public List AllChars { get; private set; }= new List(); + private List AllChars { get; set; }= new List(); List SeparatorIndicies = new List(); List ParagraphSeparatorIndicies = new List(); List SubParagraphs = new List(); - public List StyleRuns { get; private set; } = new List(); + private List StyleRuns { get; set; } = new List(); int FullTextLength = 0; int FullTextLastIdx = 0; + TextLineCollection WrappedLineCollection; public LayoutSystem(List preFragments): this(preFragments.Cast().ToList()) { } - public LayoutSystem(IEnumerable preFragments) + public LayoutSystem(IEnumerable preFragments) { InputFragments = new List(); diff --git a/src/EPPlus.Fonts.OpenType/Integration/TextFragment.cs b/src/EPPlus.Fonts.OpenType/Integration/TextFragment.cs index ccf949ba10..54123d0f4c 100644 --- a/src/EPPlus.Fonts.OpenType/Integration/TextFragment.cs +++ b/src/EPPlus.Fonts.OpenType/Integration/TextFragment.cs @@ -18,15 +18,6 @@ Date Author Change namespace EPPlus.Fonts.OpenType.Integration { - - public interface IFragInfo - { - public double AscentPoints { get; } - public double DescentPoints { get; } - - public float Size { get; } - } - /// /// Represents a text fragment with specific font properties. /// @@ -37,16 +28,16 @@ public class TextFragment : TextFragmentBase, ITextFragmentBase /// public IFontFormatBase Font { get { return RichTextOptions; } set {RichTextOptions.SetFont(value); } } - public TextFragment(IRichTextInfoBase rtFormat) : base(rtFormat) + public TextFragment(IRichTextFormatSimple rtFormat) : base(rtFormat) { RichTextOptions = rtFormat; } public TextFragment():base() { - RichTextOptions = new RichTextDefaults(); + RichTextOptions = new RichTextFormatSimple(); } - public new IRichTextInfoBase RichTextOptions { get { return (IRichTextInfoBase)base.RichTextOptions; } set { base.RichTextOptions = value; } } + public new IRichTextFormatSimple RichTextOptions { get { return (IRichTextFormatSimple)base.RichTextOptions; } set { base.RichTextOptions = value; } } public override float Size { get => RichTextOptions.Size; } } @@ -59,7 +50,7 @@ public class TextFragmentBase : ITextFragmentBase /// We must extract font info from this but nothing else is supposed to be done with this within opentype /// but we hold the data so users may more easily recognize which rich text this is in the output. /// - public virtual IRichTextFormatBase RichTextOptions { get; set; } = new OpenTypeRichTextBase(); + public virtual IRichTextFormatEssential RichTextOptions { get; set; } = new RichTextFormatBase(); public ShapingOptions Options { get; set; } public double AscentPoints { get; set; } public double DescentPoints { get; set; } @@ -75,37 +66,10 @@ public string FullFontName public TextFragmentBase() { } - public TextFragmentBase(IRichTextFormatBase richText) + public TextFragmentBase(IRichTextFormatEssential richText) { RichTextOptions = richText; } public virtual float Size { get => RichTextOptions.Size; } } - - // /// - // /// Simple class to provide some kind of fallback/defaults - // /// - // public class RichTextDefaults : IRichTextInfoBase - // { - // internal RichTextDefaults() - // { - // } - // public bool IsItalic { get; set; } = false; - - // public bool IsBold { get; set; } = false; - - // // public bool SubScript { get; set; } = false; - - // // public bool SuperScript { get; set; } = false; - - // // public int UnderlineType { get; set; } = -1; - - // // public int StrikeType { get; set; } = -1; - - // // public int Capitalization { get; set; } = -1; - - // // public Color UnderlineColor { get; set; } - - // // public Color FontColor { get; set; } - // //} } diff --git a/src/EPPlus.Interfaces/RichText/IFontFormatBase.cs b/src/EPPlus.Interfaces/RichText/IFontFormatBase.cs index 84ebe640a0..dfe46c4e93 100644 --- a/src/EPPlus.Interfaces/RichText/IFontFormatBase.cs +++ b/src/EPPlus.Interfaces/RichText/IFontFormatBase.cs @@ -12,7 +12,6 @@ public interface IFontFormatBase public FontSubFamily SubFamily { get; set; } public float Size { get; set; } - /// /// Sets the underlying data properties to be equal to the font /// diff --git a/src/EPPlus.Interfaces/RichText/IRichTextCollection.cs b/src/EPPlus.Interfaces/RichText/IRichTextCollection.cs new file mode 100644 index 0000000000..d603a5bd86 --- /dev/null +++ b/src/EPPlus.Interfaces/RichText/IRichTextCollection.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OfficeOpenXml.Interfaces.RichText +{ + public interface IRichTextCollection : IEnumerable + { + /// + /// The full text string of all richtext in the collection + /// + string Text { get; } + + /// + /// Collection containing the richtext objects + /// + /// + /// + IRichTextFormatEssential this[int index] { get; } + + /// + /// Number of items in the list + /// + int Count { get; } + + /// + /// Add a rich text string + /// + /// The text to add + /// Adds a new paragraph after the . This will add a new line break. + /// + public IRichTextFormatEssential Add(string Text, bool NewParagraph); + + /// + /// Insert a rich text string at the specified index. + /// + /// The zero-based index at which rich text should be inserted. + /// The text to insert. + /// + public IRichTextFormatEssential Insert(int index, string Text, bool NewParagraph); + + /// + /// Clear the collection + /// + public void Clear(); + + /// + /// Removes an item at the specific index + /// + /// + public void RemoveAt(int Index); + + public void Remove(IRichTextFormatEssential Item); + } +} diff --git a/src/EPPlus.Interfaces/RichText/IRichTextFormatBase.cs b/src/EPPlus.Interfaces/RichText/IRichTextFormatEssential.cs similarity index 67% rename from src/EPPlus.Interfaces/RichText/IRichTextFormatBase.cs rename to src/EPPlus.Interfaces/RichText/IRichTextFormatEssential.cs index 70dd05e07c..f0511dfb67 100644 --- a/src/EPPlus.Interfaces/RichText/IRichTextFormatBase.cs +++ b/src/EPPlus.Interfaces/RichText/IRichTextFormatEssential.cs @@ -4,7 +4,11 @@ namespace OfficeOpenXml.Interfaces.RichText { - public interface IRichTextFormatBase : IFontFormatBase + /// + /// The most basic rich text format + /// The only properties that belong in this class are those that are absolutely neccesary for Measuring the text correctly + /// + public interface IRichTextFormatEssential : IFontFormatBase { /// /// The text within the rich text diff --git a/src/EPPlus.Interfaces/RichText/ITextFragmentBase.cs b/src/EPPlus.Interfaces/RichText/ITextFragmentBase.cs index dc8d7fda39..0985fc2fd5 100644 --- a/src/EPPlus.Interfaces/RichText/ITextFragmentBase.cs +++ b/src/EPPlus.Interfaces/RichText/ITextFragmentBase.cs @@ -13,7 +13,7 @@ public interface ITextFragmentBase /// We must extract font info from this but nothing else is supposed to be done with this within opentype /// but we hold the data so users may more easily recognize which rich text this is in the output. /// - public IRichTextFormatBase RichTextOptions { get; set; } + public IRichTextFormatEssential RichTextOptions { get; set; } public ShapingOptions Options { get; set; } public double AscentPoints { get; set; } public double DescentPoints { get; set; } diff --git a/src/EPPlus.Interfaces/RichText/ITextFragmentCollection.cs b/src/EPPlus.Interfaces/RichText/ITextFragmentCollection.cs new file mode 100644 index 0000000000..74ba063287 --- /dev/null +++ b/src/EPPlus.Interfaces/RichText/ITextFragmentCollection.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OfficeOpenXml.Interfaces.RichText +{ + internal interface ITextFragmentCollection : IEnumerable + { + /// + /// The full text string of all richtext in the collection + /// + string Text { get; } + + /// + /// Collection containing the richtext objects + /// + /// + /// + ITextFragmentBase this[int index] { get; } + + /// + /// Number of items in the list + /// + int Count { get; } + + /// + /// Add a rich text string + /// + /// The text to add + /// Adds a new paragraph after the . This will add a new line break. + /// + public ITextFragmentBase Add(string Text, bool NewParagraph); + + /// + /// Insert a rich text string at the specified index. + /// + /// The zero-based index at which rich text should be inserted. + /// The text to insert. + /// + public ITextFragmentBase Insert(int index, string Text, bool NewParagraph); + + /// + /// Clear the collection + /// + public void Clear(); + + /// + /// Removes an item at the specific index + /// + /// + public void RemoveAt(int Index); + + public void Remove(ITextFragmentBase Item); + } +} diff --git a/src/EPPlus.sln b/src/EPPlus.sln index e03fba23f8..55d05b41f8 100644 --- a/src/EPPlus.sln +++ b/src/EPPlus.sln @@ -23,7 +23,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EPPlus.Fonts.OpenType", "EP EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EPPlus.Export.Pdf", "EPPlus.Export.Pdf\EPPlus.Export.Pdf.csproj", "{0D6A557E-FCD3-42A8-A5D1-C3CD5C0EF9E5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EPPlus.Export.ImageRenderer.Tests", "EPPlus.Export.ImageRenderer.Test\EPPlus.Export.ImageRenderer.Tests.csproj", "{A41EC1A4-8008-4943-AA29-A6D83918751F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EPPlus.DrawingRenderer.Tests", "EPPlus.DrawingRenderer.Tests\EPPlus.DrawingRenderer.Tests.csproj", "{A41EC1A4-8008-4943-AA29-A6D83918751F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EPPlus.Export.Pdf.Tests", "EPPlus.Export.Pdf.Tests\EPPlus.Export.Pdf.Tests.csproj", "{72041196-84B2-47B4-974D-B9FC0F8EF71A}" EndProject diff --git a/src/EPPlus/Drawing/Renderer/Chart/ChartLegendRenderer.cs b/src/EPPlus/Drawing/Renderer/Chart/ChartLegendRenderer.cs index 4ebe4271dc..febf0e50b5 100644 --- a/src/EPPlus/Drawing/Renderer/Chart/ChartLegendRenderer.cs +++ b/src/EPPlus/Drawing/Renderer/Chart/ChartLegendRenderer.cs @@ -411,7 +411,7 @@ private void SetTrendlineLegend(ExcelChart ct, int serieIndex, int entryIndex, D var tbLeft = si.X1 + LineLength + MarginIconText; var tbTop = si.Y2 - entryHeight * 0.5; //TODO:Should probably be font ascent double tbWidth; - tbWidth = Rectangle.Bounds.Width - tbLeft - RightMargin; + tbWidth = Rectangle.Bounds.Width - tbLeft; var tbHeight = entryHeight; sls.Textbox = new DrawingTextbody(Chart, Rectangle.Bounds, tbLeft, tbTop, tbWidth, tbHeight, false, true); @@ -563,9 +563,10 @@ private LineRenderItem GetLineSeriesIcon(ExcelChart ct, ExcelChartStandardSerie var line = new LineRenderItem(Rectangle.Bounds); line.SetDrawingPropertiesFill(ChartRenderer.Theme, cStandardSerie.Fill, Chart.StyleManager.Style.SeriesLine.FillReference.Color); line.SetDrawingPropertiesBorder(ChartRenderer.Theme, cStandardSerie.Border, Chart.StyleManager.Style.SeriesLine.BorderReference.Color, cStandardSerie.Border.Fill.Style != eFillStyle.NoFill, 0.75); - var icon = pSls?.SeriesIcon as LineRenderItem; + double iconTop = 0, iconLeft = 0; + pSls?.GetIconTopLeft(out iconTop, out iconLeft); - GetItemPosition(pSls, entryWidth, entryHeight, icon?.X1 ?? 0D, icon?.Y1 ?? 0D, out double x, out double y); + GetItemPosition(pSls, entryWidth, entryHeight, iconLeft, iconTop, out double x, out double y); line.X1 = x; line.X2 = x + LineLength; @@ -580,9 +581,10 @@ private LineRenderItem GetTrendLineSeriesIcon(ExcelChart ct, ExcelChartTrendline var line = new LineRenderItem(Rectangle.Bounds); line.SetDrawingPropertiesFill(ChartRenderer.Theme, tl.Fill, Chart.StyleManager.Style.Trendline.FillReference.Color); line.SetDrawingPropertiesBorder(ChartRenderer.Theme, tl.Border, Chart.StyleManager.Style.Trendline.BorderReference.Color, tl.Border.Fill.Style != eFillStyle.NoFill, 0.75); - var icon = pSls?.SeriesIcon as LineRenderItem; + double iconTop = 0, iconLeft = 0; + pSls?.GetIconTopLeft(out iconTop, out iconLeft); - GetItemPosition(pSls, entryWidth, entryHeight, icon?.X1 ?? 0D, icon?.Y1 ?? 0D, out double x, out double y); + GetItemPosition(pSls, entryWidth, entryHeight, iconLeft, iconTop, out double x, out double y); line.X1 = x; line.Y1 = y; @@ -597,9 +599,11 @@ private RectRenderItem GetBarSeriesIcon(ExcelChart ct, ExcelChartStandardSerie c { var item = new RectRenderItem(Rectangle.Bounds); var iconHeight = GetIconLenght(ct, entryHeight); - var icon = pSls?.SeriesIcon as RectRenderItem; + //var icon = pSls?.SeriesIcon as RectRenderItem; + double iconTop = 0, iconLeft = 0; + pSls?.GetIconTopLeft(out iconTop, out iconLeft); - GetItemPosition(pSls, entryWidth, entryHeight, icon?.Left ?? 0D, icon?.Top+(iconHeight/2) ?? 0D, out double x, out double y); + GetItemPosition(pSls, entryWidth, entryHeight, iconTop, iconTop + (iconHeight / 2), out double x, out double y); item.LineCap = LineCap.Round; item.Left = x; diff --git a/src/EPPlus/Drawing/Renderer/Chart/ChartTypeDrawers/BarColumnChartTypeDrawer.cs b/src/EPPlus/Drawing/Renderer/Chart/ChartTypeDrawers/BarColumnChartTypeDrawer.cs index 955039457f..d91018e3b1 100644 --- a/src/EPPlus/Drawing/Renderer/Chart/ChartTypeDrawers/BarColumnChartTypeDrawer.cs +++ b/src/EPPlus/Drawing/Renderer/Chart/ChartTypeDrawers/BarColumnChartTypeDrawer.cs @@ -80,12 +80,6 @@ internal override void DrawSeries() //} } - ////Trendlines and trendline labels - //foreach (var tr in Trendlines) - //{ - // tr.AppendRenderItems(ChartAreaRenderItems); - //} - //Append Trendline render items. foreach (var tr in Trendlines) { tr.CreateRenderCoordinatesAndDatalabel(); diff --git a/src/EPPlus/Drawing/Renderer/Chart/ChartTypeDrawers/Trendlines/ChartTrendlineRenderer.cs b/src/EPPlus/Drawing/Renderer/Chart/ChartTypeDrawers/Trendlines/ChartTrendlineRenderer.cs index 81cf17e369..fa9ad5c115 100644 --- a/src/EPPlus/Drawing/Renderer/Chart/ChartTypeDrawers/Trendlines/ChartTrendlineRenderer.cs +++ b/src/EPPlus/Drawing/Renderer/Chart/ChartTypeDrawers/Trendlines/ChartTrendlineRenderer.cs @@ -12,14 +12,11 @@ Date Author Change *************************************************************************************************/ using EPPlus.DrawingRenderer; using EPPlus.DrawingRenderer.RenderItems; -using EPPlus.Export.ImageRenderer.RenderItems.SvgItem; using EPPlusImageRenderer; using EPPlusImageRenderer.RenderItems; using EPPlusImageRenderer.Svg; -using OfficeOpenXml.DigitalSignatures; using OfficeOpenXml.Drawing.Chart; using OfficeOpenXml.Drawing.Renderer.TextBox; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; using OfficeOpenXml.FormulaParsing.Excel.Functions.Statistical; using OfficeOpenXml.Utils.TypeConversion; using System; @@ -32,7 +29,7 @@ internal class ChartTrendlineRenderer : ChartDrawingObject { private ExcelChartTrendline _trendline; private double[] _ySerie; - private List _xSerie; + private List _xSerie; private ExcelChart _chartType; private bool _useSecondaryAxis; private int _serieCount, _seriePos; @@ -40,7 +37,7 @@ public ChartTrendlineRenderer(ChartRenderer svgChart, ExcelChartTrendline trendl { _chartType = chartType; _trendline = trendline; - _xSerie = xSerie; + _xSerie = GetXSerie(xSerie); _ySerie = ySerie.Select(y => ConvertUtil.GetValueDouble(y)).ToArray(); _useSecondaryAxis = chartType.UseSecondaryAxis; _serieCount = _chartType.Series.Count; @@ -50,8 +47,8 @@ public ChartTrendlineRenderer(ChartRenderer svgChart, ExcelChartTrendline trendl { case eTrendLine.Linear: CalculateLinear(); - Coordinates.Add(new Coordinate(0, GetLinearValueAtPosition(1))); - Coordinates.Add(new Coordinate(_xSerie.Count-1, GetLinearValueAtPosition(_xSerie.Count))); + Coordinates.Add(new Coordinate(_xSerie[0], GetLinearValueAtPosition(_xSerie[0]))); + Coordinates.Add(new Coordinate(_xSerie[_xSerie.Count-1], GetLinearValueAtPosition(_xSerie[_xSerie.Count - 1]))); break; case eTrendLine.Exponential: CalculateExponential(); @@ -95,6 +92,28 @@ public ChartTrendlineRenderer(ChartRenderer svgChart, ExcelChartTrendline trendl } } + /// + /// Get the X serie values. If the values are not numeric, return a serie with the index values (1,2,3,...). Trendline calculation requires numeric X values, but Excel allows non-numeric X values for trendlines, in which case it uses the index values as X for calculation. + /// + /// Input values + /// Output doubles + private List GetXSerie(List xSerie) + { + var l=new List(); + for(int i=0;i (double)(index + 1)).ToList(); + } + } + return l; + } + private void CreateDatalabel() { if(_trendline.DisplayEquation==false && _trendline.DisplayRSquaredValue==false) @@ -104,8 +123,8 @@ private void CreateDatalabel() //Display the label for the trendline with equation and R² value. var lbl = _trendline.Label; var coord = RenderCoordinates; - var x = ChartRenderer.Plotarea.Rectangle.Left + coord[coord.Length - 2]; - var y = ChartRenderer.Plotarea.Rectangle.Top + coord[coord.Length - 1]; + var x = ChartRenderer.Plotarea.Group.Left + coord[coord.Length - 2]; + var y = ChartRenderer.Plotarea.Group.Top + coord[coord.Length - 1]; double width = 0, height = 0; if (_trendline.Label.Layout.HasLayout) @@ -138,7 +157,7 @@ private void CreateDatalabel() } DataLabel.TextBody.AutoSize = true; } - //DataLabel.ImportTextBody(lbl.TextBody, true, OfficeOpenXml.Style.ExcelHorizontalAlignment.Center); + DataLabel.ImportTextBody(lbl.TextBody, true, OfficeOpenXml.Style.ExcelHorizontalAlignment.Center); var labelText = ""; if (_trendline.DisplayEquation) { @@ -152,9 +171,8 @@ private void CreateDatalabel() } labelText += RSquare; } + lbl.TextBody.Paragraphs[0].HorizontalAlignment = OfficeOpenXml.Drawing.eTextAlignment.Center; DataLabel.ImportParagraph(lbl.TextBody.Paragraphs[0], 0, labelText); - //DataLabel.AddText(0, labelText); - //DataLabel.TextBody.Paragraphs[0].AddText(labelText, 0); DataLabel.LeftMargin = DataLabel.RightMargin = 4; DataLabel.TopMargin = DataLabel.BottomMargin = 2; @@ -196,9 +214,11 @@ private void CreateDatalabel() private void CalculateLinear() { var n = _xSerie.Count; - var sumX = n * (n + 1) / 2; + //var sumX = (double)n * (n + 1) / 2; + var sumX = _xSerie.Sum(x => x); + var sumX2 = _xSerie.Sum(x => x * x); var sumY = _ySerie.Sum(y => y); - var sumX2 = n * (n + 1) * (2 * n + 1) / 6; + //var sumX2 = (double)n * (n + 1) * (2 * n + 1) / 6; var sumXY = 0D; double slope, intercept; @@ -219,15 +239,17 @@ private void CalculateLinear() intercept = _trendline.Intercept; for (int i = 0; i < _ySerie.Length; i++) { - sumXY += (_ySerie[i] - intercept) * (i + 1); + double x= _xSerie[i]; + sumXY += x * (_ySerie[i] - intercept); // x = i+1 } + slope = sumXY / sumX2; } //var r2 = Math.Pow(Pearson.PearsonImpl(_ySerie.Cast(), GetLinearSerie(slope, intercept)), 2); var r2 = CalculateRSquared(x => slope * x + intercept, _ySerie, _trendline.Intercept); Coefficients = [slope, intercept]; - Formula = $"y={slope:G5}x{(GetValueAndSignSuppressZero(intercept))}"; + Formula = $"y={slope:N4}x{(GetValueAndSignSuppressZero(intercept))}"; RSquare = $"R²={r2:N4}"; } @@ -739,7 +761,7 @@ private void CalcMa() } } - private double GetLinearValueAtPosition(int x) + private double GetLinearValueAtPosition(double x) { return Coefficients[1] + Coefficients[0] * x; } diff --git a/src/EPPlus/Drawing/Renderer/DrawingLegendSerie.cs b/src/EPPlus/Drawing/Renderer/DrawingLegendSerie.cs index 6687300b18..4809d8098b 100644 --- a/src/EPPlus/Drawing/Renderer/DrawingLegendSerie.cs +++ b/src/EPPlus/Drawing/Renderer/DrawingLegendSerie.cs @@ -14,11 +14,31 @@ Date Author Change using EPPlus.Export.ImageRenderer.RenderItems.Shared; using EPPlusImageRenderer.RenderItems; using OfficeOpenXml.Drawing.Renderer.TextBox; +using System; namespace EPPlusImageRenderer.Svg { internal class DrawingLegendSerie : SvgLegendSeriesIcon { internal DrawingTextbody Textbox { get; set; } + + internal void GetIconTopLeft(out double top, out double left) + { + if (SeriesIcon is LineRenderItem line) + { + top = line.Y1; + left = line.X1; + } + else if (SeriesIcon is RectRenderItem rect) + { + top = rect.Top; + left = rect.Left; + } + else + { + top = SeriesIcon.Bounds.Top; + left = SeriesIcon.Bounds.Left; + } + } } } \ No newline at end of file diff --git a/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingParagraphRenderItem.cs b/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingParagraphRenderItem.cs index 66135728e2..6bc07e58c6 100644 --- a/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingParagraphRenderItem.cs +++ b/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingParagraphRenderItem.cs @@ -58,8 +58,7 @@ public DrawingParagraphRenderItem(DrawingTextbody textBody, BoundingBox parent, //---Initialize / calculate lines and runs--- //measurer must be set before AddLinesAndRichText - ParagraphFont = p.DefaultRunProperties.GetMeasureFont(); - DefaultParagraphFont = new OpenTypeFontInfoBase(ParagraphFont); + DefaultParagraphFont = new FontFormatBase(p.DefaultRunProperties.GetMeasureFont()); //---Calculate linespacing--- ImportLineSpacing(p.LineSpacing.LineSpacingType, p.LineSpacing.Value); @@ -99,25 +98,17 @@ private void ImportLinesAndTextRuns(ExcelDrawingParagraph p, string textIfEmpty) else { //Log line positions and run sizes - GenerateTextFragments(p.TextRuns); + GenerateRichText(p.TextRuns); + TextIfEmptyIsNull = string.IsNullOrEmpty(textIfEmpty); //Initalize and wrap textruns - AddTextLinesAndSpacing(textIfEmpty); + WrapTextFragmentsAndGenerateTextRuns(); ImportStyles(p.TextRuns, p.DefaultRunProperties); } } internal void ImportLinesAndTextRunsDefault(string textIfEmpty, ExcelTextFont font = null) { - GenerateTextFragments(textIfEmpty); - - Bounds.Left = GetAlignmentHorizontal(HorizontalAlignment); - if (HorizontalAlignment == TextAlignment.Center) - { - _centerAdjustment = GetAlignmentHorizontal(HorizontalAlignment); - } - - AddTextLinesAndSpacing(textIfEmpty); - + ImportLinesAndTextRunsBase(textIfEmpty); //Import RichText data to each run foreach (var run in Runs) { @@ -126,48 +117,18 @@ internal void ImportLinesAndTextRunsDefault(string textIfEmpty, ExcelTextFont fo } } - - void GenerateTextFragments(string text) - { - if (_textFragments == null) - { - _textFragments = new List(); - } - - if (string.IsNullOrEmpty(text) == false) - { - var currentFrag = new TextFragment() { Text = text }; - currentFrag.RichTextOptions.SetFont(ParagraphFont); - _textFragments.Add(currentFrag); - } - _layoutSystem = new LayoutSystem(_textFragments); - } - /// /// Log linebreak positions and sizes of the runs /// So that we can easily know what textfragment is on what line and what size it has later /// /// - void GenerateTextFragments(ExcelDrawingTextRunCollection runs, List? optionLst = null) + void GenerateRichText(ExcelDrawingTextRunCollection runs/*, List? optionLst = null*/) { - var lstOfRichText = runs.ExportToOpenTypeFormat(); - - if (optionLst == null) + //var lstOfRichText = runs.ExportToOpenTypeFormat(); + var lstOfRichText = runs.ExportToImageRendererFormat(); + foreach (var rt in lstOfRichText) { - _layoutSystem = new LayoutSystem(lstOfRichText); - } - else - { - //Use this instead if we ever need shaping options - for(int i= 0; i< lstOfRichText.Count; i++) - { - TextFragmentBase frag = new TextFragmentBase(lstOfRichText[i]); - //Is initalized within the constructor but if for some reason a certain fragment needs to be imported differently - //We can do so here - frag.Options = optionLst[i]; - _textFragments.Add(frag); - } - _layoutSystem = new LayoutSystem(_textFragments); + _textFragments.Add(rt); } } @@ -210,6 +171,14 @@ private void ImportStyleInfo(DrawingTextbody textBody, ExcelDrawingParagraph p) var fill = p._paragraphs[0].DefaultRunProperties.Fill; this.SetDrawingPropertiesFill(textBody.Theme, fill, null); } + else + { + var fc = ColorConverter.GetThemeColor(textBody.Theme.ColorScheme.Light1); + fc = ColorConverter.GetAdjustedColor(PathFillMode.Norm, fc); + FillColor = "#" + fc.ToArgb().ToString("x8").Substring(2); + //Use shape fill somehow + //Maybe use a name property for fallback theme accent1 color? + } } } } @@ -253,12 +222,12 @@ private void ImportAlignment(bool isAutoSize, double maxWidth, double parentWidt private void ImportLineSpacing(eDrawingTextLineSpacing lsType, double lineSpacingValue) { _lsType = (TextLineSpacing)lsType; - var shaper = (TextShaper)OpenTypeFonts.GetShaperForFont(ParagraphFont); + var shaper = (TextShaper)OpenTypeFonts.GetShaperForFont(DefaultParagraphFont); ParagraphLineSpacing = GetParagraphLineSpacingInPoints( lineSpacingValue, shaper, - ParagraphFont.Size); + DefaultParagraphFont.Size); } void ImportStyleFallback(ExcelTextFont font, DrawingTextRunRenderItem run) diff --git a/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingTextBody.cs b/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingTextBody.cs index 0f0cad2501..151c7c2d44 100644 --- a/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingTextBody.cs +++ b/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingTextBody.cs @@ -1,5 +1,7 @@ using EPPlus.DrawingRenderer.RenderItems; +using EPPlus.DrawingRenderer.RenderItems.SvgItem; using EPPlus.Export.ImageRenderer.RenderItems.Shared; +using EPPlus.Fonts.OpenType.Integration.DataHolders; using EPPlus.Graphics; using EPPlusImageRenderer; using EPPlusImageRenderer.RenderItems; @@ -24,8 +26,6 @@ public class DrawingTextbody : RenderTextBody internal ExcelTheme Theme { get; } - - public DrawingTextbody(ExcelDrawing drawing, BoundingBox parent, bool autoSize, bool clampedToParent = false) : base(parent, autoSize) { _drawing = drawing; @@ -108,7 +108,7 @@ internal void SetHorizontalAlignmentPosition() //} } - internal virtual void ImportTextBody(ExcelTextBody body, ExcelHorizontalAlignment horizontalDefault = ExcelHorizontalAlignment.Left) + internal virtual void ImportTextBodyAndParagraphs(ExcelTextBody body, ExcelHorizontalAlignment horizontalDefault = ExcelHorizontalAlignment.Left) { Text = null; VerticalAlignment = (TextAnchoringType)body.Anchor; @@ -117,6 +117,19 @@ internal virtual void ImportTextBody(ExcelTextBody body, ExcelHorizontalAlignmen double currentHeight = 0; double largestWidth = double.MinValue; + body.GetInsetsInPoints(out double left, out double top, out double right, out double bottom); + + if (AutoSize == false) + { + LeftMargin = left; + TopMargin = top; + RightMargin = right; + BottomMargin = bottom; + + MaxHeight = MaxHeight - top - bottom; + MaxWidth = MaxWidth - left - right; + } + foreach (var paragraph in body.Paragraphs) { ImportParagraph(paragraph, currentHeight); @@ -137,31 +150,6 @@ internal virtual void ImportTextBody(ExcelTextBody body, ExcelHorizontalAlignmen Bounds.Top = GetAlignmentVertical(); } - /// - /// Get the start of text space vertically - /// - /// - private double GetAlignmentVertical() - { - double alignmentY = 0; - - switch (VerticalAlignment) - { - case TextAnchoringType.Top: - alignmentY = Bounds.Top; - break; - //Center means center of a Shape's ENTIRE bounding box height. - //Not center of the Inset GetRectangle - case TextAnchoringType.Center: - alignmentY = (MaxHeight - Bounds.Height) / 2 + Bounds.Top; - break; - case TextAnchoringType.Bottom: - alignmentY = MaxHeight - Bounds.Height; - break; - } - - return alignmentY; - } //internal override void AppendRenderItems(List renderItems) //{ @@ -208,5 +196,12 @@ protected override ParagraphRenderItem CreateParagraph(BoundingBox parent, strin { return new DrawingParagraphRenderItem(this, parent, textIfEmpty); } + + protected override ParagraphRenderItem CreateParagraph(BoundingBox parent, IRichTextFormatSimple richText) + { + var paragraph = new SvgParagraphRenderItem(this, parent, "", false); + paragraph.AddRichText(richText); + return paragraph; + } } } diff --git a/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingTextBox.cs b/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingTextBox.cs index 7a7a53140e..9bdb7605d2 100644 --- a/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingTextBox.cs +++ b/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingTextBox.cs @@ -94,9 +94,9 @@ public RectRenderItem Rectangle // renderItems.Add(new SvgEndGroupItem(DrawingRenderer, rect.Bounds)); //} - internal void AddText(double startingY, string text = null) + internal void AddText(string text = null) { - TextBody.AddParagraph(startingY, text); + TextBody.AddParagraph(text); } @@ -228,7 +228,7 @@ internal void ImportTextBody(ExcelTextBody body, bool useDefaults = true, ExcelH RightMargin = r; BottomMargin = b; - TextBody.ImportTextBody(body); + TextBody.ImportTextBodyAndParagraphs(body, horizontalDefault); } public override void AppendRenderItems(List renderItems) diff --git a/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingTextRunItem.cs b/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingTextRunItem.cs index 662ccc1a46..7acef0d815 100644 --- a/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingTextRunItem.cs +++ b/src/EPPlus/Drawing/Renderer/RenderItems/Textbox/DrawingTextRunItem.cs @@ -36,7 +36,7 @@ internal DrawingTextRunRenderItem(BoundingBox parent, string text, int origRtInd /// internal void ImportTextRunBase(ExcelParagraphTextRunBase run, IFontFormatBase baseFont) { - InitializeBase(new OpenTypeFontInfoBase(run.GetMeasureFont())); + InitializeBase(new FontFormatBase(run.GetMeasurementFont())); _currentText = string.IsNullOrEmpty(_currentText) ? run.Text : _currentText; _isFirstInParagraph = run.IsFirstInParagraph; _baseline = run.Baseline; @@ -96,7 +96,7 @@ internal DrawingTextRunRenderItem(BoundingBox parent, IFontFormatBase font, stri /// /// Legacy format /// - internal DrawingTextRunRenderItem(BoundingBox parent, string text, ExcelTextFont font, string displayText) : base(parent, text, new OpenTypeFontInfoBase(font.GetMeasureFont()), displayText) + internal DrawingTextRunRenderItem(BoundingBox parent, string text, ExcelTextFont font, string displayText) : base(parent, text, new FontFormatBase(font.GetMeasureFont()), displayText) { _baseline = font.Baseline; @@ -117,7 +117,7 @@ internal DrawingTextRunRenderItem(BoundingBox parent, IFontFormatBase font, stri /// new format /// /// - internal DrawingTextRunRenderItem(BoundingBox parent, ExcelParagraphTextRunBase run, string displayText = "") : base(parent, run.Text, new OpenTypeFontInfoBase(run.GetMeasurementFont()), displayText) + internal DrawingTextRunRenderItem(BoundingBox parent, ExcelParagraphTextRunBase run, string displayText = "") : base(parent, run.Text, new FontFormatBase(run.GetMeasurementFont()), displayText) { //This is pre-determined/irrelevant here and does not need to be calculated as sizes are already what they should _isFirstInParagraph = false; @@ -165,9 +165,9 @@ void ImportRichTextInfo(bool italic, bool bold, eUnderLineType uType, Color uCol { _isItalic = italic; _isBold = bold; - _underLineType = (UnderLineType)uType; + _underLineType = (eDrawingUnderLineType)uType; _underlineColor = uColor; - _strikeType = (StrikeType)strikeType; + _strikeType = (eDrawingStrikeType)strikeType; } void SetClippingHeightToCurrentTextBoxBottom(BoundingBox parent) diff --git a/src/EPPlus/Drawing/Renderer/ShapeRenderer.cs b/src/EPPlus/Drawing/Renderer/ShapeRenderer.cs index c83e1dbcab..78fbbac90e 100644 --- a/src/EPPlus/Drawing/Renderer/ShapeRenderer.cs +++ b/src/EPPlus/Drawing/Renderer/ShapeRenderer.cs @@ -264,7 +264,7 @@ DrawingTextbody CreateTextBodyItem(ExcelTextBody bodyOrig) RenderItems.Add(grp); var txtBodyItem = new DrawingTextbody(Drawing, MarginTextBox.Bounds, MarginTextBox.Left, MarginTextBox.Top, MarginTextBox.Width, MarginTextBox.Height); - txtBodyItem.ImportTextBody(bodyOrig); + txtBodyItem.ImportTextBodyAndParagraphs(bodyOrig); txtBodyItem.AppendRenderItems(grp.RenderItems); diff --git a/src/EPPlus/Drawing/Style/Coloring/ExcelDrawingThemeColorManager.cs b/src/EPPlus/Drawing/Style/Coloring/ExcelDrawingThemeColorManager.cs index bfeb091d12..84ab4ef526 100644 --- a/src/EPPlus/Drawing/Style/Coloring/ExcelDrawingThemeColorManager.cs +++ b/src/EPPlus/Drawing/Style/Coloring/ExcelDrawingThemeColorManager.cs @@ -118,10 +118,6 @@ private bool IsTopNodeColorNode(XmlNode topNode) /// /// public eDrawingColorType ColorType { get; internal protected set; } = eDrawingColorType.None; - internal void SetXml(XmlNamespaceManager nameSpaceManager, XmlNode node) - { - - } ExcelColorTransformCollection _transforms = null; /// /// Color transformations diff --git a/src/EPPlus/Drawing/Style/Fill/ExcelDrawingPatternFill.cs b/src/EPPlus/Drawing/Style/Fill/ExcelDrawingPatternFill.cs index 157b9ceff2..f7eb753fec 100644 --- a/src/EPPlus/Drawing/Style/Fill/ExcelDrawingPatternFill.cs +++ b/src/EPPlus/Drawing/Style/Fill/ExcelDrawingPatternFill.cs @@ -112,10 +112,7 @@ internal override void SetXml(XmlNamespaceManager nsm, XmlNode node) } _xml.SetXmlNodeString("@prst", PatternType.ToEnumString()); var fgNode=_xml.CreateNode("a:fgClr"); - ForegroundColor.SetXml(nsm, fgNode); - var bgNode = _xml.CreateNode("a:bgClr"); - BackgroundColor.SetXml(nsm, bgNode); } internal override void GetXml() { diff --git a/src/EPPlus/Drawing/Style/Fill/ExcelDrawingSolidFill.cs b/src/EPPlus/Drawing/Style/Fill/ExcelDrawingSolidFill.cs index 169e40fe5e..e864711d2a 100644 --- a/src/EPPlus/Drawing/Style/Fill/ExcelDrawingSolidFill.cs +++ b/src/EPPlus/Drawing/Style/Fill/ExcelDrawingSolidFill.cs @@ -85,7 +85,6 @@ internal override void SetXml(XmlNamespaceManager nsm, XmlNode node) { Color.SetPresetColor(ePresetColor.Black); } - _color.SetXml(nsm, node); } internal override void GetXml() { diff --git a/src/EPPlus/Drawing/Style/Text/ExcelDrawingTextRunCollection.cs b/src/EPPlus/Drawing/Style/Text/ExcelDrawingTextRunCollection.cs index 7c31979241..aa01293b43 100644 --- a/src/EPPlus/Drawing/Style/Text/ExcelDrawingTextRunCollection.cs +++ b/src/EPPlus/Drawing/Style/Text/ExcelDrawingTextRunCollection.cs @@ -20,6 +20,7 @@ Date Author Change using OfficeOpenXml.Drawing.Interfaces; using OfficeOpenXml.Interfaces.RichText; using EPPlus.Fonts.OpenType.Integration; +using EPPlus.DrawingRenderer.RenderItems.Textbox; namespace OfficeOpenXml.Drawing { @@ -165,9 +166,19 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } - internal List ExportToOpenTypeFormat() + internal List ExportToImageRendererFormat() { - List RtFormatList = new List(); + List RtFormatList = new List(); + foreach (var item in _textRuns) + { + RtFormatList.Add(item.ExportToImageRendererFormat()); + } + return RtFormatList; + } + + internal List ExportToOpenTypeFormat() + { + List RtFormatList = new List(); foreach (var item in _textRuns) { RtFormatList.Add(item.ExportToOpenTypeFormat()); diff --git a/src/EPPlus/Drawing/Style/Text/ExcelParagraphTextRunBase.cs b/src/EPPlus/Drawing/Style/Text/ExcelParagraphTextRunBase.cs index 14bbcaf327..1f102fbbf8 100644 --- a/src/EPPlus/Drawing/Style/Text/ExcelParagraphTextRunBase.cs +++ b/src/EPPlus/Drawing/Style/Text/ExcelParagraphTextRunBase.cs @@ -10,6 +10,7 @@ Date Author Change ************************************************************************************************* 09/15/2025 EPPlus Software AB EPPlus 9 *************************************************************************************************/ +using EPPlus.DrawingRenderer.RenderItems.Textbox; using EPPlus.Fonts.OpenType.Integration.DataHolders; using OfficeOpenXml.Drawing; using OfficeOpenXml.Drawing.Interfaces; @@ -24,6 +25,7 @@ Date Author Change using System.Drawing; using System.Linq; using System.Xml; +using tc = OfficeOpenXml.Utils.TypeConversion; namespace OfficeOpenXml.Drawing { @@ -166,7 +168,11 @@ public bool HasFill() //UnderlineLine underlineFill etc. #region Underline - string _underLineColorPath = "a:rPr/a:uFill/a:solidFill/a:srgbClr/@val"; + string _underLineColorSetPath = "a:rPr/a:uFill/a:solidFill/a:srgbClr/@val"; + string _underLineColorPath = "a:rPr/a:uFill/a:solidFill"; + + ExcelDrawingColorManager _underlineColorManager = null; + /// /// The fonts underline color /// @@ -174,19 +180,28 @@ public Color UnderLineColor { get { - string col = GetXmlNodeString(_underLineColorPath); - if (col == "") + if(_underlineColorManager == null) + { + _underlineColorManager = new ExcelDrawingColorManager(NameSpaceManager, TopNode, _underLineColorPath, SchemaNodeOrder); + } + + if(_underlineColorManager.ColorType == eDrawingColorType.Scheme) { - return Color.Empty; + return tc.ColorConverter.GetThemeColor(_prd.Package.Workbook.ThemeManager.GetOrCreateTheme(), _underlineColorManager); } else { - return Color.FromArgb(int.Parse(col, System.Globalization.NumberStyles.AllowHexSpecifier)); + var col = _underlineColorManager.GetColor(); + return col; } } set { - SetXmlNodeString(_underLineColorPath, value.ToArgb().ToString("X").Substring(2, 6)); + if (_underlineColorManager == null) + { + _underlineColorManager = new ExcelDrawingColorManager(NameSpaceManager, TopNode, _underLineColorPath, SchemaNodeOrder); + } + _underlineColorManager.SetRgbColor(value); } } @@ -614,9 +629,44 @@ public bool IsLastInParagraph /// Export to OpenTypeFormat /// /// - internal IRichTextFormatBase ExportToOpenTypeFormat() + internal IRichTextFormatEssential ExportToOpenTypeFormat() + { + var measureFont = GetMeasurementFont(); + var rtBase = new RichTextFormatBase(Text, measureFont.FontFamily, measureFont.Size, FontBold, FontItalic); + return rtBase; + } + + /// + /// Export to OpenTypeFormat + /// + /// + internal IRichTextFormatDrawing ExportToImageRendererFormat() { - var rtBase = new OpenTypeRichTextBase(Text, GetMeasurementFont().FontFamily, FontSize, FontBold, FontItalic); + //This is neccesary for the baseline to be applied to the fontsize correctly + var measureFont = GetMeasurementFont(); + + var rtBase = new RichTextFormatDrawing(Text, measureFont.FontFamily, measureFont.Size, FontBold, FontItalic); + + switch (Capitalization) + { + case eTextCapsType.All: + rtBase.Text = Text.ToUpper(); + break; + case eTextCapsType.Small: + rtBase.Text = Text.ToLower(); + break; + default: + //Leave as is + break; + + } + + rtBase.Capitalization = (int)Capitalization; + rtBase.HighLightColor = HighlightColor.GetColor(); + rtBase.FontColor = Fill.Color; + rtBase.UnderlineColor = UnderLineColor; + rtBase.Baseline = Baseline; + rtBase.Spacing = Spacing; return rtBase; } } diff --git a/src/EPPlus/Encryption/EncryptionHandler.cs b/src/EPPlus/Encryption/EncryptionHandler.cs index fa404cf06d..ce739bcbab 100644 --- a/src/EPPlus/Encryption/EncryptionHandler.cs +++ b/src/EPPlus/Encryption/EncryptionHandler.cs @@ -681,8 +681,8 @@ private HashAlgorithm GetHashProvider(EncryptionInfoAgile.EncryptionKeyData encr { case eHashAlgorithm.MD5: return new MD5CryptoServiceProvider(); - case eHashAlgorithm.RIPEMD160: - return new RIPEMD160Managed(); + //case eHashAlgorithm.RIPEMD160: + // return new RIPEMD160Managed(); case eHashAlgorithm.SHA1: return new SHA1CryptoServiceProvider(); case eHashAlgorithm.SHA256: diff --git a/src/EPPlus/Properties/AssemblyInfo.cs b/src/EPPlus/Properties/AssemblyInfo.cs index c364a190b2..4d463e8c41 100644 --- a/src/EPPlus/Properties/AssemblyInfo.cs +++ b/src/EPPlus/Properties/AssemblyInfo.cs @@ -20,8 +20,7 @@ Date Author Change // associated with an assembly. [assembly: InternalsVisibleTo("EPPlusTest, PublicKey=00240000048000009400000006020000002400005253413100040000010001001dd11308ec93a6ebcec727e183a8972dc6f95c23ecc34aa04f40cbfc9c17b08b4a0ea5c00dcd203bace44d15a30ce8796e38176ae88e960ceff9cc439ab938738ba0e603e3d155fc298799b391c004fc0eb4393dd254ce25db341eb43303e4c488c9500e126f1288594f0710ec7d642e9c72e76dd860649f1c48249c00e31fba")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] -[assembly: InternalsVisibleTo("EPPlus.Export.ImageRenderer, PublicKey=002400000480000094000000060200000024000052534131000400000100010045e5d8275ad7edb388eb9c60ac6e1dd30161aea53f5334c45751217df06feb6ae799a1866fc0671f9ec0d6dbec23807ad71012617cb8d471979216c63baa607754a5d5f20b45e1ce5c256685552a2a09e19a35b554d554dcbbfecdd331bf15cb2b5c4fbd76f7424f30493635c0234ee1ea782ebfdfdc0dec3dbbc167c4c061cc")] -[assembly: InternalsVisibleTo("EPPlus.Export.ImageRenderer.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010065aa00a9b0dcfa0e8debefb14c3b6c12ef658ce1cfbfafcb6eb7dbdc0c49e4f70ea144ba29d827453f0716ffc8c1d87450ce0cf255bda1def174915caaa78f373f291ce1a7edf91ba6f9dc961d937b19d46dd5d7d70a6e2097c749d43780f0d00b29a6f5aec5c1d191bee69de0c889a6d2566bef3cb235612351eae7015382d0")] +[assembly: InternalsVisibleTo("EPPlus.DrawingRenderer.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010065aa00a9b0dcfa0e8debefb14c3b6c12ef658ce1cfbfafcb6eb7dbdc0c49e4f70ea144ba29d827453f0716ffc8c1d87450ce0cf255bda1def174915caaa78f373f291ce1a7edf91ba6f9dc961d937b19d46dd5d7d70a6e2097c749d43780f0d00b29a6f5aec5c1d191bee69de0c889a6d2566bef3cb235612351eae7015382d0")] [assembly: InternalsVisibleTo("EPPlus.Export.Pdf, PublicKey=00240000048000009400000006020000002400005253413100040000010001005dd165010a983c48dcdee4d91434cd53fbc1651ba55263e9be73f5fc07ec63a666c90f3d9de7356c9214f461bae351a0375c5d75db0a9b45800bd347b81559c5a0bc84313de08d5d278b693e7da96678aa17d00fc8b825e37c0e28142c64cf6e7d174ebd11dc04142eb2ad438ca137f324ad9757bf02279a5f7a9d7479afbcb0")] [assembly: InternalsVisibleTo("EPPlus.Graphics, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e9b33e79ac9cbb1ca1e0df0aa47d956404607d99cb28a5982f936de662eb2fd3d3459c5b625fe4791606113ff89ee8b915e1446d348be8c6b5106fd3ffde14cf337587920e1c76bebc3bfd95d7525f3a5bc384fb19f3c06306b8cd5ffbe4284c36d8285f16cc50e6b408a2e197229108ce683d865a946b92a324cfba8dbf57c2")] [assembly: Guid("9dd43b8d-c4fe-4a8b-ad6e-47ef83bbbb01")] diff --git a/src/EPPlus/Style/ExcelTextFont.cs b/src/EPPlus/Style/ExcelTextFont.cs index 8b24d3c4d8..416337815b 100644 --- a/src/EPPlus/Style/ExcelTextFont.cs +++ b/src/EPPlus/Style/ExcelTextFont.cs @@ -12,6 +12,7 @@ Date Author Change *************************************************************************************************/ using OfficeOpenXml.Drawing; using OfficeOpenXml.Drawing.Interfaces; +using OfficeOpenXml.Drawing.Style.Coloring; using OfficeOpenXml.Drawing.Style.Font; using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; using OfficeOpenXml.Interfaces.Drawing.Text; @@ -21,7 +22,9 @@ Date Author Change using System.Drawing; using System.Globalization; using System.Linq; +using System.Reflection.Emit; using System.Xml; +using tc = OfficeOpenXml.Utils.TypeConversion; namespace OfficeOpenXml.Style { @@ -177,7 +180,9 @@ internal void SetFromXml(XmlElement copyFromElement) } } - string _underLineColorPath = "a:uFill/a:solidFill/a:srgbClr/@val"; + string _underLineColorPath = "a:uFill/a:solidFill"; + + ExcelDrawingColorManager _underlineColorManager = null; /// /// The fonts underline color /// @@ -185,20 +190,28 @@ public override Color UnderLineColor { get { - string col = _xml.GetXmlNodeString(_underLineColorPath); - if (col == "") + if(_underlineColorManager == null ) { - return Color.Empty; + _underlineColorManager = new ExcelDrawingColorManager(_xml.NameSpaceManager, _xml.TopNode, _underLineColorPath, _xml.SchemaNodeOrder); + } + if (_underlineColorManager.ColorType == eDrawingColorType.Scheme) + { + return tc.ColorConverter.GetThemeColor(_pictureRelationDocument.Package.Workbook.ThemeManager.GetOrCreateTheme(), _underlineColorManager); } else { - return Color.FromArgb(int.Parse(col, System.Globalization.NumberStyles.AllowHexSpecifier)); + return _underlineColorManager.GetColor(); } } set { CreateTopNode(); - _xml.SetXmlNodeString(_underLineColorPath, value.ToArgb().ToString("X").Substring(2, 6)); + + if (_underlineColorManager == null) + { + _underlineColorManager = new ExcelDrawingColorManager(_xml.NameSpaceManager, _xml.TopNode, _underLineColorPath, _xml.SchemaNodeOrder); + } + _underlineColorManager.SetRgbColor(value); } } string _italicPath = "@i"; diff --git a/src/EPPlus/Style/RichText/ExcelRichText.cs b/src/EPPlus/Style/RichText/ExcelRichText.cs index e4b3072444..57a8fec814 100644 --- a/src/EPPlus/Style/RichText/ExcelRichText.cs +++ b/src/EPPlus/Style/RichText/ExcelRichText.cs @@ -593,9 +593,9 @@ public bool HasDefaultValue /// Export to OpenTypeFormat /// /// - public IRichTextFormatBase ExportToOpenTypeFormat() + public IRichTextFormatEssential ExportToOpenTypeFormat() { - var rtBase = new OpenTypeRichTextBase(Text, _collection._cells.Style.Font.Name, Size, Bold, Italic); + var rtBase = new RichTextFormatBase(Text, _collection._cells.Style.Font.Name, Size, Bold, Italic); return rtBase; } } diff --git a/src/EPPlusTest/Drawing/DrawingTest.cs b/src/EPPlusTest/Drawing/DrawingTest.cs index 9487d5c6de..7e9d17f85e 100644 --- a/src/EPPlusTest/Drawing/DrawingTest.cs +++ b/src/EPPlusTest/Drawing/DrawingTest.cs @@ -1254,6 +1254,90 @@ public void RichTextValidation() Assert.AreEqual("Rad", d.TextBody.Paragraphs[0].TextRuns[0].Text); Assert.AreEqual(" 1", d.TextBody.Paragraphs[0].TextRuns[1].Text); } - } + } + + [TestMethod] + public void ChangeUnderlineShapeTextRun() + { + string outputFileName = "UnderlineChange_SuperAndSubscript.xlsx"; + using (var p = OpenTemplatePackage("SuperAndSubScript.xlsx")) + { + var ws = p.Workbook.Worksheets[0]; + + var currShape = ws.Drawings[0]; + + var paragraph = currShape.As.Shape.TextBody.Paragraphs[0]; + paragraph.TextRuns[2].UnderLineColor = Color.Red; + + var file = GetOutputFile("", outputFileName); + p.SaveAs(file); + } + + using(var p = OpenPackage(outputFileName)) + { + var ws = p.Workbook.Worksheets[0]; + + var currShape = ws.Drawings[0]; + + var paragraph = currShape.As.Shape.TextBody.Paragraphs[0]; + Assert.AreEqual(Color.Red.ToArgb(), paragraph.TextRuns[2].UnderLineColor.ToArgb()); + } + } + + [TestMethod] + public void GenerateAndChangeUnderlineShapeTextRunEpplus() + { + string outputFileName = "UnderlineChange_Generated.xlsx"; + + //Generate file + using (var p = OpenPackage("UnderlineChange_Shape.xlsx",true)) + { + var ws = p.Workbook.Worksheets.Add("ws1"); + + var box = ws.Drawings.AddTextbox("boxy", "Boxy is kind"); + + box.Fill.Color = Color.DarkKhaki; + + box.TextBody.Paragraphs[0].TextRuns[0].FontUnderLine = eUnderLineType.Heavy; + box.TextBody.Paragraphs[0].TextRuns[0].UnderLineColor = Color.DarkRed; + + var file = GetOutputFile("", outputFileName); + + p.SaveAs(file); + + var file2 = GetOutputFile("", "UnderlineChange_Shape.xlsx"); + p.SaveAs(file2); + } + + //Open file, verify color, save with new color + using (var p = OpenPackage(outputFileName)) + { + var ws = p.Workbook.Worksheets[0]; + + var currShape = ws.Drawings[0]; + + var paragraph = currShape.As.Shape.TextBody.Paragraphs[0]; + var textRun = paragraph.TextRuns[0]; + Assert.AreEqual(Color.DarkRed.ToArgb(), textRun.UnderLineColor.ToArgb()); + + textRun.UnderLineColor = Color.Chartreuse; + + SaveAndCleanup(p); + } + + //Open file, verify color + using (var p = OpenPackage(outputFileName)) + { + var ws = p.Workbook.Worksheets[0]; + + var currShape = ws.Drawings[0]; + + var paragraph = currShape.As.Shape.TextBody.Paragraphs[0]; + var textRun = paragraph.TextRuns[0]; + Assert.AreEqual(Color.Chartreuse.ToArgb(), textRun.UnderLineColor.ToArgb()); + + SaveAndCleanup(p); + } + } } }