From de3a2c40b006d768316233730f5a0cfaf4be371d Mon Sep 17 00:00:00 2001 From: DomCR Date: Sun, 5 Jul 2026 16:25:39 +0200 Subject: [PATCH 01/13] Refactor SVG export: modular writers and config update Refactored SVG export by splitting `SvgXmlWriter` into `SvgDocumentBuilder`, `SvgEntityWriter`, `SvgStyleWriter`, and `CadXmlWriter` for better separation of concerns. Updated `SvgExporter` to use the new builder. Simplified `SvgConfiguration` and removed inheritance. Added `AssemblyInfo.cs` with strong-naming and test visibility. Cleaned up test project references. --- .../ACadSharp.Formats.Tests.csproj | 3 +- src/ACadSharp.Formats/AssemblyInfo.cs | 20 ++ src/ACadSharp.Formats/Svg/CadXmlWriter.cs | 50 +++++ src/ACadSharp.Formats/Svg/SvgConfiguration.cs | 6 +- .../Svg/SvgDocumentBuilder.cs | 197 ++++++++++++++++++ .../{SvgXmlWriter.cs => SvgEntityWriter.cs} | 150 +------------ src/ACadSharp.Formats/Svg/SvgExporter.cs | 25 +-- src/ACadSharp.Formats/Svg/SvgStyleWriter.cs | 37 ++++ 8 files changed, 329 insertions(+), 159 deletions(-) create mode 100644 src/ACadSharp.Formats/AssemblyInfo.cs create mode 100644 src/ACadSharp.Formats/Svg/CadXmlWriter.cs create mode 100644 src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs rename src/ACadSharp.Formats/Svg/{SvgXmlWriter.cs => SvgEntityWriter.cs} (84%) create mode 100644 src/ACadSharp.Formats/Svg/SvgStyleWriter.cs diff --git a/src/ACadSharp.Formats.Tests/ACadSharp.Formats.Tests.csproj b/src/ACadSharp.Formats.Tests/ACadSharp.Formats.Tests.csproj index 80092f4..68efad0 100644 --- a/src/ACadSharp.Formats.Tests/ACadSharp.Formats.Tests.csproj +++ b/src/ACadSharp.Formats.Tests/ACadSharp.Formats.Tests.csproj @@ -11,6 +11,7 @@ false true True + ../ACadSharp.Formats.snk @@ -35,6 +36,4 @@ - - diff --git a/src/ACadSharp.Formats/AssemblyInfo.cs b/src/ACadSharp.Formats/AssemblyInfo.cs new file mode 100644 index 0000000..3997456 --- /dev/null +++ b/src/ACadSharp.Formats/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// In SDK-style projects such as this one, several assembly attributes that were historically +// defined in this file are now automatically added during build and populated with +// values defined in project properties. For details of which attributes are included +// and how to customise this process see: https://aka.ms/assembly-info-properties + + +// Setting ComVisible to false makes the types in this assembly not visible to COM +// components. If you need to access a type in this assembly from COM, set the ComVisible +// attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM. + +[assembly: Guid("e9f16aaf-a8f5-4319-abf3-982ce8b7ed6b")] + +[assembly: InternalsVisibleTo("ACadSharp.Formats.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b148ab602997d7649ec11cf13981ab8cf9518f0ecdc7cde5f458efb8b94bdbb9c553bc6f2c65fb293bacb5661a374250f1792183b475b91c69eb26ed6eed3094caddf4957f8bfef2ac0ae127ff4e2134d9411b4cad6e83e1d2a0a3aec4c16771d8ad4b31d89099d2d4521b3fd05fb3e1bf1981045c7241e9cbbf65d6cd64fcbd")] \ No newline at end of file diff --git a/src/ACadSharp.Formats/Svg/CadXmlWriter.cs b/src/ACadSharp.Formats/Svg/CadXmlWriter.cs new file mode 100644 index 0000000..86331a1 --- /dev/null +++ b/src/ACadSharp.Formats/Svg/CadXmlWriter.cs @@ -0,0 +1,50 @@ +using ACadSharp.IO; +using ACadSharp.Types.Units; +using System; +using System.IO; +using System.Text; +using System.Xml; + +namespace ACadSharp.Formats.Svg; + +internal class CadXmlWriter : XmlTextWriter +{ + public event NotificationEventHandler OnNotification; + + public SvgConfiguration Configuration { get; } = new(); + + public UnitsType Units { get; } + + public CadXmlWriter(SvgConfiguration configuration, UnitsType units, Encoding encoding) + : this(new MemoryStream(), configuration, units, encoding) + { + } + + public CadXmlWriter(Stream stream, SvgConfiguration configuration, UnitsType units, Encoding encoding) + : base(stream, encoding) + { + this.Configuration = configuration; + this.Units = units; + this.Formatting = configuration.Formatting; + } + + public void WriteAttributeString(string localName, double value) + { + this.WriteAttributeString(localName, value, this.Units); + } + + public void WriteAttributeString(string localName, double value, UnitsType units) + { + this.WriteAttributeString(localName, value.ToSvg(units)); + } + + protected void notify(string message, NotificationType type, Exception ex = null) + { + this.triggerNotification(this, new NotificationEventArgs(message, type, ex)); + } + + protected void triggerNotification(object sender, NotificationEventArgs e) + { + this.OnNotification?.Invoke(sender, e); + } +} diff --git a/src/ACadSharp.Formats/Svg/SvgConfiguration.cs b/src/ACadSharp.Formats/Svg/SvgConfiguration.cs index 3191129..71fb9d8 100644 --- a/src/ACadSharp.Formats/Svg/SvgConfiguration.cs +++ b/src/ACadSharp.Formats/Svg/SvgConfiguration.cs @@ -1,15 +1,17 @@ using ACadSharp.Extensions; -using ACadSharp.IO; using ACadSharp.Types.Units; using System; +using System.Xml; namespace ACadSharp.Formats.Svg; /// /// Configuration for writing SVG files. /// -public class SvgConfiguration : CadWriterConfiguration +public class SvgConfiguration { + public Formatting Formatting { get; set; } = Formatting.Indented; + /// /// The will be divided by this value to process the stroke-width in the svg when the units are . /// diff --git a/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs b/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs new file mode 100644 index 0000000..c23bd10 --- /dev/null +++ b/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs @@ -0,0 +1,197 @@ +using ACadSharp.Entities; +using ACadSharp.Extensions; +using ACadSharp.IO; +using ACadSharp.Objects; +using ACadSharp.Tables; +using ACadSharp.Types.Units; +using CSMath; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace ACadSharp.Formats.Svg; + +internal class SvgDocumentBuilder +{ + public event NotificationEventHandler OnNotification; + + public SvgConfiguration Configuration { get; } = new(); + + public UnitsType Units { get; private set; } + + private readonly Encoding _encoding; + + private readonly Stream _stream; + + private SvgEntityWriter _entitiesWriter; + + private CadXmlWriter _mainWriter; + + private SvgStyleWriter _styleWriter; + + public SvgDocumentBuilder(Stream stream, SvgConfiguration configuration) : this(stream, configuration, null) + { + } + + public SvgDocumentBuilder(Stream stream, SvgConfiguration configuration, Encoding encoding) + { + this._stream = stream; + this.Configuration = configuration; + this._encoding = encoding; + } + + public void WriteBlock(BlockRecord record) + { + this.Units = record.Units; + + BoundingBox box = record.GetBoundingBox(); + + this.startDocument(box, box, this.Units); + + this.processEntities(record.Entities); + + this.endDocument(); + } + + public void WriteLayout(Layout layout) + { + this.Units = layout.PaperUnits.ToUnits(); + + double paperWidth = layout.PaperWidth; + double paperHeight = layout.PaperHeight; + + switch (layout.PaperRotation) + { + case PlotRotation.Degrees90: + case PlotRotation.Degrees270: + paperWidth = layout.PaperHeight; + paperHeight = layout.PaperWidth; + break; + } + + XYZ lowerCorner = XYZ.Zero; + XYZ upperCorner = new XYZ(paperWidth, paperHeight, 0.0); + BoundingBox paper = new BoundingBox(lowerCorner, upperCorner); + + XYZ lowerMargin = layout.UnprintableMargin.BottomLeftCorner.Convert(); + XYZ upperMargin = upperCorner - layout.UnprintableMargin.TopCorner.Convert(); + BoundingBox margins = new BoundingBox( + lowerMargin, + upperMargin); + + + Transform transform = new Transform( + lowerMargin.ToPixelSize(UnitsType.Millimeters), + new XYZ(layout.PrintScale), + XYZ.Zero); + + this.startDocument(paper, null, UnitsType.Millimeters, true); + + processEntities(layout.AssociatedBlock.Entities, transform); + + this.endDocument(); + } + + protected void notify(string message, NotificationType type, Exception ex = null) + { + this.triggerNotification(this, new NotificationEventArgs(message, type, ex)); + } + + protected void triggerNotification(object sender, NotificationEventArgs e) + { + this.OnNotification?.Invoke(sender, e); + } + + private void endDocument() + { + this.mergeStream(this._styleWriter); + + this.mergeStream(this._entitiesWriter); + + this._mainWriter.WriteEndElement(); + this._mainWriter.WriteEndDocument(); + this._mainWriter.Close(); + } + + private void initWriters(bool isPaperSpace) + { + this._mainWriter = new CadXmlWriter(this._stream, this.Configuration, this.Units, this._encoding); + this._mainWriter.OnNotification += this.triggerNotification; + + this._entitiesWriter = new SvgEntityWriter(this.Configuration, this.Units, this._encoding) + { + IsPaperSpace = isPaperSpace + }; + this._entitiesWriter.OnNotification += this.triggerNotification; + + this._styleWriter = new SvgStyleWriter(this.Configuration, this.Units, this._encoding); + this._styleWriter.OnNotification += this.triggerNotification; + } + + private void mergeStream(CadXmlWriter writer) + { + writer.Flush(); + writer.BaseStream.Position = 0; + using (StreamReader sr = new StreamReader(writer.BaseStream)) + { + this._mainWriter.WriteRaw(sr.ReadToEnd()); + this._mainWriter.WriteRaw(Environment.NewLine); + } + } + + private void processEntities(IEnumerable entities) + { + this.processEntities(entities, new Transform()); + } + + private void processEntities(IEnumerable entities, Transform transform) + { + foreach (var entity in entities) + { + this.processEntity(entity, transform); + } + } + + private void processEntity(Entity entity, Transform transform) + { + this._styleWriter.AddLineType(entity.LineType); + this._styleWriter.AddLayer(entity.Layer); + + this._entitiesWriter.WriteEntity(entity, transform); + } + + private void startDocument(BoundingBox box, BoundingBox? viewBox, UnitsType units, bool isPaperSpace = false) + { + this.initWriters(isPaperSpace); + + this._mainWriter.WriteStartDocument(); + + this._mainWriter.WriteStartElement("svg"); + this._mainWriter.WriteAttributeString("xmlns", "http://www.w3.org/2000/svg"); + + this._mainWriter.WriteAttributeString("width", box.Max.X - box.Min.X, units); + this._mainWriter.WriteAttributeString("height", box.Max.Y - box.Min.Y, units); + + if (viewBox.HasValue) + { + var vb = viewBox.Value; + this._mainWriter.WriteStartAttribute("viewBox"); + this._mainWriter.WriteValue(vb.Min.X.ToPixelSize(units)); + this._mainWriter.WriteValue(" "); + this._mainWriter.WriteValue(vb.Min.Y.ToPixelSize(units)); + this._mainWriter.WriteValue(" "); + this._mainWriter.WriteValue(vb.LengthX.ToPixelSize(units)); + this._mainWriter.WriteValue(" "); + this._mainWriter.WriteValue(vb.LengthY.ToPixelSize(units)); + this._mainWriter.WriteEndAttribute(); + } + + this._mainWriter.WriteAttributeString("transform", $"scale(1,-1)"); + + if (isPaperSpace) + { + this._mainWriter.WriteAttributeString("style", "background-color:white"); + } + } +} \ No newline at end of file diff --git a/src/ACadSharp.Formats/Svg/SvgXmlWriter.cs b/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs similarity index 84% rename from src/ACadSharp.Formats/Svg/SvgXmlWriter.cs rename to src/ACadSharp.Formats/Svg/SvgEntityWriter.cs index 3902d57..8adaa3e 100644 --- a/src/ACadSharp.Formats/Svg/SvgXmlWriter.cs +++ b/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs @@ -1,7 +1,6 @@ using ACadSharp.Entities; using ACadSharp.Extensions; using ACadSharp.IO; -using ACadSharp.Objects; using ACadSharp.Tables; using ACadSharp.Types.Units; using CSMath; @@ -11,108 +10,18 @@ using System.IO; using System.Linq; using System.Text; -using System.Xml; namespace ACadSharp.Formats.Svg; -internal class SvgXmlWriter : XmlTextWriter +internal class SvgEntityWriter : CadXmlWriter { - public event NotificationEventHandler OnNotification; + public bool IsPaperSpace { get; set; } = false; - public SvgConfiguration Configuration { get; } = new(); - - public Layout Layout { get; set; } - - public UnitsType Units { get; protected set; } - - public SvgXmlWriter(Stream stream, SvgConfiguration configuration) : this(stream, configuration, null) - { - } - - public SvgXmlWriter(Stream stream, SvgConfiguration configuration, Encoding? encoding) : base(stream, encoding) - { - this.Configuration = configuration; - } - - public void WriteAttributeString(string localName, double value) - { - this.WriteAttributeString(localName, value, this.Units); - } - - public void WriteAttributeString(string localName, double value, UnitsType units) - { - this.WriteAttributeString(localName, value.ToSvg(units)); - } - - public void WriteBlock(BlockRecord record) + public SvgEntityWriter(SvgConfiguration configuration, UnitsType units, Encoding encoding) : base(configuration, units, encoding) { - this.Units = record.Units; - - BoundingBox box = record.GetBoundingBox(); - - this.startDocument(box, box, this.Units); - - foreach (var e in record.Entities) - { - this.writeEntity(e); - } - - this.endDocument(); } - public void WriteLayout(Layout layout) - { - this.Layout = layout; - this.Units = layout.PaperUnits.ToUnits(); - - double paperWidth = layout.PaperWidth; - double paperHeight = layout.PaperHeight; - - switch (layout.PaperRotation) - { - case PlotRotation.Degrees90: - case PlotRotation.Degrees270: - paperWidth = layout.PaperHeight; - paperHeight = layout.PaperWidth; - break; - } - - XYZ lowerCorner = XYZ.Zero; - XYZ upperCorner = new XYZ(paperWidth, paperHeight, 0.0); - BoundingBox paper = new BoundingBox(lowerCorner, upperCorner); - - XYZ lowerMargin = layout.UnprintableMargin.BottomLeftCorner.Convert(); - XYZ upperMargin = upperCorner - layout.UnprintableMargin.TopCorner.Convert(); - BoundingBox margins = new BoundingBox( - lowerMargin, - upperMargin); - - this.startDocument(paper, null, UnitsType.Millimeters); - - Transform transform = new Transform( - lowerMargin.ToPixelSize(UnitsType.Millimeters), - new XYZ(layout.PrintScale), - XYZ.Zero); - - foreach (var e in layout.AssociatedBlock.Entities) - { - this.writeEntity(e, transform); - } - - this.endDocument(); - } - - protected void notify(string message, NotificationType type, Exception ex = null) - { - this.OnNotification?.Invoke(this, new NotificationEventArgs(message, type, ex)); - } - - protected void triggerNotification(object sender, NotificationEventArgs e) - { - this.OnNotification?.Invoke(sender, e); - } - - protected void writeEntity(Entity entity, Transform transform) + public void WriteEntity(Entity entity, Transform transform) { this.WriteComment($"{entity.ObjectName} | {entity.Handle}"); @@ -160,9 +69,9 @@ protected void writeEntity(Entity entity, Transform transform) } } - private string colorSvg(Color color) + protected string colorSvg(Color color) { - if (this.Layout != null && color.Equals(Color.Default)) + if (this.IsPaperSpace && color.Equals(Color.Default)) { color = Color.Black; } @@ -204,50 +113,11 @@ private bool drawableLineType(LineType lineType) return lineType.IsComplex && !lineType.HasShapes; } - private void endDocument() - { - this.WriteEndElement(); - this.WriteEndDocument(); - this.Close(); - } - private double getPointSize(IEntity entity) { return entity.GetActiveLineWeightType().GetLineWeightValue().ToPixelSize(this.Units); } - private void startDocument(BoundingBox box, BoundingBox? viewBox, UnitsType units) - { - this.WriteStartDocument(); - - this.WriteStartElement("svg"); - this.WriteAttributeString("xmlns", "http://www.w3.org/2000/svg"); - - this.WriteAttributeString("width", box.Max.X - box.Min.X, units); - this.WriteAttributeString("height", box.Max.Y - box.Min.Y, units); - - if (viewBox.HasValue) - { - var vb = viewBox.Value; - this.WriteStartAttribute("viewBox"); - this.WriteValue(vb.Min.X.ToPixelSize(units)); - this.WriteValue(" "); - this.WriteValue(vb.Min.Y.ToPixelSize(units)); - this.WriteValue(" "); - this.WriteValue((vb.LengthX).ToPixelSize(units)); - this.WriteValue(" "); - this.WriteValue((vb.LengthY).ToPixelSize(units)); - this.WriteEndAttribute(); - } - - this.WriteAttributeString("transform", $"scale(1,-1)"); - - if (this.Layout != null) - { - this.WriteAttributeString("style", "background-color:white"); - } - } - private string svgPoints(IEnumerable points, Transform transform) where T : IVector, new() { @@ -349,7 +219,7 @@ private void writeDimension(Dimension dimension, Transform transform) foreach (Entity e in dimension.Block.Entities) { - this.writeEntity(e, transform); + this.WriteEntity(e, transform); } this.WriteEndElement(); @@ -411,9 +281,9 @@ private void writeEllipse(Ellipse ellipse, Transform transform) } } - private void writeEntity(Entity entity) + public void WriteEntity(Entity entity) { - this.writeEntity(entity, new Transform()); + this.WriteEntity(entity, new Transform()); } private void writeEntityAsPath(Entity entity, Transform transform, params IEnumerable points) @@ -495,7 +365,7 @@ private void writeInsert(Insert insert, Transform transform) foreach (var e in insert.Block.Entities) { - this.writeEntity(e); + this.WriteEntity(e); } this.WriteEndElement(); diff --git a/src/ACadSharp.Formats/Svg/SvgExporter.cs b/src/ACadSharp.Formats/Svg/SvgExporter.cs index 5e2469d..02e1b90 100644 --- a/src/ACadSharp.Formats/Svg/SvgExporter.cs +++ b/src/ACadSharp.Formats/Svg/SvgExporter.cs @@ -9,7 +9,7 @@ public class SvgExporter : ExporterBase { public SvgConfiguration Configuration { get; set; } = new SvgConfiguration(); - private SvgXmlWriter _writer; + private SvgDocumentBuilder _builder; public SvgExporter(string filename) : this(File.Create(filename)) @@ -23,33 +23,28 @@ public SvgExporter(Stream stream) public override void Export(Layout layout) { - this.createWriter(); + this.createBuilder(); - this._writer.WriteLayout(layout); + this._builder.WriteLayout(layout); } public override void Export(BlockRecord record) { - this.createWriter(); + this.createBuilder(); - this._writer.WriteBlock(record); + this._builder.WriteBlock(record); } public override void Export(CadDocument document) { - this.createWriter(); + this.createBuilder(); - this._writer.WriteBlock(document.ModelSpace); + this._builder.WriteBlock(document.ModelSpace); } - private void createWriter() + private void createBuilder() { - StreamWriter textWriter = new StreamWriter(this._stream); - this._writer = new SvgXmlWriter(this._stream, this.Configuration) - { - Formatting = Formatting.Indented - }; - - this._writer.OnNotification += this.triggerNotification; + this._builder = new SvgDocumentBuilder(this._stream, this.Configuration); + this._builder.OnNotification += this.triggerNotification; } } \ No newline at end of file diff --git a/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs b/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs new file mode 100644 index 0000000..4bcc729 --- /dev/null +++ b/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs @@ -0,0 +1,37 @@ +using ACadSharp.Tables; +using ACadSharp.Types.Units; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace ACadSharp.Formats.Svg; + +internal class SvgStyleWriter : CadXmlWriter +{ + private readonly Dictionary _layers = new(); + + private readonly Dictionary _lineTypes = new(); + + public SvgStyleWriter(SvgConfiguration configuration, UnitsType units, Encoding encoding) : base(configuration, units, encoding) + { + } + + public void AddLayer(Layer layer) + { + if (layer == null || this._layers.ContainsKey(layer.Name)) + { + return; + } + this._layers.Add(layer.Name, layer); + } + + public void AddLineType(LineType lineType) + { + if (lineType == null || this._lineTypes.ContainsKey(lineType.Name)) + { + return; + } + + this._lineTypes.Add(lineType.Name, lineType); + } +} \ No newline at end of file From fbfe1bc2ff6315b7470c0e98c3f7828f7e394b05 Mon Sep 17 00:00:00 2001 From: DomCR Date: Mon, 6 Jul 2026 08:12:59 +0200 Subject: [PATCH 02/13] Refactor and enhance SVG export and styling Refactored SVG export pipeline for improved structure and maintainability. Moved color handling to CadXmlWriter and centralized PaperSpace logic. Updated SvgDocumentBuilder to use XDocument for merging and wrap entities in . SvgStyleWriter now outputs CSS styles for layers and entities. Entities include id and class attributes for better SVG styling and identification. Cleaned up redundant code and improved attribute handling. --- src/ACadSharp.Formats/Svg/CadXmlWriter.cs | 22 ++++++++++- .../Svg/SvgDocumentBuilder.cs | 26 ++++++++++--- src/ACadSharp.Formats/Svg/SvgEntityWriter.cs | 25 +++++------- src/ACadSharp.Formats/Svg/SvgStyleWriter.cs | 39 ++++++++++++++++++- 4 files changed, 88 insertions(+), 24 deletions(-) diff --git a/src/ACadSharp.Formats/Svg/CadXmlWriter.cs b/src/ACadSharp.Formats/Svg/CadXmlWriter.cs index 86331a1..8309bcf 100644 --- a/src/ACadSharp.Formats/Svg/CadXmlWriter.cs +++ b/src/ACadSharp.Formats/Svg/CadXmlWriter.cs @@ -1,7 +1,15 @@ -using ACadSharp.IO; +using ACadSharp.Entities; +using ACadSharp.Extensions; +using ACadSharp.Formats.Svg; +using ACadSharp.IO; +using ACadSharp.Tables; using ACadSharp.Types.Units; +using CSMath; using System; +using System.Collections.Generic; +using System.Globalization; using System.IO; +using System.Linq; using System.Text; using System.Xml; @@ -15,6 +23,8 @@ internal class CadXmlWriter : XmlTextWriter public UnitsType Units { get; } + public bool IsPaperSpace { get; set; } = false; + public CadXmlWriter(SvgConfiguration configuration, UnitsType units, Encoding encoding) : this(new MemoryStream(), configuration, units, encoding) { @@ -47,4 +57,14 @@ protected void triggerNotification(object sender, NotificationEventArgs e) { this.OnNotification?.Invoke(sender, e); } + + protected string colorSvg(Color color) + { + if (this.IsPaperSpace && color.Equals(Color.Default)) + { + color = Color.Black; + } + + return $"rgb({color.R},{color.G},{color.B})"; + } } diff --git a/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs b/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs index c23bd10..37610e3 100644 --- a/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs +++ b/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs @@ -9,6 +9,8 @@ using System.Collections.Generic; using System.IO; using System.Text; +using System.Xml; +using System.Xml.Linq; namespace ACadSharp.Formats.Svg; @@ -105,8 +107,9 @@ protected void triggerNotification(object sender, NotificationEventArgs e) private void endDocument() { - this.mergeStream(this._styleWriter); + this._styleWriter.WriteStyles(); + this.mergeStream(this._styleWriter); this.mergeStream(this._entitiesWriter); this._mainWriter.WriteEndElement(); @@ -125,7 +128,10 @@ private void initWriters(bool isPaperSpace) }; this._entitiesWriter.OnNotification += this.triggerNotification; - this._styleWriter = new SvgStyleWriter(this.Configuration, this.Units, this._encoding); + this._styleWriter = new SvgStyleWriter(this.Configuration, this.Units, this._encoding) + { + IsPaperSpace = isPaperSpace + }; this._styleWriter.OnNotification += this.triggerNotification; } @@ -133,10 +139,16 @@ private void mergeStream(CadXmlWriter writer) { writer.Flush(); writer.BaseStream.Position = 0; - using (StreamReader sr = new StreamReader(writer.BaseStream)) + + XDocument temp = XDocument.Load(writer.BaseStream, LoadOptions.None); + if (temp.Root is null) { - this._mainWriter.WriteRaw(sr.ReadToEnd()); - this._mainWriter.WriteRaw(Environment.NewLine); + return; + } + + foreach (XNode node in temp.Root.Nodes()) + { + node.WriteTo(this._mainWriter); } } @@ -147,10 +159,14 @@ private void processEntities(IEnumerable entities) private void processEntities(IEnumerable entities, Transform transform) { + this._entitiesWriter.WriteStartElement("svg", "http://www.w3.org/2000/svg"); + foreach (var entity in entities) { this.processEntity(entity, transform); } + + this._entitiesWriter.WriteEndElement(); } private void processEntity(Entity entity, Transform transform) diff --git a/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs b/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs index 8adaa3e..014211a 100644 --- a/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs +++ b/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs @@ -15,16 +15,13 @@ namespace ACadSharp.Formats.Svg; internal class SvgEntityWriter : CadXmlWriter { - public bool IsPaperSpace { get; set; } = false; - - public SvgEntityWriter(SvgConfiguration configuration, UnitsType units, Encoding encoding) : base(configuration, units, encoding) + public SvgEntityWriter(SvgConfiguration configuration, UnitsType units, Encoding encoding) + : base(configuration, units, encoding) { } public void WriteEntity(Entity entity, Transform transform) { - this.WriteComment($"{entity.ObjectName} | {entity.Handle}"); - switch (entity) { case Arc arc: @@ -69,16 +66,6 @@ public void WriteEntity(Entity entity, Transform transform) } } - protected string colorSvg(Color color) - { - if (this.IsPaperSpace && color.Equals(Color.Default)) - { - color = Color.Black; - } - - return $"rgb({color.R},{color.G},{color.B})"; - } - private string createPath(params IEnumerable polylines) { StringBuilder sb = new StringBuilder(); @@ -306,7 +293,13 @@ private void writeEntityHeader(IEntity entity, Transform transform, bool drawStr { Color color = entity.GetActiveColor(); - this.WriteAttributeString("vector-effect", "non-scaling-stroke"); + this.WriteAttributeString("id", entity.Handle.ToString()); + + this.WriteStartAttribute("class"); + this.WriteValue("entity"); + this.WriteValue(" "); + this.WriteValue($"layer_{entity.Layer.Name}"); + this.WriteEndAttribute(); if (drawStroke) { diff --git a/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs b/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs index 4bcc729..90103aa 100644 --- a/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs +++ b/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs @@ -1,8 +1,9 @@ using ACadSharp.Tables; using ACadSharp.Types.Units; +using System; using System.Collections.Generic; -using System.IO; using System.Text; +using System.Xml; namespace ACadSharp.Formats.Svg; @@ -12,7 +13,8 @@ internal class SvgStyleWriter : CadXmlWriter private readonly Dictionary _lineTypes = new(); - public SvgStyleWriter(SvgConfiguration configuration, UnitsType units, Encoding encoding) : base(configuration, units, encoding) + public SvgStyleWriter(SvgConfiguration configuration, UnitsType units, Encoding encoding) + : base(configuration, units, encoding) { } @@ -34,4 +36,37 @@ public void AddLineType(LineType lineType) this._lineTypes.Add(lineType.Name, lineType); } + + public void WriteStyles() + { + this.WriteStartElement("svg", "http://www.w3.org/2000/svg"); + this.WriteStartElement("style"); + + this.WriteString(Environment.NewLine); + this.writeCssLine(".entity{"); + this.writeCssLine("vector-effect: non-scaling-stroke;"); + this.writeCssLine("}"); + this.WriteString(Environment.NewLine); + + foreach (var layer in this._layers.Values) + { + this.writeCssLine($".layer_{layer.Name}"); + this.writeCssLine("{"); + this.writeCssLine($"stroke: {this.colorSvg(layer.Color)};"); + this.writeCssLine($"stroke-width: {this.Configuration.GetLineWeightValue(layer.LineWeight, this.Units).ToSvg(UnitsType.Millimeters)}"); + this.writeCssLine("}"); + this.WriteString(Environment.NewLine); + } + + this.WriteEndElement(); + this.WriteEndElement(); + } + + private void writeCssLine(string line) + { + string indent = new string(this.IndentChar, this.Indentation); + this.WriteString(indent); + this.WriteString(line); + this.WriteString(Environment.NewLine); + } } \ No newline at end of file From ce891ef1731abece9005cd6e28d3f0f4e9cf6e3f Mon Sep 17 00:00:00 2001 From: DomCR Date: Mon, 6 Jul 2026 09:31:45 +0200 Subject: [PATCH 03/13] Refactor SVG writer classes for clarity and separation Refactored SVG writer classes to improve separation of concerns and code clarity. Moved color and line weight formatting to CadXmlWriter, updated SvgStyleWriter to delegate XML writing, and unified document writing in SvgDocumentBuilder. Cleaned up method names, removed unused usings, and improved CSS style generation. Updated all usages to match the new structure. --- src/ACadSharp.Formats/Svg/CadXmlWriter.cs | 41 +++++++------ .../Svg/SvgDocumentBuilder.cs | 55 ++++++++---------- src/ACadSharp.Formats/Svg/SvgEntityWriter.cs | 20 +++---- src/ACadSharp.Formats/Svg/SvgExporter.cs | 1 - src/ACadSharp.Formats/Svg/SvgStyleWriter.cs | 57 ++++++++++++------- 5 files changed, 91 insertions(+), 83 deletions(-) diff --git a/src/ACadSharp.Formats/Svg/CadXmlWriter.cs b/src/ACadSharp.Formats/Svg/CadXmlWriter.cs index 8309bcf..4e9c8ed 100644 --- a/src/ACadSharp.Formats/Svg/CadXmlWriter.cs +++ b/src/ACadSharp.Formats/Svg/CadXmlWriter.cs @@ -1,15 +1,7 @@ -using ACadSharp.Entities; -using ACadSharp.Extensions; -using ACadSharp.Formats.Svg; -using ACadSharp.IO; -using ACadSharp.Tables; +using ACadSharp.IO; using ACadSharp.Types.Units; -using CSMath; using System; -using System.Collections.Generic; -using System.Globalization; using System.IO; -using System.Linq; using System.Text; using System.Xml; @@ -21,10 +13,10 @@ internal class CadXmlWriter : XmlTextWriter public SvgConfiguration Configuration { get; } = new(); - public UnitsType Units { get; } - public bool IsPaperSpace { get; set; } = false; + public UnitsType Units { get; } + public CadXmlWriter(SvgConfiguration configuration, UnitsType units, Encoding encoding) : this(new MemoryStream(), configuration, units, encoding) { @@ -38,6 +30,16 @@ public CadXmlWriter(Stream stream, SvgConfiguration configuration, UnitsType uni this.Formatting = configuration.Formatting; } + public string ColorSvg(Color color) + { + if (this.IsPaperSpace && color.Equals(Color.Default)) + { + color = Color.Black; + } + + return $"rgb({color.R},{color.G},{color.B})"; + } + public void WriteAttributeString(string localName, double value) { this.WriteAttributeString(localName, value, this.Units); @@ -48,6 +50,11 @@ public void WriteAttributeString(string localName, double value, UnitsType units this.WriteAttributeString(localName, value.ToSvg(units)); } + public string WriteLineWeightValue(LineWeightType lineWeight) + { + return $"{this.Configuration.GetLineWeightValue(lineWeight, this.Units).ToSvg(UnitsType.Millimeters)}"; + } + protected void notify(string message, NotificationType type, Exception ex = null) { this.triggerNotification(this, new NotificationEventArgs(message, type, ex)); @@ -57,14 +64,4 @@ protected void triggerNotification(object sender, NotificationEventArgs e) { this.OnNotification?.Invoke(sender, e); } - - protected string colorSvg(Color color) - { - if (this.IsPaperSpace && color.Equals(Color.Default)) - { - color = Color.Black; - } - - return $"rgb({color.R},{color.G},{color.B})"; - } -} +} \ No newline at end of file diff --git a/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs b/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs index 37610e3..33415db 100644 --- a/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs +++ b/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.IO; using System.Text; -using System.Xml; using System.Xml.Linq; namespace ACadSharp.Formats.Svg; @@ -28,7 +27,7 @@ internal class SvgDocumentBuilder private SvgEntityWriter _entitiesWriter; - private CadXmlWriter _mainWriter; + private CadXmlWriter _xmlWriter; private SvgStyleWriter _styleWriter; @@ -109,18 +108,18 @@ private void endDocument() { this._styleWriter.WriteStyles(); - this.mergeStream(this._styleWriter); this.mergeStream(this._entitiesWriter); - this._mainWriter.WriteEndElement(); - this._mainWriter.WriteEndDocument(); - this._mainWriter.Close(); + this._xmlWriter.WriteEndElement(); + this._xmlWriter.WriteEndDocument(); + this._xmlWriter.Close(); } private void initWriters(bool isPaperSpace) { - this._mainWriter = new CadXmlWriter(this._stream, this.Configuration, this.Units, this._encoding); - this._mainWriter.OnNotification += this.triggerNotification; + this._xmlWriter = new CadXmlWriter(this._stream, this.Configuration, this.Units, this._encoding); + this._xmlWriter.IsPaperSpace = isPaperSpace; + this._xmlWriter.OnNotification += this.triggerNotification; this._entitiesWriter = new SvgEntityWriter(this.Configuration, this.Units, this._encoding) { @@ -128,11 +127,7 @@ private void initWriters(bool isPaperSpace) }; this._entitiesWriter.OnNotification += this.triggerNotification; - this._styleWriter = new SvgStyleWriter(this.Configuration, this.Units, this._encoding) - { - IsPaperSpace = isPaperSpace - }; - this._styleWriter.OnNotification += this.triggerNotification; + this._styleWriter = new SvgStyleWriter(this._xmlWriter); } private void mergeStream(CadXmlWriter writer) @@ -148,7 +143,7 @@ private void mergeStream(CadXmlWriter writer) foreach (XNode node in temp.Root.Nodes()) { - node.WriteTo(this._mainWriter); + node.WriteTo(this._xmlWriter); } } @@ -181,33 +176,33 @@ private void startDocument(BoundingBox box, BoundingBox? viewBox, UnitsType unit { this.initWriters(isPaperSpace); - this._mainWriter.WriteStartDocument(); + this._xmlWriter.WriteStartDocument(); - this._mainWriter.WriteStartElement("svg"); - this._mainWriter.WriteAttributeString("xmlns", "http://www.w3.org/2000/svg"); + this._xmlWriter.WriteStartElement("svg"); + this._xmlWriter.WriteAttributeString("xmlns", "http://www.w3.org/2000/svg"); - this._mainWriter.WriteAttributeString("width", box.Max.X - box.Min.X, units); - this._mainWriter.WriteAttributeString("height", box.Max.Y - box.Min.Y, units); + this._xmlWriter.WriteAttributeString("width", box.Max.X - box.Min.X, units); + this._xmlWriter.WriteAttributeString("height", box.Max.Y - box.Min.Y, units); if (viewBox.HasValue) { var vb = viewBox.Value; - this._mainWriter.WriteStartAttribute("viewBox"); - this._mainWriter.WriteValue(vb.Min.X.ToPixelSize(units)); - this._mainWriter.WriteValue(" "); - this._mainWriter.WriteValue(vb.Min.Y.ToPixelSize(units)); - this._mainWriter.WriteValue(" "); - this._mainWriter.WriteValue(vb.LengthX.ToPixelSize(units)); - this._mainWriter.WriteValue(" "); - this._mainWriter.WriteValue(vb.LengthY.ToPixelSize(units)); - this._mainWriter.WriteEndAttribute(); + this._xmlWriter.WriteStartAttribute("viewBox"); + this._xmlWriter.WriteValue(vb.Min.X.ToPixelSize(units)); + this._xmlWriter.WriteValue(" "); + this._xmlWriter.WriteValue(vb.Min.Y.ToPixelSize(units)); + this._xmlWriter.WriteValue(" "); + this._xmlWriter.WriteValue(vb.LengthX.ToPixelSize(units)); + this._xmlWriter.WriteValue(" "); + this._xmlWriter.WriteValue(vb.LengthY.ToPixelSize(units)); + this._xmlWriter.WriteEndAttribute(); } - this._mainWriter.WriteAttributeString("transform", $"scale(1,-1)"); + this._xmlWriter.WriteAttributeString("transform", $"scale(1,-1)"); if (isPaperSpace) { - this._mainWriter.WriteAttributeString("style", "background-color:white"); + this._xmlWriter.WriteAttributeString("style", "background-color:white"); } } } \ No newline at end of file diff --git a/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs b/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs index 014211a..89390bf 100644 --- a/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs +++ b/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs @@ -167,7 +167,7 @@ private void writeCircle(Circle circle, Transform transform) this.WriteEndElement(); } - private void writeDashes(IEnumerable dashes) + public void WriteDashes(IEnumerable dashes) { StringBuilder sb = new StringBuilder(); @@ -180,7 +180,7 @@ private void writeDashes(IEnumerable dashes) this.WriteAttributeString("stroke-dasharray", sb.ToString().Trim()); } - private void writeDashes(LineType lineType, double pointSize) + public void WriteDashes(LineType lineType, double pointSize) { StringBuilder sb = new StringBuilder(); foreach (LineType.Segment segment in lineType.Segments) @@ -303,7 +303,7 @@ private void writeEntityHeader(IEntity entity, Transform transform, bool drawStr if (drawStroke) { - this.WriteAttributeString("stroke", this.colorSvg(color)); + this.WriteAttributeString("stroke", this.ColorSvg(color)); } else { @@ -318,7 +318,7 @@ private void writeEntityHeader(IEntity entity, Transform transform, bool drawStr LineType lt = entity.GetActiveLineType(); if (this.drawableLineType(lt)) { - this.writeDashes(lt, this.getPointSize(entity)); + this.WriteDashes(lt, this.getPointSize(entity)); } } @@ -421,12 +421,12 @@ private string writePattern(Hatch hatch) //this.WriteAttributeString("x2", 1.0d.ToSvg(this.Units)); //this.WriteAttributeString("y2", (item.LineOffset / 2).ToSvg(this.Units)); - this.WriteAttributeString("stroke", this.colorSvg(hatch.GetActiveColor())); + this.WriteAttributeString("stroke", this.ColorSvg(hatch.GetActiveColor())); this.WriteAttributeString("stroke-width", $"{this.Configuration.GetLineWeightValue(hatch.GetActiveLineWeightType(), this.Units).ToSvg(UnitsType.Millimeters)}"); if (item.DashLengths.Any()) { - this.writeDashes(item.DashLengths); + this.WriteDashes(item.DashLengths); } //Line @@ -498,7 +498,7 @@ private void writePoint(Point point, Transform transform) this.WriteAttributeString("cx", point.Location.X); this.WriteAttributeString("cy", point.Location.Y); - this.WriteAttributeString("fill", this.colorSvg(point.GetActiveColor())); + this.WriteAttributeString("fill", this.ColorSvg(point.GetActiveColor())); this.WriteEndElement(); } @@ -532,7 +532,7 @@ private void writeSolid(Solid solid, Transform transform) string pts = this.svgPoints([solid.FirstCorner, solid.SecondCorner, solid.ThirdCorner, solid.FourthCorner], transform); this.WriteAttributeString("points", pts); - this.WriteAttributeString("fill", this.colorSvg(solid.GetActiveColor())); + this.WriteAttributeString("fill", this.ColorSvg(solid.GetActiveColor())); this.WriteEndElement(); } @@ -548,7 +548,7 @@ private string writeSolidPattern(Hatch hatch) this.WriteAttributeString("width", "100%"); this.WriteAttributeString("height", "100%"); - this.WriteAttributeString("fill", this.colorSvg(hatch.Color)); + this.WriteAttributeString("fill", this.ColorSvg(hatch.Color)); //rect this.WriteEndElement(); @@ -589,7 +589,7 @@ private void writeText(IText text, Transform transform) this.writeTransform(translation: insert.ToPixelSize(this.Units), scale: new XYZ(1, -1, 0), rotation: text.Rotation != 0 ? text.Rotation : null); - this.WriteAttributeString("fill", this.colorSvg(text.GetActiveColor())); + this.WriteAttributeString("fill", this.ColorSvg(text.GetActiveColor())); //My this.WriteStartAttribute("style"); diff --git a/src/ACadSharp.Formats/Svg/SvgExporter.cs b/src/ACadSharp.Formats/Svg/SvgExporter.cs index 02e1b90..2970eab 100644 --- a/src/ACadSharp.Formats/Svg/SvgExporter.cs +++ b/src/ACadSharp.Formats/Svg/SvgExporter.cs @@ -1,7 +1,6 @@ using ACadSharp.Objects; using ACadSharp.Tables; using System.IO; -using System.Xml; namespace ACadSharp.Formats.Svg; diff --git a/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs b/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs index 90103aa..1e51945 100644 --- a/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs +++ b/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs @@ -1,21 +1,21 @@ using ACadSharp.Tables; -using ACadSharp.Types.Units; using System; using System.Collections.Generic; -using System.Text; using System.Xml; namespace ACadSharp.Formats.Svg; -internal class SvgStyleWriter : CadXmlWriter +internal class SvgStyleWriter { private readonly Dictionary _layers = new(); private readonly Dictionary _lineTypes = new(); - public SvgStyleWriter(SvgConfiguration configuration, UnitsType units, Encoding encoding) - : base(configuration, units, encoding) + private readonly CadXmlWriter _writer; + + public SvgStyleWriter(CadXmlWriter writer) { + this._writer = writer; } public void AddLayer(Layer layer) @@ -39,34 +39,51 @@ public void AddLineType(LineType lineType) public void WriteStyles() { - this.WriteStartElement("svg", "http://www.w3.org/2000/svg"); - this.WriteStartElement("style"); + this._writer.WriteStartElement("style"); - this.WriteString(Environment.NewLine); + this._writer.WriteString(Environment.NewLine); this.writeCssLine(".entity{"); this.writeCssLine("vector-effect: non-scaling-stroke;"); this.writeCssLine("}"); - this.WriteString(Environment.NewLine); + this._writer.WriteString(Environment.NewLine); foreach (var layer in this._layers.Values) { - this.writeCssLine($".layer_{layer.Name}"); - this.writeCssLine("{"); - this.writeCssLine($"stroke: {this.colorSvg(layer.Color)};"); - this.writeCssLine($"stroke-width: {this.Configuration.GetLineWeightValue(layer.LineWeight, this.Units).ToSvg(UnitsType.Millimeters)}"); + this.writeCssLine($".layer_{layer.Name}{{"); + this.writeCssLine($"stroke: {this._writer.ColorSvg(layer.Color)};"); + this.writeCssLine($"stroke-width: {this._writer.WriteLineWeightValue(layer.LineWeight)};"); this.writeCssLine("}"); - this.WriteString(Environment.NewLine); + this._writer.WriteString(Environment.NewLine); + } + + if (false) + { + // It cannot get the lineweight of the entity, so the points will not be the correct size + foreach (var lt in this._lineTypes.Values) + { + if (lt.HasShapes) + { + continue; + } + + this.writeCssLine($".lt_{lt.Name}{{"); + this.writeCssLine("}"); + this._writer.WriteString(Environment.NewLine); + } } - this.WriteEndElement(); - this.WriteEndElement(); + this._writer.WriteEndElement(); } private void writeCssLine(string line) { - string indent = new string(this.IndentChar, this.Indentation); - this.WriteString(indent); - this.WriteString(line); - this.WriteString(Environment.NewLine); + if (this._writer.Formatting == Formatting.Indented) + { + string indent = new string(this._writer.IndentChar, this._writer.Indentation); + this._writer.WriteString(indent); + } + + this._writer.WriteString(line); + this._writer.WriteString(Environment.NewLine); } } \ No newline at end of file From 6b7c6f29b62bb7f4c6337e623dc5a19d6d1d99ee Mon Sep 17 00:00:00 2001 From: DomCR Date: Mon, 6 Jul 2026 13:13:41 +0200 Subject: [PATCH 04/13] Refactor SvgEntityWriter to use CadXmlWriter composition Refactored SvgEntityWriter to use composition over inheritance by delegating XML writing to a private CadXmlWriter instance. Moved SVG formatting utilities to CadXmlWriter and updated all usages. Updated SvgDocumentBuilder to pass the writer instance. Cleaned up obsolete code and improved separation of concerns for better maintainability. --- src/ACadSharp.Formats/Svg/CadXmlWriter.cs | 29 +- src/ACadSharp.Formats/Svg/SvgConverter.cs | 2 +- .../Svg/SvgDocumentBuilder.cs | 12 +- src/ACadSharp.Formats/Svg/SvgEntityWriter.cs | 380 +++++++++--------- src/ACadSharp.Formats/Svg/SvgStyleWriter.cs | 2 +- 5 files changed, 225 insertions(+), 200 deletions(-) diff --git a/src/ACadSharp.Formats/Svg/CadXmlWriter.cs b/src/ACadSharp.Formats/Svg/CadXmlWriter.cs index 4e9c8ed..03b3328 100644 --- a/src/ACadSharp.Formats/Svg/CadXmlWriter.cs +++ b/src/ACadSharp.Formats/Svg/CadXmlWriter.cs @@ -1,5 +1,8 @@ -using ACadSharp.IO; +using ACadSharp.Entities; +using ACadSharp.Extensions; +using ACadSharp.IO; using ACadSharp.Types.Units; +using CSMath; using System; using System.IO; using System.Text; @@ -40,6 +43,26 @@ public string ColorSvg(Color color) return $"rgb({color.R},{color.G},{color.B})"; } + public string ToSvgPixelSizeFormat(IVector value) + { + return value.ToPixelSize(this.Units).ToSvg(); + } + + public string ToSvgPixelSizeFormat(double value) + { + return value.ToPixelSize(this.Units).ToSvg(); + } + + public string ToSvgFormat(double value) + { + return value.ToSvg(this.Units); + } + + public double GetPointSize(IEntity entity) + { + return entity.GetActiveLineWeightType().GetLineWeightValue().ToPixelSize(this.Units); + } + public void WriteAttributeString(string localName, double value) { this.WriteAttributeString(localName, value, this.Units); @@ -50,12 +73,12 @@ public void WriteAttributeString(string localName, double value, UnitsType units this.WriteAttributeString(localName, value.ToSvg(units)); } - public string WriteLineWeightValue(LineWeightType lineWeight) + public string LineWeightValueToSvg(LineWeightType lineWeight) { return $"{this.Configuration.GetLineWeightValue(lineWeight, this.Units).ToSvg(UnitsType.Millimeters)}"; } - protected void notify(string message, NotificationType type, Exception ex = null) + public void Notify(string message, NotificationType type, Exception ex = null) { this.triggerNotification(this, new NotificationEventArgs(message, type, ex)); } diff --git a/src/ACadSharp.Formats/Svg/SvgConverter.cs b/src/ACadSharp.Formats/Svg/SvgConverter.cs index c19df26..6601cfe 100644 --- a/src/ACadSharp.Formats/Svg/SvgConverter.cs +++ b/src/ACadSharp.Formats/Svg/SvgConverter.cs @@ -28,7 +28,7 @@ public static string ToSvg(this double value, UnitsType units) break; } - return $"{value.ToString(CultureInfo.InvariantCulture)}{unitSufix}"; + return $"{value.ToSvg()}{unitSufix}"; } public static string ToSvg(this T vector) diff --git a/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs b/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs index 33415db..e305849 100644 --- a/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs +++ b/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs @@ -108,7 +108,7 @@ private void endDocument() { this._styleWriter.WriteStyles(); - this.mergeStream(this._entitiesWriter); + //this._entitiesWriter.WriteEntities(); this._xmlWriter.WriteEndElement(); this._xmlWriter.WriteEndDocument(); @@ -121,11 +121,7 @@ private void initWriters(bool isPaperSpace) this._xmlWriter.IsPaperSpace = isPaperSpace; this._xmlWriter.OnNotification += this.triggerNotification; - this._entitiesWriter = new SvgEntityWriter(this.Configuration, this.Units, this._encoding) - { - IsPaperSpace = isPaperSpace - }; - this._entitiesWriter.OnNotification += this.triggerNotification; + this._entitiesWriter = new SvgEntityWriter(this._xmlWriter); this._styleWriter = new SvgStyleWriter(this._xmlWriter); } @@ -154,14 +150,10 @@ private void processEntities(IEnumerable entities) private void processEntities(IEnumerable entities, Transform transform) { - this._entitiesWriter.WriteStartElement("svg", "http://www.w3.org/2000/svg"); - foreach (var entity in entities) { this.processEntity(entity, transform); } - - this._entitiesWriter.WriteEndElement(); } private void processEntity(Entity entity, Transform transform) diff --git a/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs b/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs index 89390bf..6dfc525 100644 --- a/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs +++ b/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs @@ -13,11 +13,13 @@ namespace ACadSharp.Formats.Svg; -internal class SvgEntityWriter : CadXmlWriter +internal class SvgEntityWriter { - public SvgEntityWriter(SvgConfiguration configuration, UnitsType units, Encoding encoding) - : base(configuration, units, encoding) + private readonly CadXmlWriter _writer; + + public SvgEntityWriter(CadXmlWriter writer) { + this._writer = writer; } public void WriteEntity(Entity entity, Transform transform) @@ -61,7 +63,7 @@ public void WriteEntity(Entity entity, Transform transform) this.writeSolid(solid, transform); break; default: - this.notify($"[{entity.ObjectName}] Entity not implemented.", NotificationType.NotImplemented); + _writer.Notify($"[{entity.ObjectName}] Entity not implemented.", NotificationType.NotImplemented); break; } } @@ -79,11 +81,11 @@ private string createPath(params IEnumerable polylines) } var pt = pts[0]; - sb.Append($"M {pt.ToPixelSize(this.Units).ToSvg()} "); + sb.Append($"M {this._writer.ToSvgPixelSizeFormat(pt)} "); for (int i = 1; i < pts.Length; i++) { pt = pts[i]; - sb.Append($"L {pt.ToPixelSize(this.Units).ToSvg()} "); + sb.Append($"L {this._writer.ToSvgPixelSizeFormat(pt)} "); } if (item.IsClosed) @@ -100,9 +102,10 @@ private bool drawableLineType(LineType lineType) return lineType.IsComplex && !lineType.HasShapes; } + [Obsolete] private double getPointSize(IEntity entity) { - return entity.GetActiveLineWeightType().GetLineWeightValue().ToPixelSize(this.Units); + return this._writer.GetPointSize(entity); } private string svgPoints(IEnumerable points, Transform transform) @@ -114,11 +117,11 @@ private string svgPoints(IEnumerable points, Transform transform) } StringBuilder sb = new StringBuilder(); - sb.Append(points.First().ToPixelSize(this.Units).ToSvg()); + sb.Append(this._writer.ToSvgPixelSizeFormat(points.First())); foreach (T point in points.Skip(1)) { sb.Append(' '); - sb.Append(point.ToPixelSize(this.Units).ToSvg()); + sb.Append(this._writer.ToSvgPixelSizeFormat(point)); } return sb.ToString(); @@ -126,7 +129,7 @@ private string svgPoints(IEnumerable points, Transform transform) private void writeArc(Arc arc, Transform transform) { - this.WriteStartElement("path"); + _writer.WriteStartElement("path"); this.writeEntityHeader(arc, transform); @@ -137,34 +140,34 @@ private void writeArc(Arc arc, Transform transform) StringBuilder sb = new StringBuilder(); - sb.Append($"M {start.ToPixelSize(this.Units).ToSvg()}"); + sb.Append($"M {this._writer.ToSvgPixelSizeFormat(start)}"); sb.Append($" "); - sb.Append($"A {arc.Radius.ToPixelSize(this.Units)} {arc.Radius.ToPixelSize(this.Units)}"); + sb.Append($"A {this._writer.ToSvgPixelSizeFormat(arc.Radius)} {this._writer.ToSvgPixelSizeFormat(arc.Radius)}"); sb.Append($" "); - sb.Append($"{0} {largeArc} {1} {end.ToPixelSize(this.Units).ToSvg()}"); + sb.Append($"{0} {largeArc} {1} {this._writer.ToSvgPixelSizeFormat(end)}"); - this.WriteAttributeString("d", sb.ToString()); + _writer.WriteAttributeString("d", sb.ToString()); - this.WriteAttributeString("fill", "none"); + _writer.WriteAttributeString("fill", "none"); - this.WriteEndElement(); + _writer.WriteEndElement(); } private void writeCircle(Circle circle, Transform transform) { var loc = transform.ApplyTransform(circle.Center); - this.WriteStartElement("circle"); + _writer.WriteStartElement("circle"); this.writeEntityHeader(circle, transform); - this.WriteAttributeString("r", circle.Radius); - this.WriteAttributeString("cx", loc.X); - this.WriteAttributeString("cy", loc.Y); + _writer.WriteAttributeString("r", circle.Radius); + _writer.WriteAttributeString("cx", loc.X); + _writer.WriteAttributeString("cy", loc.Y); - this.WriteAttributeString("fill", "none"); + _writer.WriteAttributeString("fill", "none"); - this.WriteEndElement(); + _writer.WriteEndElement(); } public void WriteDashes(IEnumerable dashes) @@ -173,11 +176,11 @@ public void WriteDashes(IEnumerable dashes) foreach (var d in dashes) { - sb.Append(Math.Abs(d.ToPixelSize(this.Units))); + sb.Append(this._writer.ToSvgPixelSizeFormat(Math.Abs(d))); sb.Append(' '); } - this.WriteAttributeString("stroke-dasharray", sb.ToString().Trim()); + _writer.WriteAttributeString("stroke-dasharray", sb.ToString().Trim()); } public void WriteDashes(LineType lineType, double pointSize) @@ -187,36 +190,36 @@ public void WriteDashes(LineType lineType, double pointSize) { if (segment.IsPoint) { - sb.Append(pointSize.ToPixelSize(this.Units)); + sb.Append(this._writer.ToSvgPixelSizeFormat(pointSize)); } else { - sb.Append(Math.Abs(segment.Length.ToPixelSize(this.Units))); + sb.Append(this._writer.ToSvgPixelSizeFormat(Math.Abs(segment.Length))); } sb.Append(' '); } - this.WriteAttributeString("stroke-dasharray", sb.ToString().Trim()); + _writer.WriteAttributeString("stroke-dasharray", sb.ToString().Trim()); } private void writeDimension(Dimension dimension, Transform transform) { - this.WriteStartElement("g"); + _writer.WriteStartElement("g"); foreach (Entity e in dimension.Block.Entities) { this.WriteEntity(e, transform); } - this.WriteEndElement(); + _writer.WriteEndElement(); } private void writeEllipse(Ellipse ellipse, Transform transform) { if (ellipse.IsFullEllipse) { - this.WriteStartElement("path"); + _writer.WriteStartElement("path"); this.writeEntityHeader(ellipse, transform); @@ -225,46 +228,53 @@ private void writeEllipse(Ellipse ellipse, Transform transform) XYZ start = ellipse.PolarCoordinateRelativeToCenter(0); XYZ end = ellipse.PolarCoordinateRelativeToCenter(Math.PI); - sb.Append($"M {start.ToPixelSize(this.Units).ToSvg()} "); - sb.Append($"A {ellipse.MajorAxis / 2} {ellipse.MinorAxis / 2} {MathHelper.RadToDeg(ellipse.Rotation)} {0} {1} {end.ToPixelSize(this.Units).ToSvg()} "); + string rx = this._writer.ToSvgPixelSizeFormat(ellipse.MajorAxis / 2); + string ry = this._writer.ToSvgPixelSizeFormat(ellipse.MinorAxis / 2); + string x_axis_rotation = MathHelper.RadToDeg(ellipse.Rotation).ToSvg(); + string xy = this._writer.ToSvgPixelSizeFormat(end); + + sb.Append($"M {this._writer.ToSvgPixelSizeFormat(start)} "); + sb.Append($"A {rx} {ry} {x_axis_rotation} {0} {1} {xy} "); start = ellipse.PolarCoordinateRelativeToCenter(Math.PI); end = ellipse.PolarCoordinateRelativeToCenter(MathHelper.TwoPI); - sb.Append($"A {ellipse.MajorAxis / 2} {ellipse.MinorAxis / 2} {MathHelper.RadToDeg(ellipse.Rotation)} {0} {1} {end.ToPixelSize(this.Units).ToSvg()}"); + + xy = this._writer.ToSvgPixelSizeFormat(end); + sb.Append($"A {rx} {ry} {x_axis_rotation} {0} {1} {xy}"); //A rx ry rotation large-arc-flag sweep-flag x y - this.WriteAttributeString("d", sb.ToString()); + _writer.WriteAttributeString("d", sb.ToString()); - this.WriteAttributeString("fill", "none"); - this.WriteEndElement(); + _writer.WriteAttributeString("fill", "none"); + _writer.WriteEndElement(); } else { - this.WriteStartElement("polyline"); + _writer.WriteStartElement("polyline"); this.writeEntityHeader(ellipse, transform); var vertices = ellipse.PolygonalVertexes(256); string pts = this.svgPoints(vertices, transform); - this.WriteAttributeString("points", pts); - this.WriteAttributeString("fill", "none"); + _writer.WriteAttributeString("points", pts); + _writer.WriteAttributeString("fill", "none"); - this.WriteEndElement(); + _writer.WriteEndElement(); return; //TODO: Fix the ellipse generation - this.WriteStartElement("path"); + _writer.WriteStartElement("path"); this.writeEntityHeader(ellipse, transform); ellipse.GetEndVertices(out XYZ start, out XYZ end); //A rx ry rotation large-arc-flag sweep-flag x y - this.WriteAttributeString("d", $"M {start.ToPixelSize(this.Units).ToSvg()} A {ellipse.MajorAxis} {ellipse.MinorAxis} {MathHelper.RadToDeg(ellipse.Rotation)} {0} {1} {end.ToPixelSize(this.Units).ToSvg()}"); + _writer.WriteAttributeString("d", $"M {start.ToPixelSize(this._writer.Units).ToSvg()} A {ellipse.MajorAxis} {ellipse.MinorAxis} {MathHelper.RadToDeg(ellipse.Rotation)} {0} {1} {end.ToPixelSize(this._writer.Units).ToSvg()}"); - this.WriteAttributeString("fill", "none"); - this.WriteEndElement(); + _writer.WriteAttributeString("fill", "none"); + _writer.WriteEndElement(); } } @@ -280,38 +290,38 @@ private void writeEntityAsPath(Entity entity, Transform transform, params IEn double pointSize = this.getPointSize(entity); var lines = entity.GetActiveLineType().CreateLineTypeShape(pointSize, points); - this.WriteStartElement("path"); + _writer.WriteStartElement("path"); this.writeEntityHeader(entity, transform); - this.WriteAttributeString("d", this.createPath(lines)); + _writer.WriteAttributeString("d", this.createPath(lines)); - this.WriteEndElement(); + _writer.WriteEndElement(); } private void writeEntityHeader(IEntity entity, Transform transform, bool drawStroke = true) { Color color = entity.GetActiveColor(); - this.WriteAttributeString("id", entity.Handle.ToString()); + _writer.WriteAttributeString("id", entity.Handle.ToString()); - this.WriteStartAttribute("class"); - this.WriteValue("entity"); - this.WriteValue(" "); - this.WriteValue($"layer_{entity.Layer.Name}"); - this.WriteEndAttribute(); + this._writer.WriteStartAttribute("class"); + this._writer.WriteValue("entity"); + this._writer.WriteValue(" "); + this._writer.WriteValue($"layer_{entity.Layer.Name}"); + this._writer.WriteEndAttribute(); if (drawStroke) { - this.WriteAttributeString("stroke", this.ColorSvg(color)); + _writer.WriteAttributeString("stroke", this._writer.ColorSvg(color)); } else { - this.WriteAttributeString("stroke", "none"); + _writer.WriteAttributeString("stroke", "none"); } var lineWeight = entity.GetActiveLineWeightType(); - this.WriteAttributeString("stroke-width", $"{this.Configuration.GetLineWeightValue(lineWeight, this.Units).ToSvg(UnitsType.Millimeters)}"); + _writer.WriteAttributeString("stroke-width", $"{this._writer.LineWeightValueToSvg(lineWeight)}"); this.writeTransform(transform); @@ -324,28 +334,28 @@ private void writeEntityHeader(IEntity entity, Transform transform, bool drawStr private void writeHatch(Hatch hatch, Transform transform) { - this.WriteStartElement("g"); + _writer.WriteStartElement("g"); var patternId = this.writePattern(hatch); List plines = new List(); foreach (Hatch.BoundaryPath path in hatch.Paths) { - var pline = new Polyline3D(path.GetPoints(this.Configuration.ArcPoints)); + var pline = new Polyline3D(path.GetPoints(this._writer.Configuration.ArcPoints)); plines.Add(pline); } - this.WriteStartElement("path"); + _writer.WriteStartElement("path"); this.writeEntityHeader(hatch, transform, drawStroke: false); - this.WriteAttributeString("d", this.createPath(plines)); + _writer.WriteAttributeString("d", this.createPath(plines)); - this.WriteAttributeString("fill", $"url(#{patternId})"); + _writer.WriteAttributeString("fill", $"url(#{patternId})"); - this.WriteEndElement(); + _writer.WriteEndElement(); - this.WriteEndElement(); + _writer.WriteEndElement(); } private void writeInsert(Insert insert, Transform transform) @@ -353,7 +363,7 @@ private void writeInsert(Insert insert, Transform transform) var insertTransform = insert.GetTransform(); var merged = new Transform(transform.Matrix * insertTransform.Matrix); - this.WriteStartElement("g"); + _writer.WriteStartElement("g"); this.writeTransform(merged); foreach (var e in insert.Block.Entities) @@ -361,21 +371,21 @@ private void writeInsert(Insert insert, Transform transform) this.WriteEntity(e); } - this.WriteEndElement(); + _writer.WriteEndElement(); } private void writeLine(Line line, Transform transform) { - this.WriteStartElement("line"); + _writer.WriteStartElement("line"); this.writeEntityHeader(line, transform); - this.WriteAttributeString("x1", line.StartPoint.X.ToSvg(this.Units)); - this.WriteAttributeString("y1", line.StartPoint.Y.ToSvg(this.Units)); - this.WriteAttributeString("x2", line.EndPoint.X.ToSvg(this.Units)); - this.WriteAttributeString("y2", line.EndPoint.Y.ToSvg(this.Units)); + _writer.WriteAttributeString("x1", this._writer.ToSvgFormat(line.StartPoint.X)); + _writer.WriteAttributeString("y1", this._writer.ToSvgFormat(line.StartPoint.Y)); + _writer.WriteAttributeString("x2", this._writer.ToSvgFormat(line.EndPoint.X)); + _writer.WriteAttributeString("y2", this._writer.ToSvgFormat(line.EndPoint.Y)); - this.WriteEndElement(); + _writer.WriteEndElement(); } private string writePattern(Hatch hatch) @@ -393,14 +403,14 @@ private string writePattern(Hatch hatch) //Each line works individually repeating itself every offset this.writePatternHeader(i); - this.WriteAttributeString("width", item.LineOffset.ToSvg(this.Units)); - this.WriteAttributeString("height", item.LineOffset.ToSvg(this.Units)); + _writer.WriteAttributeString("width", this._writer.ToSvgFormat(item.LineOffset)); + _writer.WriteAttributeString("height", this._writer.ToSvgFormat(item.LineOffset)); this.writeTransform(name: "patternTransform", - translation: item.BasePoint.Convert().ToPixelSize(this.Units)); + translation: item.BasePoint.Convert().ToPixelSize(this._writer.Units)); //rotation: -item.Angle); - this.WriteStartElement("line"); + _writer.WriteStartElement("line"); //Direction of the line var length = item.Offset.GetLength(); @@ -410,19 +420,19 @@ private string writePattern(Hatch hatch) //Offset -> is the size of the line box //Add BasePoint - this.WriteAttributeString("x1", 0.0d.ToSvg(this.Units)); - this.WriteAttributeString("y1", 0.0d.ToSvg(this.Units)); - this.WriteAttributeString("x2", (x).ToSvg(this.Units)); - this.WriteAttributeString("y2", (y).ToSvg(this.Units)); + _writer.WriteAttributeString("x1", 0.0d.ToSvg(this._writer.Units)); + _writer.WriteAttributeString("y1", 0.0d.ToSvg(this._writer.Units)); + _writer.WriteAttributeString("x2", (x).ToSvg(this._writer.Units)); + _writer.WriteAttributeString("y2", (y).ToSvg(this._writer.Units)); //Rotate the pattern after line - //this.WriteAttributeString("x1", 0.0d.ToSvg(this.Units)); - //this.WriteAttributeString("y1", (item.LineOffset / 2).ToSvg(this.Units)); - //this.WriteAttributeString("x2", 1.0d.ToSvg(this.Units)); - //this.WriteAttributeString("y2", (item.LineOffset / 2).ToSvg(this.Units)); + //_writer.WriteAttributeString("x1", 0.0d.ToSvg(this._writer.Units)); + //_writer.WriteAttributeString("y1", (item.LineOffset / 2).ToSvg(this._writer.Units)); + //_writer.WriteAttributeString("x2", 1.0d.ToSvg(this._writer.Units)); + //_writer.WriteAttributeString("y2", (item.LineOffset / 2).ToSvg(this._writer.Units)); - this.WriteAttributeString("stroke", this.ColorSvg(hatch.GetActiveColor())); - this.WriteAttributeString("stroke-width", $"{this.Configuration.GetLineWeightValue(hatch.GetActiveLineWeightType(), this.Units).ToSvg(UnitsType.Millimeters)}"); + _writer.WriteAttributeString("stroke", this._writer.ColorSvg(hatch.GetActiveColor())); + _writer.WriteAttributeString("stroke-width", $"{this._writer.LineWeightValueToSvg(hatch.GetActiveLineWeightType())}"); if (item.DashLengths.Any()) { @@ -430,131 +440,131 @@ private string writePattern(Hatch hatch) } //Line - this.WriteEndElement(); + _writer.WriteEndElement(); if (false) { - this.WriteStartElement("rect"); - this.WriteAttributeString("width", (item.LineOffset).ToSvg(this.Units)); - this.WriteAttributeString("height", (item.LineOffset).ToSvg(this.Units)); - this.WriteAttributeString("fill", $"none"); - this.WriteAttributeString("stroke", $"red"); - this.WriteEndElement(); + _writer.WriteStartElement("rect"); + _writer.WriteAttributeString("width", (item.LineOffset).ToSvg(this._writer.Units)); + _writer.WriteAttributeString("height", (item.LineOffset).ToSvg(this._writer.Units)); + _writer.WriteAttributeString("fill", $"none"); + _writer.WriteAttributeString("stroke", $"red"); + _writer.WriteEndElement(); } //Pattern - this.WriteEndElement(); + _writer.WriteEndElement(); } string id = this.writePatternHeader(hatch); var width = patterns.Values.Max(w => w.LengthX); var height = patterns.Values.Max(w => w.LengthY); - this.WriteAttributeString("width", width.ToSvg(this.Units)); - this.WriteAttributeString("height", height.ToSvg(this.Units)); + _writer.WriteAttributeString("width", width.ToSvg(this._writer.Units)); + _writer.WriteAttributeString("height", height.ToSvg(this._writer.Units)); foreach (var item in patterns) { - this.WriteStartElement("rect"); - this.WriteAttributeString("width", (item.Value.LengthX).ToSvg(this.Units)); - this.WriteAttributeString("height", (item.Value.LengthY).ToSvg(this.Units)); - this.WriteAttributeString("fill", $"url(#{item.Key})"); - this.WriteEndElement(); + _writer.WriteStartElement("rect"); + _writer.WriteAttributeString("width", (item.Value.LengthX).ToSvg(this._writer.Units)); + _writer.WriteAttributeString("height", (item.Value.LengthY).ToSvg(this._writer.Units)); + _writer.WriteAttributeString("fill", $"url(#{item.Key})"); + _writer.WriteEndElement(); } //pattern - this.WriteEndElement(); + _writer.WriteEndElement(); return id; } private void writePatternHeader(string id) { - this.WriteStartElement("pattern"); + _writer.WriteStartElement("pattern"); - this.WriteAttributeString("id", id); - this.WriteAttributeString("patternUnits", "userSpaceOnUse"); + _writer.WriteAttributeString("id", id); + _writer.WriteAttributeString("patternUnits", "userSpaceOnUse"); } private string writePatternHeader(Hatch hatch) { string id = $"{hatch.Pattern.GetHashCode()}_{hatch.Pattern.Name}"; - this.WriteStartElement("pattern"); + _writer.WriteStartElement("pattern"); - this.WriteAttributeString("id", id); - this.WriteAttributeString("patternUnits", "userSpaceOnUse"); + _writer.WriteAttributeString("id", id); + _writer.WriteAttributeString("patternUnits", "userSpaceOnUse"); return id; } private void writePoint(Point point, Transform transform) { - this.WriteStartElement("circle"); + _writer.WriteStartElement("circle"); this.writeEntityHeader(point, transform); - this.WriteAttributeString("r", this.Configuration.PointRadius, UnitsType.Unitless); - this.WriteAttributeString("cx", point.Location.X); - this.WriteAttributeString("cy", point.Location.Y); + _writer.WriteAttributeString("r", this._writer.Configuration.PointRadius, UnitsType.Unitless); + _writer.WriteAttributeString("cx", point.Location.X); + _writer.WriteAttributeString("cy", point.Location.Y); - this.WriteAttributeString("fill", this.ColorSvg(point.GetActiveColor())); + _writer.WriteAttributeString("fill", this._writer.ColorSvg(point.GetActiveColor())); - this.WriteEndElement(); + _writer.WriteEndElement(); } private void writePolyline(IPolyline polyline, Transform transform) { if (polyline.IsClosed) { - this.WriteStartElement("polygon"); + _writer.WriteStartElement("polygon"); } else { - this.WriteStartElement("polyline"); + _writer.WriteStartElement("polyline"); } this.writeEntityHeader(polyline, transform); - string pts = this.svgPoints(polyline.GetPoints(this.Configuration.ArcPoints), transform); + string pts = this.svgPoints(polyline.GetPoints(this._writer.Configuration.ArcPoints), transform); - this.WriteAttributeString("points", pts); - this.WriteAttributeString("fill", "none"); + _writer.WriteAttributeString("points", pts); + _writer.WriteAttributeString("fill", "none"); - this.WriteEndElement(); + _writer.WriteEndElement(); } private void writeSolid(Solid solid, Transform transform) { - this.WriteStartElement("polygon"); + _writer.WriteStartElement("polygon"); this.writeEntityHeader(solid, transform); string pts = this.svgPoints([solid.FirstCorner, solid.SecondCorner, solid.ThirdCorner, solid.FourthCorner], transform); - this.WriteAttributeString("points", pts); - this.WriteAttributeString("fill", this.ColorSvg(solid.GetActiveColor())); + _writer.WriteAttributeString("points", pts); + _writer.WriteAttributeString("fill", this._writer.ColorSvg(solid.GetActiveColor())); - this.WriteEndElement(); + _writer.WriteEndElement(); } private string writeSolidPattern(Hatch hatch) { string id = this.writePatternHeader(hatch); - this.WriteAttributeString("width", "100%"); - this.WriteAttributeString("height", "100%"); + _writer.WriteAttributeString("width", "100%"); + _writer.WriteAttributeString("height", "100%"); - this.WriteStartElement("rect"); + _writer.WriteStartElement("rect"); - this.WriteAttributeString("width", "100%"); - this.WriteAttributeString("height", "100%"); - this.WriteAttributeString("fill", this.ColorSvg(hatch.Color)); + _writer.WriteAttributeString("width", "100%"); + _writer.WriteAttributeString("height", "100%"); + _writer.WriteAttributeString("fill", this._writer.ColorSvg(hatch.Color)); //rect - this.WriteEndElement(); + _writer.WriteEndElement(); //pattern - this.WriteEndElement(); + _writer.WriteEndElement(); return id; } @@ -562,7 +572,7 @@ private string writeSolidPattern(Hatch hatch) private void writeSpline(Spline spline, Transform transform) { spline.UpdateFromFitPoints(); - this.writeEntityAsPath(spline, transform, spline.PolygonalVertexes(this.Configuration.ArcPoints)); + this.writeEntityAsPath(spline, transform, spline.PolygonalVertexes(this._writer.Configuration.ArcPoints)); } private void writeText(IText text, Transform transform) @@ -582,37 +592,37 @@ private void writeText(IText text, Transform transform) insert = text.InsertPoint; } - this.WriteStartElement("g"); + _writer.WriteStartElement("g"); this.writeTransform(transform); - this.WriteStartElement("text"); + _writer.WriteStartElement("text"); - this.writeTransform(translation: insert.ToPixelSize(this.Units), scale: new XYZ(1, -1, 0), rotation: text.Rotation != 0 ? text.Rotation : null); + this.writeTransform(translation: insert.ToPixelSize(this._writer.Units), scale: new XYZ(1, -1, 0), rotation: text.Rotation != 0 ? text.Rotation : null); - this.WriteAttributeString("fill", this.ColorSvg(text.GetActiveColor())); + _writer.WriteAttributeString("fill", this._writer.ColorSvg(text.GetActiveColor())); //My - this.WriteStartAttribute("style"); - this.WriteValue("font:"); - this.WriteValue(text.Height.ToSvg(this.Units)); - if (this.Units == UnitsType.Unitless) + this._writer.WriteStartAttribute("style"); + this._writer.WriteValue("font:"); + this._writer.WriteValue(text.Height.ToSvg(this._writer.Units)); + if (this._writer.Units == UnitsType.Unitless) { - this.WriteValue("px"); + this._writer.WriteValue("px"); } if (text.Style.TrueType.HasFlag(FontFlags.Bold)) { - this.WriteValue("bold"); + this._writer.WriteValue("bold"); } if (text.Style.TrueType.HasFlag(FontFlags.Italic)) { - this.WriteValue("italic"); + this._writer.WriteValue("italic"); } - this.WriteValue(" "); - this.WriteValue(Path.GetFileNameWithoutExtension(text.Style.Filename)); - this.WriteEndAttribute(); + this._writer.WriteValue(" "); + this._writer.WriteValue(Path.GetFileNameWithoutExtension(text.Style.Filename)); + this._writer.WriteEndAttribute(); switch (text) { @@ -620,40 +630,40 @@ private void writeText(IText text, Transform transform) switch (mtext.AttachmentPoint) { case AttachmentPointType.TopLeft: - this.WriteAttributeString("alignment-baseline", "hanging"); - this.WriteAttributeString("text-anchor", "start"); + _writer.WriteAttributeString("alignment-baseline", "hanging"); + _writer.WriteAttributeString("text-anchor", "start"); break; case AttachmentPointType.TopCenter: - this.WriteAttributeString("alignment-baseline", "hanging"); - this.WriteAttributeString("text-anchor", "middle"); + _writer.WriteAttributeString("alignment-baseline", "hanging"); + _writer.WriteAttributeString("text-anchor", "middle"); break; case AttachmentPointType.TopRight: - this.WriteAttributeString("alignment-baseline", "hanging"); - this.WriteAttributeString("text-anchor", "end"); + _writer.WriteAttributeString("alignment-baseline", "hanging"); + _writer.WriteAttributeString("text-anchor", "end"); break; case AttachmentPointType.MiddleLeft: - this.WriteAttributeString("alignment-baseline", "middle"); - this.WriteAttributeString("text-anchor", "start"); + _writer.WriteAttributeString("alignment-baseline", "middle"); + _writer.WriteAttributeString("text-anchor", "start"); break; case AttachmentPointType.MiddleCenter: - this.WriteAttributeString("alignment-baseline", "middle"); - this.WriteAttributeString("text-anchor", "middle"); + _writer.WriteAttributeString("alignment-baseline", "middle"); + _writer.WriteAttributeString("text-anchor", "middle"); break; case AttachmentPointType.MiddleRight: - this.WriteAttributeString("alignment-baseline", "middle"); - this.WriteAttributeString("text-anchor", "end"); + _writer.WriteAttributeString("alignment-baseline", "middle"); + _writer.WriteAttributeString("text-anchor", "end"); break; case AttachmentPointType.BottomLeft: - this.WriteAttributeString("alignment-baseline", "baseline"); - this.WriteAttributeString("text-anchor", "start"); + _writer.WriteAttributeString("alignment-baseline", "baseline"); + _writer.WriteAttributeString("text-anchor", "start"); break; case AttachmentPointType.BottomCenter: - this.WriteAttributeString("alignment-baseline", "baseline"); - this.WriteAttributeString("text-anchor", "middle"); + _writer.WriteAttributeString("alignment-baseline", "baseline"); + _writer.WriteAttributeString("text-anchor", "middle"); break; case AttachmentPointType.BottomRight: - this.WriteAttributeString("alignment-baseline", "baseline"); - this.WriteAttributeString("text-anchor", "end"); + _writer.WriteAttributeString("alignment-baseline", "baseline"); + _writer.WriteAttributeString("text-anchor", "end"); break; default: break; @@ -661,34 +671,34 @@ private void writeText(IText text, Transform transform) foreach (var item in mtext.GetPlainTextLines()) { - this.WriteStartElement("tspan"); - this.WriteAttributeString("x", 0); - this.WriteAttributeString("dy", "1em"); - this.WriteString(item); - this.WriteEndElement(); + _writer.WriteStartElement("tspan"); + _writer.WriteAttributeString("x", 0); + _writer.WriteAttributeString("dy", "1em"); + this._writer.WriteString(item); + _writer.WriteEndElement(); } //Line to avoid the strange offset at the end - this.WriteStartElement("tspan"); - this.WriteAttributeString("x", 0); - this.WriteAttributeString("dy", "1em"); - this.WriteAttributeString("visibility", "hidden"); - this.WriteString("."); - this.WriteEndElement(); + _writer.WriteStartElement("tspan"); + _writer.WriteAttributeString("x", 0); + _writer.WriteAttributeString("dy", "1em"); + _writer.WriteAttributeString("visibility", "hidden"); + this._writer.WriteString("."); + _writer.WriteEndElement(); break; case TextEntity textEntity: switch (textEntity.HorizontalAlignment) { case TextHorizontalAlignment.Left: - this.WriteAttributeString("text-anchor", "start"); + _writer.WriteAttributeString("text-anchor", "start"); break; case TextHorizontalAlignment.Middle: case TextHorizontalAlignment.Center: - this.WriteAttributeString("text-anchor", "middle"); + _writer.WriteAttributeString("text-anchor", "middle"); break; case TextHorizontalAlignment.Right: - this.WriteAttributeString("text-anchor", "end"); + _writer.WriteAttributeString("text-anchor", "end"); break; } @@ -696,22 +706,22 @@ private void writeText(IText text, Transform transform) { case TextVerticalAlignmentType.Baseline: case TextVerticalAlignmentType.Bottom: - this.WriteAttributeString("alignment-baseline", "baseline"); + _writer.WriteAttributeString("alignment-baseline", "baseline"); break; case TextVerticalAlignmentType.Middle: - this.WriteAttributeString("alignment-baseline", "middle"); + _writer.WriteAttributeString("alignment-baseline", "middle"); break; case TextVerticalAlignmentType.Top: - this.WriteAttributeString("alignment-baseline", "hanging"); + _writer.WriteAttributeString("alignment-baseline", "hanging"); break; } - this.WriteString(text.Value); + this._writer.WriteString(text.Value); break; } - this.WriteEndElement(); - this.WriteEndElement(); + _writer.WriteEndElement(); + _writer.WriteEndElement(); } private void writeTransform(Transform transform) @@ -758,6 +768,6 @@ private void writeTransform(string name = "transform", XYZ? translation = null, return; } - this.WriteAttributeString(name, sb.ToString()); + _writer.WriteAttributeString(name, sb.ToString()); } } \ No newline at end of file diff --git a/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs b/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs index 1e51945..e2be6c4 100644 --- a/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs +++ b/src/ACadSharp.Formats/Svg/SvgStyleWriter.cs @@ -51,7 +51,7 @@ public void WriteStyles() { this.writeCssLine($".layer_{layer.Name}{{"); this.writeCssLine($"stroke: {this._writer.ColorSvg(layer.Color)};"); - this.writeCssLine($"stroke-width: {this._writer.WriteLineWeightValue(layer.LineWeight)};"); + this.writeCssLine($"stroke-width: {this._writer.LineWeightValueToSvg(layer.LineWeight)};"); this.writeCssLine("}"); this._writer.WriteString(Environment.NewLine); } From 1d8df8a544b4e020f8a920593ae6aaa643281987 Mon Sep 17 00:00:00 2001 From: DomCR Date: Mon, 6 Jul 2026 13:32:40 +0200 Subject: [PATCH 05/13] Refactor SVG stroke attributes to use style attribute Replaces separate "stroke" and "stroke-width" attributes with a single CSS "style" attribute for SVG entities. Uses StringBuilder to construct style rules, improving maintainability and centralizing SVG styling logic. Handles cases for disabling stroke, custom colors, and line weights. --- src/ACadSharp.Formats/Svg/SvgEntityWriter.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs b/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs index 6dfc525..36dc0e3 100644 --- a/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs +++ b/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs @@ -311,17 +311,27 @@ private void writeEntityHeader(IEntity entity, Transform transform, bool drawStr this._writer.WriteValue($"layer_{entity.Layer.Name}"); this._writer.WriteEndAttribute(); - if (drawStroke) + StringBuilder style = new StringBuilder(); + + if (!drawStroke) { - _writer.WriteAttributeString("stroke", this._writer.ColorSvg(color)); + style.Append($"stroke:none;"); } - else + else if (!entity.Color.IsByLayer) { - _writer.WriteAttributeString("stroke", "none"); + style.Append($"stroke:{this._writer.ColorSvg(color)};"); } var lineWeight = entity.GetActiveLineWeightType(); - _writer.WriteAttributeString("stroke-width", $"{this._writer.LineWeightValueToSvg(lineWeight)}"); + if (entity.LineWeight != LineWeightType.ByLayer) + { + style.Append($"stroke-width: {this._writer.LineWeightValueToSvg(lineWeight)};"); + } + + if (style.Length > 0) + { + this._writer.WriteAttributeString("style", style.ToString()); + } this.writeTransform(transform); From 25ddf8e68c2ec710a54e1e6d09ccfdaa3f4abaf2 Mon Sep 17 00:00:00 2001 From: DomCR Date: Mon, 6 Jul 2026 15:43:18 +0200 Subject: [PATCH 06/13] Add decimal precision config for SVG output and refactor Introduce DecimalPlaces to SvgConfiguration for controlling SVG number formatting. Refactor SvgConverter and related methods to support decimal precision. Update SvgEntityWriter to batch entities before writing. Remove obsolete methods and improve code organization and clarity. --- src/ACadSharp.Formats/Svg/CadXmlWriter.cs | 6 +- src/ACadSharp.Formats/Svg/SvgConfiguration.cs | 29 ++++--- src/ACadSharp.Formats/Svg/SvgConverter.cs | 77 ++++++++++--------- .../Svg/SvgDocumentBuilder.cs | 25 ++---- src/ACadSharp.Formats/Svg/SvgEntityWriter.cs | 41 ++++++---- 5 files changed, 95 insertions(+), 83 deletions(-) diff --git a/src/ACadSharp.Formats/Svg/CadXmlWriter.cs b/src/ACadSharp.Formats/Svg/CadXmlWriter.cs index 03b3328..191cd42 100644 --- a/src/ACadSharp.Formats/Svg/CadXmlWriter.cs +++ b/src/ACadSharp.Formats/Svg/CadXmlWriter.cs @@ -45,17 +45,17 @@ public string ColorSvg(Color color) public string ToSvgPixelSizeFormat(IVector value) { - return value.ToPixelSize(this.Units).ToSvg(); + return value.ToPixelSize(this.Units).ToSvg(this.Configuration.DecimalPlaces); } public string ToSvgPixelSizeFormat(double value) { - return value.ToPixelSize(this.Units).ToSvg(); + return value.ToPixelSize(this.Units).ToSvg(this.Configuration.DecimalPlaces); } public string ToSvgFormat(double value) { - return value.ToSvg(this.Units); + return value.ToSvg(this.Units, this.Configuration.DecimalPlaces); } public double GetPointSize(IEntity entity) diff --git a/src/ACadSharp.Formats/Svg/SvgConfiguration.cs b/src/ACadSharp.Formats/Svg/SvgConfiguration.cs index 71fb9d8..7cfea7c 100644 --- a/src/ACadSharp.Formats/Svg/SvgConfiguration.cs +++ b/src/ACadSharp.Formats/Svg/SvgConfiguration.cs @@ -10,24 +10,37 @@ namespace ACadSharp.Formats.Svg; /// public class SvgConfiguration { - public Formatting Formatting { get; set; } = Formatting.Indented; + /// + /// Number of points to use when drawing arcs and circles. + /// + public int ArcPoints { get; set; } = 256; /// - /// The will be divided by this value to process the stroke-width in the svg when the units are . + /// Number of decimal places to use when writing numbers in the SVG file. /// - /// - /// The default value is 100, which matches with the line weight real value in mm. - /// - public double LineWeightRatio { get; set; } = 100; + public int DecimalPlaces { get; set; } = 2; /// - /// Weight value for the . + /// Weight value for the . /// /// /// Value must be in mm. /// public double DefaultLineWeight { get; set; } = 0.01; + /// + /// Gets or sets the for the XML output. + /// + public Formatting Formatting { get; set; } = Formatting.Indented; + + /// + /// The will be divided by this value to process the stroke-width in the svg when the units are . + /// + /// + /// The default value is 100, which matches with the line weight real value in mm. + /// + public double LineWeightRatio { get; set; } = 100; + /// /// Radius applied for the points. /// @@ -36,8 +49,6 @@ public class SvgConfiguration /// public double PointRadius { get; set; } = 0.1; - public int ArcPoints { get; set; } = 256; - /// /// Get the value of the stroke-width in mm. /// diff --git a/src/ACadSharp.Formats/Svg/SvgConverter.cs b/src/ACadSharp.Formats/Svg/SvgConverter.cs index 6601cfe..1a959d7 100644 --- a/src/ACadSharp.Formats/Svg/SvgConverter.cs +++ b/src/ACadSharp.Formats/Svg/SvgConverter.cs @@ -7,12 +7,45 @@ namespace ACadSharp.Formats.Svg; internal static class SvgConverter { - public static string ToSvg(this double value) + public static double ToPixelSize(this double value, UnitsType units) + { + switch (units) + { + case UnitsType.Inches: + return value * 96; + case UnitsType.Millimeters: + return value * 96 / 25.4; + case UnitsType.Unitless: + return value; + default: + throw new InvalidEnumArgumentException(nameof(units), (int)units, units.GetType()); + } + } + + public static T ToPixelSize(this T value, UnitsType units) + where T : IVector + { + for (int i = 0; i < value.Dimension; i++) + { + value[i] = ToPixelSize(value[i], units); + } + + return value; + } + + public static string ToSvg(this double value, int? decimalPlaces = null) { - return value.ToString(CultureInfo.InvariantCulture); + if (decimalPlaces != null) + { + return value.ToString($"N{decimalPlaces}", CultureInfo.InvariantCulture); + } + else + { + return value.ToString(CultureInfo.InvariantCulture); + } } - public static string ToSvg(this double value, UnitsType units) + public static string ToSvg(this double value, UnitsType units, int? decimalPlaces = null) { string unitSufix = string.Empty; switch (units) @@ -28,44 +61,18 @@ public static string ToSvg(this double value, UnitsType units) break; } - return $"{value.ToSvg()}{unitSufix}"; + return $"{value.ToSvg(decimalPlaces)}{unitSufix}"; } - public static string ToSvg(this T vector) + public static string ToSvg(this T vector, int? decimalPlaces = null) where T : IVector { - return $"{vector[0].ToSvg()},{vector[1].ToSvg()}"; - } - - public static string ToSvg(this T vector, UnitsType units) - where T : IVector - { - return $"{vector[0].ToSvg(units)},{vector[1].ToSvg(units)}"; - } - - public static double ToPixelSize(this double value, UnitsType units) - { - switch (units) - { - case UnitsType.Inches: - return value * 96; - case UnitsType.Millimeters: - return value * 96 / 25.4; - case UnitsType.Unitless: - return value; - default: - throw new InvalidEnumArgumentException(nameof(units), (int)units, units.GetType()); - } + return $"{vector[0].ToSvg(decimalPlaces)},{vector[1].ToSvg(decimalPlaces)}"; } - public static T ToPixelSize(this T value, UnitsType units) + public static string ToSvg(this T vector, UnitsType units, int? decimalPlaces = null) where T : IVector { - for (int i = 0; i < value.Dimension; i++) - { - value[i] = ToPixelSize(value[i], units); - } - - return value; + return $"{vector[0].ToSvg(units, decimalPlaces)},{vector[1].ToSvg(units, decimalPlaces)}"; } -} +} \ No newline at end of file diff --git a/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs b/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs index e305849..4c03bac 100644 --- a/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs +++ b/src/ACadSharp.Formats/Svg/SvgDocumentBuilder.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.IO; using System.Text; -using System.Xml.Linq; namespace ACadSharp.Formats.Svg; @@ -108,7 +107,7 @@ private void endDocument() { this._styleWriter.WriteStyles(); - //this._entitiesWriter.WriteEntities(); + this._entitiesWriter.WriteEntities(); this._xmlWriter.WriteEndElement(); this._xmlWriter.WriteEndDocument(); @@ -126,23 +125,6 @@ private void initWriters(bool isPaperSpace) this._styleWriter = new SvgStyleWriter(this._xmlWriter); } - private void mergeStream(CadXmlWriter writer) - { - writer.Flush(); - writer.BaseStream.Position = 0; - - XDocument temp = XDocument.Load(writer.BaseStream, LoadOptions.None); - if (temp.Root is null) - { - return; - } - - foreach (XNode node in temp.Root.Nodes()) - { - node.WriteTo(this._xmlWriter); - } - } - private void processEntities(IEnumerable entities) { this.processEntities(entities, new Transform()); @@ -152,6 +134,9 @@ private void processEntities(IEnumerable entities, Transform transform) { foreach (var entity in entities) { + if(entity.IsInvisible) + continue; + this.processEntity(entity, transform); } } @@ -161,7 +146,7 @@ private void processEntity(Entity entity, Transform transform) this._styleWriter.AddLineType(entity.LineType); this._styleWriter.AddLayer(entity.Layer); - this._entitiesWriter.WriteEntity(entity, transform); + this._entitiesWriter.AddEntity(entity, transform); } private void startDocument(BoundingBox box, BoundingBox? viewBox, UnitsType units, bool isPaperSpace = false) diff --git a/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs b/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs index 36dc0e3..b6c63c6 100644 --- a/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs +++ b/src/ACadSharp.Formats/Svg/SvgEntityWriter.cs @@ -15,6 +15,8 @@ namespace ACadSharp.Formats.Svg; internal class SvgEntityWriter { + public readonly List<(Entity, Transform)> _entities = new(); + private readonly CadXmlWriter _writer; public SvgEntityWriter(CadXmlWriter writer) @@ -22,6 +24,19 @@ public SvgEntityWriter(CadXmlWriter writer) this._writer = writer; } + public void AddEntity(Entity entity, Transform transform) + { + this._entities.Add((entity, transform)); + } + + public void WriteEntities() + { + foreach (var (entity, transform) in this._entities) + { + this.WriteEntity(entity, transform); + } + } + public void WriteEntity(Entity entity, Transform transform) { switch (entity) @@ -68,6 +83,11 @@ public void WriteEntity(Entity entity, Transform transform) } } + public void WriteEntity(Entity entity) + { + this.WriteEntity(entity, new Transform()); + } + private string createPath(params IEnumerable polylines) { StringBuilder sb = new StringBuilder(); @@ -102,12 +122,6 @@ private bool drawableLineType(LineType lineType) return lineType.IsComplex && !lineType.HasShapes; } - [Obsolete] - private double getPointSize(IEntity entity) - { - return this._writer.GetPointSize(entity); - } - private string svgPoints(IEnumerable points, Transform transform) where T : IVector, new() { @@ -170,7 +184,7 @@ private void writeCircle(Circle circle, Transform transform) _writer.WriteEndElement(); } - public void WriteDashes(IEnumerable dashes) + private void writeDashes(IEnumerable dashes) { StringBuilder sb = new StringBuilder(); @@ -183,7 +197,7 @@ public void WriteDashes(IEnumerable dashes) _writer.WriteAttributeString("stroke-dasharray", sb.ToString().Trim()); } - public void WriteDashes(LineType lineType, double pointSize) + private void writeDashes(LineType lineType, double pointSize) { StringBuilder sb = new StringBuilder(); foreach (LineType.Segment segment in lineType.Segments) @@ -278,16 +292,11 @@ private void writeEllipse(Ellipse ellipse, Transform transform) } } - public void WriteEntity(Entity entity) - { - this.WriteEntity(entity, new Transform()); - } - private void writeEntityAsPath(Entity entity, Transform transform, params IEnumerable points) where T : IVector { //Will be needed to write the linetypes that use shapes - double pointSize = this.getPointSize(entity); + double pointSize = this._writer.GetPointSize(entity); var lines = entity.GetActiveLineType().CreateLineTypeShape(pointSize, points); _writer.WriteStartElement("path"); @@ -338,7 +347,7 @@ private void writeEntityHeader(IEntity entity, Transform transform, bool drawStr LineType lt = entity.GetActiveLineType(); if (this.drawableLineType(lt)) { - this.WriteDashes(lt, this.getPointSize(entity)); + this.writeDashes(lt, this._writer.GetPointSize(entity)); } } @@ -446,7 +455,7 @@ private string writePattern(Hatch hatch) if (item.DashLengths.Any()) { - this.WriteDashes(item.DashLengths); + this.writeDashes(item.DashLengths); } //Line From 5db772b8504b02b53bb2f72d04e4667f6afbc339 Mon Sep 17 00:00:00 2001 From: DomCR Date: Mon, 6 Jul 2026 15:47:11 +0200 Subject: [PATCH 07/13] docs --- src/ACadSharp.Formats/ExporterBase.cs | 25 ++++++++++++++++++++++++ src/ACadSharp.Formats/IExporter.cs | 23 ++++++++++++++++++++-- src/ACadSharp.Formats/Svg/SvgExporter.cs | 17 ++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/ACadSharp.Formats/ExporterBase.cs b/src/ACadSharp.Formats/ExporterBase.cs index b0a5f7a..8898d29 100644 --- a/src/ACadSharp.Formats/ExporterBase.cs +++ b/src/ACadSharp.Formats/ExporterBase.cs @@ -6,12 +6,25 @@ namespace ACadSharp.Formats; +/// +/// Abstract base class for exporting CAD documents and layouts to various formats. +/// public abstract class ExporterBase : IExporter { + /// + /// Occurs when a notification is triggered during the export process. + /// public event NotificationEventHandler OnNotification; + /// + /// The output stream for the exported data. + /// protected readonly Stream _stream; + /// + /// Initializes a new instance of the class. + /// + /// The stream to write the exported data to. protected ExporterBase(Stream stream) { this._stream = stream; @@ -23,10 +36,22 @@ public void Dispose() this._stream.Dispose(); } + /// + /// Exports a specific block record. + /// + /// The block record to export. public abstract void Export(BlockRecord record); + /// + /// Exports a complete CAD document. + /// + /// The CAD document to export. public abstract void Export(CadDocument document); + /// + /// Exports a specific layout. + /// + /// The layout to export. public abstract void Export(Layout layout); protected void triggerNotification(string message, NotificationType notificationType, Exception ex = null) diff --git a/src/ACadSharp.Formats/IExporter.cs b/src/ACadSharp.Formats/IExporter.cs index ffcc34f..c674862 100644 --- a/src/ACadSharp.Formats/IExporter.cs +++ b/src/ACadSharp.Formats/IExporter.cs @@ -5,12 +5,31 @@ namespace ACadSharp.Formats; +/// +/// Defines the contract for exporting CAD entities and documents to a specific format. +/// public interface IExporter : IDisposable { + /// + /// Event triggered when a notification occurs during the export process. + /// public event NotificationEventHandler OnNotification; + /// + /// Exports a specific block record. + /// + /// The block record to export. public void Export(BlockRecord record); + /// + /// Exports an entire CAD document. + /// + /// The CAD document to export. public void Export(CadDocument document); - void Export(Layout layout); -} + + /// + /// Exports a specific layout. + /// + /// The layout to export. + public void Export(Layout layout); +} \ No newline at end of file diff --git a/src/ACadSharp.Formats/Svg/SvgExporter.cs b/src/ACadSharp.Formats/Svg/SvgExporter.cs index 2970eab..4082fce 100644 --- a/src/ACadSharp.Formats/Svg/SvgExporter.cs +++ b/src/ACadSharp.Formats/Svg/SvgExporter.cs @@ -4,22 +4,37 @@ namespace ACadSharp.Formats.Svg; +/// +/// Exports CAD data to SVG (Scalable Vector Graphics) format. +/// public class SvgExporter : ExporterBase { + /// + /// Gets or sets the configuration settings for SVG export. + /// public SvgConfiguration Configuration { get; set; } = new SvgConfiguration(); private SvgDocumentBuilder _builder; + /// + /// Initializes a new instance of the class with the specified file path. + /// + /// The path to the output SVG file. public SvgExporter(string filename) : this(File.Create(filename)) { } + /// + /// Initializes a new instance of the class with the specified stream. + /// + /// The stream to write the SVG output to. public SvgExporter(Stream stream) : base(stream) { } + /// public override void Export(Layout layout) { this.createBuilder(); @@ -27,6 +42,7 @@ public override void Export(Layout layout) this._builder.WriteLayout(layout); } + /// public override void Export(BlockRecord record) { this.createBuilder(); @@ -34,6 +50,7 @@ public override void Export(BlockRecord record) this._builder.WriteBlock(record); } + /// public override void Export(CadDocument document) { this.createBuilder(); From cfad2802f1fc7b1de797d2e7b2d1b15dd4f7cae2 Mon Sep 17 00:00:00 2001 From: DomCR Date: Fri, 21 Aug 2026 17:41:27 +0200 Subject: [PATCH 08/13] submodule --- src/ACadSharp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ACadSharp b/src/ACadSharp index 3f33ac9..c3ccfb2 160000 --- a/src/ACadSharp +++ b/src/ACadSharp @@ -1 +1 @@ -Subproject commit 3f33ac9b8b51e8d7ecb72ab4b1b4385669ffc988 +Subproject commit c3ccfb21e36a8fbafb133dea6d62c1bca867dd8d From 53e76359436982dd53960792eb84ea3f46d05d32 Mon Sep 17 00:00:00 2001 From: DomCR Date: Tue, 25 Aug 2026 09:45:56 +0200 Subject: [PATCH 09/13] workflows --- .github/CONTRIBUTING.md | 26 ++++++++++ .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE/bug_report.md | 29 +++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 +++++++ .github/pull_request_template.md | 13 +++++ .github/workflows/auto-version.yml | 36 +++++++++++++ .github/workflows/coveralls.yml | 36 +++++++++++++ .github/workflows/csharp.yml | 63 +++++++++++++++++++++++ .github/workflows/doc-gen.yml | 39 ++++++++++++++ .github/workflows/publish.yml | 25 +++++++++ 10 files changed, 288 insertions(+) create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/auto-version.yml create mode 100644 .github/workflows/coveralls.yml create mode 100644 .github/workflows/csharp.yml create mode 100644 .github/workflows/doc-gen.yml create mode 100644 .github/workflows/publish.yml diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..222999c --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# Contribute to ACadSharp.Formats + +## Report bugs + +- Before creating a new issue, please ensure the bug was not already reported or there is already a similar one +by searching on GitHub under [issues](https://github.com/DomCR/ACadSharp.Formats/issues). +- Otherwise [open an issue](https://github.com/DomCR/ACadSharp.Formats/issues/new?template=bug_report.md) addressing the problem. Please try to follow the template which helps to identify the problem faster. +- If you write a snipped of code, make sure that is [correctly formatted](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks) and specify the language used in it. + +## Request a new feature + +- Use the following [template](https://github.com/DomCR/ACadSharp.Formats/issues/new?template=feature_request.md) to propose a new feature. + +## Create a Pull Request + +- If you fixed an issue or have developed a useful feature that you would like to include in the library, feel free to create a pull request, I'll give feedback as soon as I can. + +## Building + +Before building run: + +```console +git submodule update --init --recursive +``` + +This command will clone the submodules necessary to build the project. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..c010e46 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: DomCR \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..ae0c1a2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior or an example of the code used. + +**Expected behavior** +A clear and concise description of what you expected to happen if needed. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional context** +Add any other context about the problem here. + +--- + +**Release needed** + +- [ ] Yes, using the Nuget package. +- [ ] No, using the github repo. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..b98021a --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +# Description + +Short description about this PR + +# Tasks done in this PR +* [ ] Something awesome. +* [ ] An evil bug have been defeated. +* [ ] Code cleanup and maintenance has been done. + +# Related Issues / Pull Requests +- Add the links of issues or PR related to this one. +# Notes for reviewer + - Things to consider during the review of this PR. \ No newline at end of file diff --git a/.github/workflows/auto-version.yml b/.github/workflows/auto-version.yml new file mode 100644 index 0000000..0e8b2ee --- /dev/null +++ b/.github/workflows/auto-version.yml @@ -0,0 +1,36 @@ +name: Auto Version + +on: + pull_request: + types: [labeled] + +permissions: + contents: write + +env: + CURRENT_BRANCH: ${{github.event.pull_request.head.ref}} + VERSION_FORMAT: ${{ fromJSON('[ "*.^.0", "*.*.^"]')[contains(github.event.pull_request.labels.*.name, 'minor')] }} + +jobs: + update: + if: contains(github.event.pull_request.labels.*.name, 'minor') || contains(github.event.pull_request.labels.*.name, 'major') + runs-on: windows-latest + name: Update version + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + ref: ${{env.CURRENT_BRANCH}} + - name: Update version + id: update + uses: vers-one/dotnet-project-version-updater@v1.7 + with: + file: ./src/ACadSharp.Formats/ACadSharp.Formats.csproj + version: ${{env.VERSION_FORMAT}} + - name: Push changes + run: ./push-changes.sh + shell: bash + env: + VERSION: ${{steps.update.outputs.newVersion}} + BRANCH: ${{env.CURRENT_BRANCH}} diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml new file mode 100644 index 0000000..e36b665 --- /dev/null +++ b/.github/workflows/coveralls.yml @@ -0,0 +1,36 @@ +name: Coveralls + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + coveralls: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: cardinalby/export-env-action@v2 + with: + envFile: 'github.env' + - name: Build and Test ACadSharp + working-directory: src + run: | + dotnet restore + dotnet build --no-restore + dotnet test -p:CollectCoverage=true -p:CoverletOutput=TestResults/ -p:CoverletOutputFormat=lcov --no-restore --no-build --framework net9.0 --verbosity normal ACadSharp.Formats.Tests/ + - name: Build and Test Submodules + working-directory: src/ACadSharp/src/CSUtilities + run: | + dotnet restore + dotnet build --no-restore + dotnet test -p:CollectCoverage=true -p:CoverletOutput=TestResults/ -p:CoverletOutputFormat=lcov --no-restore --no-build --framework net9.0 --verbosity normal CSUtilities.Tests/ + dotnet test -p:CollectCoverage=true -p:CoverletOutput=TestResults/ -p:CoverletOutputFormat=lcov --no-restore --no-build --framework net9.0 --verbosity normal CSMath.Tests/ + - name: Coveralls action + uses: coverallsapp/github-action@v2 + with: + github-token: ${{ github.token }} + files: src/ACadSharp.Formats.Tests/TestResults/coverage.net9.0.info src/ACadSharp/src/CSUtilities/CSUtilities.Tests/TestResults/coverage.net9.0.info src/ACadSharp/src/CSUtilities/CSMath.Tests/TestResults/coverage.info \ No newline at end of file diff --git a/.github/workflows/csharp.yml b/.github/workflows/csharp.yml new file mode 100644 index 0000000..79d958d --- /dev/null +++ b/.github/workflows/csharp.yml @@ -0,0 +1,63 @@ +name: CSharp + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +defaults: + run: + working-directory: ./src + +jobs: + build: + name: Build + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: cardinalby/export-env-action@v2 + with: + envFile: 'github.env' + - name: Install dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + + testDotNet: + name: Test Dotnet + runs-on: windows-latest + needs: build + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: cardinalby/export-env-action@v2 + with: + envFile: 'github.env' + - name: Install dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test with the dotnet CLI + run: dotnet test --no-build --no-restore --framework net9.0 + + testFramework: + name: Test net Framework + runs-on: windows-latest + needs: build + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: cardinalby/export-env-action@v2 + with: + envFile: 'github.env' + - name: Install dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test with the dotnet CLI + run: dotnet test --no-build --no-restore --framework net48 \ No newline at end of file diff --git a/.github/workflows/doc-gen.yml b/.github/workflows/doc-gen.yml new file mode 100644 index 0000000..3d79b23 --- /dev/null +++ b/.github/workflows/doc-gen.yml @@ -0,0 +1,39 @@ +name: GitHub Pages Documentation + +on: + push: + branches: [ master ] + +permissions: + contents: write + +jobs: + generate: + runs-on: windows-latest + steps: + + - name: Checkout + run: git config --global core.autocrlf true + - uses: actions/checkout@v2 + with: + submodules: true + + - name: Dotnet Setup + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.x + + - name: Update Index + run: cp README.md docs/index.md + + - name: Install DocFX + run: dotnet tool install -g docfx + + - name: Build site + run: docfx docs/docfx.json + + - name: Publish to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ github.token }} + publish_dir: ./docs/_site \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..bc500ec --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,25 @@ +name: Publish NuGet package + +on: + workflow_dispatch: + release: + branches: [ master ] + +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: true + fetch-depth: 0 + - name: Publish NuGet package + working-directory: src + run: | + git submodule update --force --recursive --init --remote + dotnet restore + dotnet build --configuration Release + dotnet pack ./ACadSharp.Formats/ACadSharp.Formats.csproj --configuration Release + foreach($file in (Get-ChildItem ./nupkg -Recurse -Include *.nupkg)) { + dotnet nuget push $file --api-key "${{ secrets.NUGET_API_KEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate + } \ No newline at end of file From 55c4be157550d407c4f82c783401f4222a1acb7f Mon Sep 17 00:00:00 2001 From: DomCR Date: Tue, 25 Aug 2026 09:50:02 +0200 Subject: [PATCH 10/13] vars --- .gitignore | 1 - github.env | 4 ++++ push-changes.sh | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 github.env create mode 100644 push-changes.sh diff --git a/.gitignore b/.gitignore index d5a18de..2de098b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ *.user *.userosscache *.sln.docstates -*.env # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs diff --git a/github.env b/github.env new file mode 100644 index 0000000..830d332 --- /dev/null +++ b/github.env @@ -0,0 +1,4 @@ +SAMPLES_FOLDER: "../../../../../samples" +OUTPUT_SAMPLES_FOLDER: "../../../../../samples/out" +OUTPUT_SVG "../../../../../samples/out/svg" +OUTPUT_PDF "../../../../../samples/out/pdf" diff --git a/push-changes.sh b/push-changes.sh new file mode 100644 index 0000000..ea17880 --- /dev/null +++ b/push-changes.sh @@ -0,0 +1,16 @@ +echo Prepare commit +#Get commit details +author=`git log -1 --format="%an"` +email=`git log -1 --format="%ae"` + +git config --local user.email "$email" +git config --local user.name "$author" +git add . +if git diff-index --quiet HEAD; then + echo "Nothing changed" + exit 0 +fi + +echo "Pushing changes $BRANCH" +git commit -m "Update project version to $VERSION" +git push origin $BRANCH \ No newline at end of file From c6fc59b484d098e6d0bfd5b296b3f6e6bd650ede Mon Sep 17 00:00:00 2001 From: DomCR Date: Tue, 15 Sep 2026 12:00:22 +0200 Subject: [PATCH 11/13] test --- .github/workflows/csharp.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/csharp.yml b/.github/workflows/csharp.yml index 79d958d..a3276f7 100644 --- a/.github/workflows/csharp.yml +++ b/.github/workflows/csharp.yml @@ -16,11 +16,13 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v4 - with: + with: submodules: true - uses: cardinalby/export-env-action@v2 with: envFile: 'github.env' + - name: Setup submodules + run: git submodule update --init --recursive - name: Install dependencies run: dotnet restore - name: Build From f14009077873a586da83ea28c1f0fe739e411f1d Mon Sep 17 00:00:00 2001 From: DomCR Date: Tue, 15 Sep 2026 12:03:19 +0200 Subject: [PATCH 12/13] submodules in workflows --- .github/workflows/coveralls.yml | 2 ++ .github/workflows/csharp.yml | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml index e36b665..6bf0b8a 100644 --- a/.github/workflows/coveralls.yml +++ b/.github/workflows/coveralls.yml @@ -16,6 +16,8 @@ jobs: - uses: cardinalby/export-env-action@v2 with: envFile: 'github.env' + - name: Setup submodules + run: git submodule update --init --recursive - name: Build and Test ACadSharp working-directory: src run: | diff --git a/.github/workflows/csharp.yml b/.github/workflows/csharp.yml index a3276f7..c225db4 100644 --- a/.github/workflows/csharp.yml +++ b/.github/workflows/csharp.yml @@ -39,6 +39,8 @@ jobs: - uses: cardinalby/export-env-action@v2 with: envFile: 'github.env' + - name: Setup submodules + run: git submodule update --init --recursive - name: Install dependencies run: dotnet restore - name: Build @@ -57,6 +59,8 @@ jobs: - uses: cardinalby/export-env-action@v2 with: envFile: 'github.env' + - name: Setup submodules + run: git submodule update --init --recursive - name: Install dependencies run: dotnet restore - name: Build From 158c2ce21e06f79a1bdbb881558ce54c8ac52acd Mon Sep 17 00:00:00 2001 From: DomCR Date: Tue, 15 Sep 2026 12:04:12 +0200 Subject: [PATCH 13/13] coverage fix --- .github/workflows/coveralls.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml index 6bf0b8a..2a00bfb 100644 --- a/.github/workflows/coveralls.yml +++ b/.github/workflows/coveralls.yml @@ -35,4 +35,4 @@ jobs: uses: coverallsapp/github-action@v2 with: github-token: ${{ github.token }} - files: src/ACadSharp.Formats.Tests/TestResults/coverage.net9.0.info src/ACadSharp/src/CSUtilities/CSUtilities.Tests/TestResults/coverage.net9.0.info src/ACadSharp/src/CSUtilities/CSMath.Tests/TestResults/coverage.info \ No newline at end of file + files: src/ACadSharp.Formats.Tests/TestResults/coverage.net9.0.info src/ACadSharp.Formats.Tests/src/CSUtilities/CSUtilities.Tests/TestResults/coverage.net9.0.info src/ACadSharp.Formats.Tests/src/CSUtilities/CSMath.Tests/TestResults/coverage.info \ No newline at end of file