Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6c9144b
QoL update to rect + grandchild test
OssianEPPlus Dec 8, 2025
d15b98a
Moved classes, internal visible solved projrefs
OssianEPPlus Dec 8, 2025
f3d0c62
Added potential version of htmlwriter for svg
OssianEPPlus Dec 9, 2025
8e51ba4
Non-functional but promising start
OssianEPPlus Dec 9, 2025
01d72bc
fixed bugs + moved render function
OssianEPPlus Dec 10, 2025
b9e748f
Merge branch 'develop9' into refactor/svg-writer
OssianEPPlus Dec 10, 2025
1d97267
fixed merge conflict
OssianEPPlus Dec 10, 2025
07fafa5
Fixed textbox position bug
OssianEPPlus Dec 10, 2025
106061d
Start of renderitem refactor
OssianEPPlus Dec 10, 2025
338781b
Broken out text-wrapping and runs
OssianEPPlus Dec 11, 2025
2a69bbd
start of individual glyph measures
OssianEPPlus Dec 11, 2025
9cfbcd1
Started work on new textbody. Made Vector2 struct again
OssianEPPlus Dec 15, 2025
736839d
Fixed and tested generic textbody
OssianEPPlus Dec 15, 2025
97c2b12
Refactored/added private wrapText + bugfixes
OssianEPPlus Dec 15, 2025
f6b5f73
Fixed Opentype strong-name. fixes .net481 tests
OssianEPPlus Dec 16, 2025
34207bf
Simplified multipleTextFragments + fixed bug
OssianEPPlus Dec 16, 2025
ff011d6
Simplified wrapping considerably
OssianEPPlus Dec 16, 2025
f45399e
Almost finished wrapper refactor
OssianEPPlus Dec 16, 2025
536fb25
Created organizing classes
OssianEPPlus Dec 17, 2025
a39981f
Fourth iteration of wrapper
OssianEPPlus Dec 17, 2025
5b6a290
Fifth iteration of wrapper. Wordier but clearer
OssianEPPlus Dec 17, 2025
4176b89
Attempt at adding easier line oversight
OssianEPPlus Dec 17, 2025
65f6f2f
Solved basic bugs
OssianEPPlus Dec 17, 2025
a0455b3
Fixed converter bug
OssianEPPlus Dec 17, 2025
3e6eb2a
Resolved bugs, fixed tests
OssianEPPlus Dec 18, 2025
8bb52cb
Pressed code-lines gently
OssianEPPlus Dec 18, 2025
99d1e12
Cleanup and unification
OssianEPPlus Dec 18, 2025
62980ab
Cleaned up unnecesary comments for measureText Y
OssianEPPlus Dec 18, 2025
6a5fbe0
Merge branch 'develop9' into refactor/new-svg-writer
OssianEPPlus Dec 18, 2025
8c0ac43
Broke out 'rect' changes as new class boundingBox
OssianEPPlus Dec 18, 2025
f26ecc0
Draft of Restructured render base-classes
OssianEPPlus Dec 19, 2025
b8ce6b0
Updated multiple calls to avoid build errors
OssianEPPlus Dec 19, 2025
c486cb0
Merge branch 'develop9' into refactor/new-svg-writer
OssianEPPlus Dec 19, 2025
4d0514f
Fixed strange merge conflicts
OssianEPPlus Dec 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/EPPlus.Export.ImageRenderer.Test/TestTextContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ public void TestDynamicSizeSingleLine()
var expectedHeight = 18d;

var mf = new MeasurementFont() { FontFamily = "Aptos", Size = 11, Style = MeasurementFontStyles.Regular };

var container = new TextContainer(content, mf, true);

Assert.AreEqual(expectedWidth, Math.Round(container.Width,0));
Assert.AreEqual(expectedHeight, Math.Round(container.Height,0));
Assert.AreEqual(expectedWidth, Math.Round(container.Width, 0));
Assert.AreEqual(expectedHeight, Math.Round(container.Height, 0));
}

[TestMethod]
Expand Down Expand Up @@ -111,17 +111,17 @@ public void EnsureTextBodyAddsRunsCorrectly()
shapeRect.Height = 10;

FontMeasurerTrueType measurer = new FontMeasurerTrueType(12, "Aptos Narrow", FontSubFamily.Regular);
var body = new TextBody(measurer, shapeRect, true);
var body = new TextBody(shapeRect);

body.transform.Name = "TxtBody";
body.Bounds.transform.Name = "TxtBody";

body.AddText("A new Paragraph");
body.AddText("Second paragraph");
body.AddText("A new Paragraph", measurer);
body.AddText("Second paragraph", measurer);

Assert.AreEqual(2, body.transform.ChildObjects.Count);
Assert.AreEqual(body.transform.ChildObjects[0], body.Runs[0].transform);
Assert.AreEqual(2, body.Bounds.transform.ChildObjects.Count);
Assert.AreEqual(body.Bounds.transform.ChildObjects[0], body.Paragraphs[0].Bounds.transform);

Assert.AreEqual(shapeRect.transform, body.transform.Parent);
Assert.AreEqual(shapeRect.transform, body.Bounds.transform.Parent);
}

//[TestMethod]
Expand Down
12 changes: 9 additions & 3 deletions src/EPPlus.Export.ImageRenderer/DrawingBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,32 @@ Date Author Change

using EPPlus.Export.ImageRenderer;
using EPPlusImageRenderer.RenderItems;
using OfficeOpenXml;
using OfficeOpenXml.Drawing;
using OfficeOpenXml.Drawing.Theme;
using OfficeOpenXml.Interfaces.Drawing.Text;
using System.Collections.Generic;
using System.Text;

namespace EPPlusImageRenderer
{
internal abstract class DrawingBase
internal abstract class DrawingBase : RenderItem
{
internal DrawingBase(ExcelDrawing drawing)
protected ExcelWorkbook _wb;

internal DrawingBase(ExcelDrawing drawing) : base(drawing)
{
drawing.GetSizeInPixels(out int width, out int height);
Drawing = drawing;
Size = new DrawingSize(width, height);
TextMeasurer = drawing._drawings._package.Settings.TextSettings.PrimaryTextMeasurer;

_wb = drawing._drawings.Worksheet.Workbook;
_theme = _wb.ThemeManager.GetOrCreateTheme();
}
public ExcelDrawing Drawing { get; }
internal ITextMeasurer TextMeasurer { get; }
public List<RenderItem> RenderItems { get; } = new List<RenderItem>();
public DrawingSize Size { get; internal set; }
public abstract void Render(StringBuilder sb);
}
}
7 changes: 6 additions & 1 deletion src/EPPlus.Export.ImageRenderer/DrawingShape.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,23 @@ Date Author Change
27/11/2025 EPPlus Software AB EPPlus 9
*************************************************************************************************/

using EPPlus.Export.ImageRenderer.RenderItems.Shared;
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
internal abstract class DrawingShape : DrawingBaseItem
{
protected ExcelShape _shape;
protected DrawingShape(ExcelShape shape) : base(shape)
{
var style = shape.Style;

_shape = shape;
}
protected static void AddCmd(SvgRenderPathItem pi, DrawingPath path, List<double> coordinates, ref PathCommands cmd, PathsBase pp, PathsBase p, PathCommandType commandType)
Expand Down
56 changes: 35 additions & 21 deletions src/EPPlus.Export.ImageRenderer/ImageRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,34 @@ Date Author Change

using EPPlus.Export.ImageRenderer;
using EPPlus.Export.ImageRenderer.RenderItems.Shared;
using EPPlus.Export.ImageRenderer.RenderItems.Shared;
using EPPlus.Export.ImageRenderer.Svg;
using EPPlus.Export.ImageRenderer.Svg.NodeAttributes;
using EPPlus.Export.ImageRenderer.Svg.Writer;
using EPPlus.Export.ImageRenderer.Text;
using EPPlus.Fonts.OpenType;
using EPPlus.Graphics;
using EPPlus.Export.ImageRenderer.Svg.NodeAttributes;
using EPPlus.Export.ImageRenderer.Svg.Writer;
using EPPlus.Export.ImageRenderer.Text;
using EPPlus.Fonts.OpenType;
using EPPlus.Graphics;
using EPPlusImageRenderer.Svg;
using OfficeOpenXml;
using OfficeOpenXml.Drawing;
using OfficeOpenXml.Drawing.Chart;
using OfficeOpenXml.Utils;
using OfficeOpenXml.Utils;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Xml.Linq;
using System.Xml.Linq;

namespace EPPlusImageRenderer
{
Expand Down Expand Up @@ -77,6 +88,7 @@ public string RenderRangeToSvg(ExcelRange range)

var sb = new StringBuilder();
sb.Append($"<svg width=\"{500}\" height=\"{500}\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xml:space=\"preserve\" Overflow=\"Hidden\">");
sb.Append($"<svg width=\"{500}\" height=\"{500}\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xml:space=\"preserve\" Overflow=\"Hidden\">");
sRange.Render(sb);
sb.Append($"</svg>");
return sb.ToString();
Expand Down Expand Up @@ -118,24 +130,26 @@ public string RenderTextBody(string txtBody)
shapeRect.Height = 200;

FontMeasurerTrueType measurer = new FontMeasurerTrueType(11, "Aptos Narrow", FontSubFamily.Regular);
var body = new TextBody(measurer, shapeRect, true);
var body = new TextBody(shapeRect);

body.Bounds.transform.Name = "TxtBody";

body.transform.Name = "TxtBody";
body.Bounds.X = 40;
body.Bounds.Y = 40;

body.X = 40;
body.Y = 40;
body.AddText(txtBody, measurer);

body.AddText(txtBody);
var para1 = body.Paragraphs[0];

body.Runs[0].X = 20;
body.Runs[0].Y = 20;
para1.Runs[0].X = 20;
para1.Runs[0].Y = 20;

body.AddText("Extra Text");
body.Runs[1].X = 30;
body.Runs[1].Y = 40;
para1.AddText("Extra Text", measurer);
para1.Runs[1].X = 30;
para1.Runs[1].Y = 40;

body.Width = 120;
body.Height = 100;
para1.Bounds.Width = 120;
para1.Bounds.Height = 100;

var svgBody = GenerateSvgTextBody(body, (int)shapeRect.Width, (int)shapeRect.Height);

Expand Down Expand Up @@ -173,14 +187,14 @@ internal SvgElement GenerateSvgTextBody(TextBody body, int width, int height)

body.AllowOverflow = true;

var svgDefs = GetDefinitions(body, out string nameId, body.AllowOverflow);
var svgDefs = GetDefinitions(body.Bounds, out string nameId, body.AllowOverflow);

var fontSizePx = 16d;

doc.AddChildElement(svgDefs);
doc.AddChildElement(bg);

foreach (var run in body.Runs)
foreach (var run in body.Paragraphs[0].Runs)
{
var bbVisual = new SvgElement("rect");
bbVisual.AddAttribute("x", run.GlobalX);
Expand All @@ -194,20 +208,20 @@ internal SvgElement GenerateSvgTextBody(TextBody body, int width, int height)
}

var txBodyVisual = new SvgElement("rect");
txBodyVisual.AddAttribute("x", body.GlobalX);
txBodyVisual.AddAttribute("y", body.GlobalY);
txBodyVisual.AddAttribute("width", body.Width);
txBodyVisual.AddAttribute("height", body.Height);
txBodyVisual.AddAttribute("x", body.Bounds.GlobalX);
txBodyVisual.AddAttribute("y", body.Bounds.GlobalY);
txBodyVisual.AddAttribute("width", body.Bounds.Width);
txBodyVisual.AddAttribute("height", body.Bounds.Height);
txBodyVisual.AddAttribute("fill", "green");
txBodyVisual.AddAttribute("opacity", "0.5");

doc.AddChildElement(txBodyVisual);

foreach (var run in body.Runs)
foreach (var run in body.Paragraphs)
{
var renderElement = new SvgElement("text");
renderElement.AddAttribute("x", run.GlobalX);
renderElement.AddAttribute("y", run.GlobalY + fontSizePx);
renderElement.AddAttribute("x", run.Bounds.GlobalX);
renderElement.AddAttribute("y", run.Bounds.GlobalY + fontSizePx);
renderElement.AddAttribute("font-size", $"{fontSizePx}px");
renderElement.AddAttribute("clip-path", $"url(#{nameId})");

Expand Down
61 changes: 60 additions & 1 deletion src/EPPlus.Export.ImageRenderer/RenderItems/RenderItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ 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;
Expand Down Expand Up @@ -64,6 +65,36 @@ protected void CloneBase(RenderItem item)
item.LineJoin = LineJoin;
item.FillColorSource = FillColorSource;
}

//internal virtual void SetDrawingPropertiesFill(ExcelDrawingFill fill)
//{
// switch (fill.Style)
// {
// case eFillStyle.NoFill:
// if (fill.IsEmpty)
// {
// FillColor = GetFillColor(fill, FillColorSource);
// }
// else
// {
// FillColor = "none";
// }
// break;
// case eFillStyle.SolidFill:
// FillColor = GetFillColor(fill, fill.SolidFill.Color, FillColorSource);
// break;
// case eFillStyle.GradientFill:
// GradientFill = new DrawGradientFill(_theme, fill.GradientFill);
// FillColor = null;
// break;
// case eFillStyle.PatternFill:
// PatternFill = fill.PatternFill;
// break;
// case eFillStyle.BlipFill:
// BlipFill = fill.BlipFill;
// break;
// }
//}
internal virtual void SetDrawingPropertiesFill(ExcelDrawingFill fill, ExcelDrawingColorManager color)
{
switch (fill.Style)
Expand Down Expand Up @@ -159,6 +190,32 @@ private double[] GetDashArray(ExcelDrawingBorder border)
return null;
}

//private string GetFillColor(ExcelDrawingFill fill, PathFillMode fillColorSource)
//{
// if (fillColorSource == PathFillMode.None)
// {
// return "none";
// }

// Color fc;

// if (fill == null || fill.Style == eFillStyle.NoFill)
// {
// fc = EPPlusColorConverter.GetThemeColor(_theme.ColorScheme.Accent1);
// }
// else if (fill.Style == eFillStyle.SolidFill)
// {
// fc = EPPlusColorConverter.GetThemeColor(_theme, fill.SolidFill.Color);
// }
// else
// {
// return string.Empty;
// }

// fc = ColorUtils.GetAdjustedColor(fillColorSource, fc);
// return "#" + fc.ToArgb().ToString("x8").Substring(2);
//}

private string GetFillColor(ExcelDrawingFillBasic fill, ExcelDrawingColorManager styleFillColor, PathFillMode fillColorSource)
{
if (fillColorSource == PathFillMode.None)
Expand Down Expand Up @@ -190,14 +247,16 @@ private string GetFillColor(ExcelDrawingFillBasic fill, ExcelDrawingColorManager
fc = ColorUtils.GetAdjustedColor(fillColorSource, fc);
return "#" + fc.ToArgb().ToString("x8").Substring(2);
}

internal BoundingBox Bounds = new BoundingBox();
internal abstract void GetBounds(out double il, out double it, out double ir, out double ib);
}
/// <summary>
/// Base class for any item rendered.
/// </summary>
internal abstract class RenderItemBase
{
public abstract SvgItemType Type { get; }
public abstract RenderItemType Type { get; }
public abstract void Render(StringBuilder sb);

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Date Author Change
*************************************************************************************************/
namespace EPPlusImageRenderer.RenderItems
{
internal enum SvgItemType
internal enum RenderItemType
{
Path = 0,
Rect = 1,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using EPPlusImageRenderer;
using EPPlusImageRenderer.RenderItems;
using OfficeOpenXml.Drawing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace EPPlus.Export.ImageRenderer.RenderItems.Shared
{
internal abstract class DrawingBaseItem : DrawingBase
{
public DrawingBaseItem(ExcelShapeBase drawing) : base(drawing)
{
ImportEpplusDrawing(drawing);
}

internal void ImportEpplusDrawing(ExcelShapeBase drawing)
{
Bounds.Left = drawing.GetPixelLeft();
Bounds.Top = drawing.GetPixelTop();
Bounds.Width = drawing.GetPixelWidth();
Bounds.Height = drawing.GetPixelHeight();

SetDrawingPropertiesFill(drawing.Fill, null);
SetDrawingPropertiesBorder(drawing.Border, null, drawing.Border != null);
}

public override RenderItemType Type => RenderItemType.Group;

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;
}
}
}
22 changes: 22 additions & 0 deletions src/EPPlus.Export.ImageRenderer/RenderItems/Shared/InsetTextbox.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using EPPlus.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EPPlus.Export.ImageRenderer.RenderItems.Shared
{
/// <summary>
/// Calculated textbox position from a shape
/// That contains the textbody
/// </summary>
internal class InsetTextbox: BoundingBox
{
TextBody textBody;

//internal InsetTextbox(double l, double t, double width, double height)
//{
// textBody = new TextBody(this);
//}
}
}
Loading