• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DomCR / ACadSharp / 18588619962

17 Oct 2025 09:30AM UTC coverage: 78.11% (+0.003%) from 78.107%
18588619962

push

github

web-flow
Merge pull request #826 from DomCR/svg-text

Svg text

6877 of 9561 branches covered (71.93%)

Branch coverage included in aggregate %.

3 of 12 new or added lines in 2 files covered. (25.0%)

3 existing lines in 1 file now uncovered.

26475 of 33138 relevant lines covered (79.89%)

108149.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.43
/src/ACadSharp/IO/SVG/SvgXmlWriter.cs
1
using ACadSharp.Entities;
2
using ACadSharp.Extensions;
3
using ACadSharp.Objects;
4
using ACadSharp.Tables;
5
using ACadSharp.Types.Units;
6
using CSMath;
7
using CSUtilities.Extensions;
8
using System;
9
using System.Collections.Generic;
10
using System.Globalization;
11
using System.IO;
12
using System.Linq;
13
using System.Text;
14
using System.Xml;
15

16
namespace ACadSharp.IO.SVG
17
{
18
        internal class SvgXmlWriter : XmlTextWriter
19
        {
20
                public event NotificationEventHandler OnNotification;
21

22
                public SvgConfiguration Configuration { get; } = new();
215✔
23

24
                public Layout Layout { get; set; }
195✔
25

26
                public UnitsType Units { get; protected set; }
12,361✔
27

28
                public SvgXmlWriter(Stream stream, SvgConfiguration configuration) : this(stream, null, configuration)
×
29
                {
×
30
                }
×
31

32
                public SvgXmlWriter(Stream stream, Encoding? encoding, SvgConfiguration configuration) : base(stream, encoding)
8✔
33
                {
8✔
34
                        this.Configuration = configuration;
8✔
35
                }
8✔
36

37
                public void WriteAttributeString(string localName, double value)
38
                {
51✔
39
                        this.WriteAttributeString(localName, value, this.Units);
51✔
40
                }
51✔
41

42
                public void WriteAttributeString(string localName, double value, UnitsType units)
43
                {
67✔
44
                        this.WriteAttributeString(localName, value.ToSvg(units));
67✔
45
                }
67✔
46

47
                public void WriteBlock(BlockRecord record)
48
                {
1✔
49
                        this.Units = record.Units;
1✔
50

51
                        BoundingBox box = record.GetBoundingBox();
1✔
52

53
                        this.startDocument(box, box, this.Units);
1✔
54

55
                        foreach (var e in record.Entities)
47✔
56
                        {
22✔
57
                                this.writeEntity(e);
22✔
58
                        }
22✔
59

60
                        this.endDocument();
1✔
61
                }
1✔
62

63
                public void WriteLayout(Layout layout)
64
                {
7✔
65
                        this.Layout = layout;
7✔
66
                        this.Units = layout.PaperUnits.ToUnits();
7✔
67

68
                        double paperWidth = layout.PaperWidth;
7✔
69
                        double paperHeight = layout.PaperHeight;
7✔
70

71
                        switch (layout.PaperRotation)
7✔
72
                        {
73
                                case PlotRotation.Degrees90:
74
                                case PlotRotation.Degrees270:
75
                                        paperWidth = layout.PaperHeight;
5✔
76
                                        paperHeight = layout.PaperWidth;
5✔
77
                                        break;
5✔
78
                        }
79

80
                        XYZ lowerCorner = XYZ.Zero;
7✔
81
                        XYZ upperCorner = new XYZ(paperWidth, paperHeight, 0.0);
7✔
82
                        BoundingBox paper = new BoundingBox(lowerCorner, upperCorner);
7✔
83

84
                        XYZ lowerMargin = layout.UnprintableMargin.BottomLeftCorner.Convert<XYZ>();
7✔
85
                        XYZ upperMargin = upperCorner - layout.UnprintableMargin.TopCorner.Convert<XYZ>();
7✔
86
                        BoundingBox margins = new BoundingBox(
7✔
87
                                lowerMargin,
7✔
88
                                upperMargin);
7✔
89

90
                        this.startDocument(paper, null, UnitsType.Millimeters);
7✔
91

92
                        Transform transform = new Transform(
7✔
93
                                lowerMargin.ToPixelSize(UnitsType.Millimeters),
7✔
94
                                new XYZ(layout.PrintScale),
7✔
95
                                XYZ.Zero);
7✔
96

97
                        foreach (var e in layout.AssociatedBlock.Entities)
347✔
98
                        {
163✔
99
                                this.writeEntity(e, transform);
163✔
100
                        }
163✔
101

102
                        this.endDocument();
7✔
103
                }
7✔
104

105
                protected void notify(string message, NotificationType type, Exception ex = null)
106
                {
10✔
107
                        this.OnNotification?.Invoke(this, new NotificationEventArgs(message, type, ex));
10!
108
                }
10✔
109

110
                protected void triggerNotification(object sender, NotificationEventArgs e)
111
                {
×
112
                        this.OnNotification?.Invoke(sender, e);
×
113
                }
×
114

115
                protected void writeEntity(Entity entity, Transform transform)
116
                {
186✔
117
                        this.WriteComment($"{entity.ObjectName} | {entity.Handle}");
186✔
118

119
                        switch (entity)
186✔
120
                        {
121
                                case Arc arc:
122
                                        this.writeArc(arc, transform);
9✔
123
                                        break;
9✔
124
                                case Line line:
125
                                        this.writeLine(line, transform);
84✔
126
                                        break;
84✔
127
                                case Point point:
128
                                        this.writePoint(point, transform);
1✔
129
                                        break;
1✔
130
                                case Circle circle:
131
                                        this.writeCircle(circle, transform);
8✔
132
                                        break;
8✔
133
                                case Ellipse ellipse:
134
                                        this.writeEllipse(ellipse, transform);
11✔
135
                                        break;
11✔
136
                                case Hatch hatch:
137
                                        this.writeHatch(hatch, transform);
14✔
138
                                        break;
14✔
139
                                case Insert insert:
140
                                        this.writeInsert(insert, transform);
1✔
141
                                        break;
1✔
142
                                case IPolyline polyline:
143
                                        this.writePolyline(polyline, transform);
22✔
144
                                        break;
22✔
145
                                case IText text:
146
                                        this.writeText(text, transform);
26✔
147
                                        break;
26✔
148
                                //case Spline spline:
149
                                //        this.writeSpline(spline, transform);
150
                                //        break;
151
                                default:
152
                                        this.notify($"[{entity.ObjectName}] Entity not implemented.", NotificationType.NotImplemented);
10✔
153
                                        break;
10✔
154
                        }
155
                }
186✔
156

157
                private string colorSvg(Color color)
158
                {
180✔
159
                        if (this.Layout != null && color.Equals(Color.Default))
180✔
160
                        {
136✔
161
                                color = Color.Black;
136✔
162
                        }
136✔
163

164
                        return $"rgb({color.R},{color.G},{color.B})";
180✔
165
                }
180✔
166

167
                private string createPath(params IEnumerable<IPolyline> polylines)
168
                {
14✔
169
                        StringBuilder sb = new StringBuilder();
14✔
170

171
                        foreach (var item in polylines)
78✔
172
                        {
18✔
173
                                var pts = item.GetPoints<XY>().ToArray();
18✔
174
                                if (!pts.Any())
18!
175
                                {
×
176
                                        continue;
×
177
                                }
178

179
                                var pt = pts[0];
18✔
180
                                sb.Append($"M {pt.ToPixelSize(this.Units).ToSvg()} ");
18✔
181
                                for (int i = 1; i < pts.Length; i++)
7,788✔
182
                                {
3,876✔
183
                                        pt = pts[i];
3,876✔
184
                                        sb.Append($"L {pt.ToPixelSize(this.Units).ToSvg()} ");
3,876✔
185
                                }
3,876✔
186

187
                                if (item.IsClosed)
18!
188
                                {
×
189
                                        sb.Append("Z");
×
190
                                }
×
191
                        }
18✔
192

193
                        return sb.ToString();
14✔
194
                }
14✔
195

196
                private bool drawableLineType(IEntity entity)
197
                {
149✔
198
                        var lineType = entity.GetActiveLineType();
149✔
199
                        return lineType.IsComplex && !lineType.Segments.Any(s => s.IsShape);
259✔
200
                }
149✔
201

202
                private void endDocument()
203
                {
8✔
204
                        this.WriteEndElement();
8✔
205
                        this.WriteEndDocument();
8✔
206
                        this.Close();
8✔
207
                }
8✔
208

209
                private double getPointSize(IEntity entity)
210
                {
26✔
211
                        return entity.GetActiveLineWeightType().GetLineWeightValue().ToPixelSize(this.Units);
26✔
212
                }
26✔
213

214
                private void startDocument(BoundingBox box, BoundingBox? viewBox, UnitsType units)
215
                {
8✔
216
                        this.WriteStartDocument();
8✔
217

218
                        this.WriteStartElement("svg");
8✔
219
                        this.WriteAttributeString("xmlns", "http://www.w3.org/2000/svg");
8✔
220

221
                        this.WriteAttributeString("width", box.Max.X - box.Min.X, units);
8✔
222
                        this.WriteAttributeString("height", box.Max.Y - box.Min.Y, units);
8✔
223

224
                        if (viewBox.HasValue)
8✔
225
                        {
1✔
226
                                var vb = viewBox.Value;
1✔
227
                                this.WriteStartAttribute("viewBox");
1✔
228
                                this.WriteValue(vb.Min.X.ToPixelSize(units));
1✔
229
                                this.WriteValue(" ");
1✔
230
                                this.WriteValue(vb.Min.Y.ToPixelSize(units));
1✔
231
                                this.WriteValue(" ");
1✔
232
                                this.WriteValue((vb.Width).ToPixelSize(units));
1✔
233
                                this.WriteValue(" ");
1✔
234
                                this.WriteValue((vb.Height).ToPixelSize(units));
1✔
235
                                this.WriteEndAttribute();
1✔
236
                        }
1✔
237

238
                        this.WriteAttributeString("transform", $"scale(1,-1)");
8✔
239

240
                        if (this.Layout != null)
8✔
241
                        {
7✔
242
                                this.WriteAttributeString("style", "background-color:white");
7✔
243
                        }
7✔
244
                }
8✔
245

246
                private string svgPoints<T>(IEnumerable<T> points, Transform transform)
247
                        where T : IVector, new()
248
                {
30✔
249
                        if (!points.Any())
30!
250
                        {
×
251
                                return string.Empty;
×
252
                        }
253

254
                        StringBuilder sb = new StringBuilder();
30✔
255
                        sb.Append(points.First().ToPixelSize(this.Units).ToSvg());
30✔
256
                        foreach (T point in points.Skip(1))
15,012✔
257
                        {
7,461✔
258
                                sb.Append(' ');
7,461✔
259
                                sb.Append(point.ToPixelSize(this.Units).ToSvg());
7,461✔
260
                        }
7,461✔
261

262
                        return sb.ToString();
30✔
263
                }
30✔
264

265
                private void writeArc(Arc arc, Transform transform)
266
                {
9✔
267
                        this.WriteStartElement("path");
9✔
268

269
                        this.writeEntityHeader(arc, transform);
9✔
270

271
                        //A rx ry rotation large-arc-flag sweep-flag x y
272

273
                        arc.GetEndVertices(out XYZ start, out XYZ end);
9✔
274
                        var largeArc = Math.Abs(arc.Sweep) > MathHelper.PI ? 1 : 0;
9✔
275
                        this.WriteAttributeString("d", $"M {start.ToPixelSize(this.Units).ToSvg()} A {arc.Radius} {arc.Radius} {0} {largeArc} {1} {end.ToPixelSize(this.Units).ToSvg()}");
9✔
276

277
                        this.WriteAttributeString("fill", "none");
9✔
278

279
                        this.WriteEndElement();
9✔
280
                }
9✔
281

282
                private void writeCircle(Circle circle, Transform transform)
283
                {
8✔
284
                        var loc = transform.ApplyTransform(circle.Center);
8✔
285

286
                        this.WriteStartElement("circle");
8✔
287

288
                        this.writeEntityHeader(circle, transform);
8✔
289

290
                        this.WriteAttributeString("r", circle.Radius);
8✔
291
                        this.WriteAttributeString("cx", loc.X);
8✔
292
                        this.WriteAttributeString("cy", loc.Y);
8✔
293

294
                        this.WriteAttributeString("fill", "none");
8✔
295

296
                        this.WriteEndElement();
8✔
297
                }
8✔
298

299
                private void writeDashes(IEnumerable<double> dashes)
300
                {
4✔
301
                        StringBuilder sb = new StringBuilder();
4✔
302

303
                        foreach (var d in dashes)
32✔
304
                        {
10✔
305
                                sb.Append(Math.Abs(d.ToPixelSize(this.Units)));
10✔
306
                                sb.Append(' ');
10✔
307
                        }
10✔
308

309
                        this.WriteAttributeString("stroke-dasharray", sb.ToString().Trim());
4✔
310
                }
4✔
311

312
                private void writeDashes(LineType lineType, double pointSize)
313
                {
26✔
314
                        StringBuilder sb = new StringBuilder();
26✔
315
                        foreach (LineType.Segment segment in lineType.Segments)
266✔
316
                        {
94✔
317
                                if (segment.IsPoint)
94✔
318
                                {
13✔
319
                                        sb.Append(pointSize.ToPixelSize(this.Units));
13✔
320
                                }
13✔
321
                                else
322
                                {
81✔
323
                                        sb.Append(Math.Abs(segment.Length.ToPixelSize(this.Units)));
81✔
324
                                }
81✔
325

326
                                sb.Append(' ');
94✔
327
                        }
94✔
328

329
                        this.WriteAttributeString("stroke-dasharray", sb.ToString().Trim());
26✔
330
                }
26✔
331

332
                private void writeEllipse(Ellipse ellipse, Transform transform)
333
                {
11✔
334
                        if (ellipse.IsFullEllipse)
11✔
335
                        {
3✔
336
                                this.WriteStartElement("path");
3✔
337

338
                                this.writeEntityHeader(ellipse, transform);
3✔
339

340
                                StringBuilder sb = new StringBuilder();
3✔
341

342
                                XYZ start = ellipse.PolarCoordinateRelativeToCenter(0);
3✔
343
                                XYZ end = ellipse.PolarCoordinateRelativeToCenter(Math.PI);
3✔
344

345
                                sb.Append($"M {start.ToPixelSize(this.Units).ToSvg()} ");
3✔
346
                                sb.Append($"A {ellipse.MajorAxis / 2} {ellipse.MinorAxis / 2} {MathHelper.RadToDeg(ellipse.Rotation)} {0} {1} {end.ToPixelSize(this.Units).ToSvg()} ");
3✔
347

348
                                start = ellipse.PolarCoordinateRelativeToCenter(Math.PI);
3✔
349
                                end = ellipse.PolarCoordinateRelativeToCenter(MathHelper.TwoPI);
3✔
350
                                sb.Append($"A {ellipse.MajorAxis / 2} {ellipse.MinorAxis / 2} {MathHelper.RadToDeg(ellipse.Rotation)} {0} {1} {end.ToPixelSize(this.Units).ToSvg()}");
3✔
351

352
                                //A rx ry rotation large-arc-flag sweep-flag x y
353
                                this.WriteAttributeString("d", sb.ToString());
3✔
354

355
                                this.WriteAttributeString("fill", "none");
3✔
356
                                this.WriteEndElement();
3✔
357
                        }
3✔
358
                        else
359
                        {
8✔
360
                                this.WriteStartElement("polyline");
8✔
361

362
                                this.writeEntityHeader(ellipse, transform);
8✔
363

364
                                var vertices = ellipse.PolygonalVertexes(256);
8✔
365
                                string pts = this.svgPoints(vertices, transform);
8✔
366
                                this.WriteAttributeString("points", pts);
8✔
367
                                this.WriteAttributeString("fill", "none");
8✔
368

369
                                this.WriteEndElement();
8✔
370

371
                                return;
8✔
372

373
                                //TODO: Fix the ellipse generation
374
                                this.WriteStartElement("path");
375

376
                                this.writeEntityHeader(ellipse, transform);
377

378
                                ellipse.GetEndVertices(out XYZ start, out XYZ end);
379

380
                                //A rx ry rotation large-arc-flag sweep-flag x y
381
                                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()}");
382

383
                                this.WriteAttributeString("fill", "none");
384
                                this.WriteEndElement();
385
                        }
386
                }
11✔
387

388
                private void writeEntity(Entity entity)
389
                {
23✔
390
                        this.writeEntity(entity, new Transform());
23✔
391
                }
23✔
392

393
                private void writeEntityAsPath<T>(Entity entity, Transform transform, params IEnumerable<T> points)
394
                        where T : IVector
395
                {
×
396
                        //Will be needed to write the linetypes that use shapes
397
                        double pointSize = this.getPointSize(entity);
×
398
                        var lines = entity.GetActiveLineType().CreateLineTypeShape(pointSize, points);
×
399

400
                        this.WriteStartElement("path");
×
401

402
                        this.writeEntityHeader(entity, transform);
×
403

404
                        this.WriteAttributeString("d", this.createPath(lines));
×
405

406
                        this.WriteEndElement();
×
407
                }
×
408

409
                private void writeEntityHeader(IEntity entity, Transform transform, bool drawStroke = true)
410
                {
149✔
411
                        Color color = entity.GetActiveColor();
149✔
412

413
                        this.WriteAttributeString("vector-effect", "non-scaling-stroke");
149✔
414

415
                        if (drawStroke)
149✔
416
                        {
135✔
417
                                this.WriteAttributeString("stroke", this.colorSvg(color));
135✔
418
                        }
135✔
419
                        else
420
                        {
14✔
421
                                this.WriteAttributeString("stroke", "none");
14✔
422
                        }
14✔
423

424
                        var lineWeight = entity.GetActiveLineWeightType();
149✔
425
                        this.WriteAttributeString("stroke-width", $"{this.Configuration.GetLineWeightValue(lineWeight, this.Units).ToSvg(UnitsType.Millimeters)}");
149!
426

427
                        this.writeTransform(transform);
149✔
428

429
                        if (this.drawableLineType(entity))
149✔
430
                        {
26✔
431
                                this.writeDashes(entity.GetActiveLineType(), this.getPointSize(entity));
26✔
432
                        }
26✔
433
                }
149✔
434

435
                private void writeHatch(Hatch hatch, Transform transform)
436
                {
14✔
437
                        this.WriteStartElement("g");
14✔
438

439
                        var patternId = this.writePattern(hatch);
14✔
440

441
                        List<Polyline3D> plines = new List<Polyline3D>();
14✔
442
                        foreach (Hatch.BoundaryPath path in hatch.Paths)
78✔
443
                        {
18✔
444
                                var pline = new Polyline3D(path.GetPoints(this.Configuration.ArcPoints));
18✔
445
                                plines.Add(pline);
18✔
446
                        }
18✔
447

448
                        this.WriteStartElement("path");
14✔
449

450
                        this.writeEntityHeader(hatch, transform, drawStroke: false);
14✔
451

452
                        this.WriteAttributeString("d", this.createPath(plines));
14✔
453

454
                        this.WriteAttributeString("fill", $"url(#{patternId})");
14✔
455

456
                        this.WriteEndElement();
14✔
457

458
                        this.WriteEndElement();
14✔
459
                }
14✔
460

461
                private void writePatternHeader(string id)
462
                {
17✔
463
                        this.WriteStartElement("pattern");
17✔
464

465
                        this.WriteAttributeString("id", id);
17✔
466
                        this.WriteAttributeString("patternUnits", "userSpaceOnUse");
17✔
467
                }
17✔
468

469
                private string writePatternHeader(Hatch hatch)
470
                {
14✔
471
                        string id = $"{hatch.Pattern.GetHashCode()}_{hatch.Pattern.Name}";
14✔
472

473
                        this.WriteStartElement("pattern");
14✔
474

475
                        this.WriteAttributeString("id", id);
14✔
476
                        this.WriteAttributeString("patternUnits", "userSpaceOnUse");
14✔
477

478
                        return id;
14✔
479
                }
14✔
480

481
                private string writeSolidPattern(Hatch hatch)
482
                {
1✔
483
                        string id = this.writePatternHeader(hatch);
1✔
484

485
                        this.WriteAttributeString("width", "100%");
1✔
486
                        this.WriteAttributeString("height", "100%");
1✔
487

488
                        this.WriteStartElement("rect");
1✔
489

490
                        this.WriteAttributeString("width", "100%");
1✔
491
                        this.WriteAttributeString("height", "100%");
1✔
492
                        this.WriteAttributeString("fill", this.colorSvg(hatch.Color));
1✔
493

494
                        //rect
495
                        this.WriteEndElement();
1✔
496

497
                        //pattern
498
                        this.WriteEndElement();
1✔
499

500
                        return id;
1✔
501
                }
1✔
502

503
                private string writePattern(Hatch hatch)
504
                {
14✔
505
                        if (hatch.IsSolid)
14✔
506
                        {
1✔
507
                                return this.writeSolidPattern(hatch);
1✔
508
                        }
509

510
                        Dictionary<string, BoundingBox> patterns = new();
13✔
511
                        foreach (var item in hatch.Pattern.Lines)
73✔
512
                        {
17✔
513
                                var i = $"{item.GetHashCode()}_line";
17✔
514
                                patterns.Add(i, new BoundingBox(XYZ.Zero, new XYZ(item.LineOffset, item.LineOffset, 0)));
17✔
515

516
                                //Each line works individually repeating itself every offset
517
                                this.writePatternHeader(i);
17✔
518
                                this.WriteAttributeString("width", item.LineOffset.ToSvg(this.Units));
17✔
519
                                this.WriteAttributeString("height", item.LineOffset.ToSvg(this.Units));
17✔
520

521
                                this.writeTransform(name: "patternTransform",
17✔
522
                                        translation: item.BasePoint.Convert<XYZ>().ToPixelSize(this.Units));
17✔
523
                                //rotation: -item.Angle);
524

525
                                this.WriteStartElement("line");
17✔
526

527
                                //Direction of the line
528
                                var length = item.Offset.GetLength();
17✔
529
                                double x = MathHelper.Cos(item.Angle) * 10;
17✔
530
                                double y = MathHelper.Sin(item.Angle) * 10;
17✔
531

532
                                //Offset -> is the size of the line box
533

534
                                //Add BasePoint
535
                                this.WriteAttributeString("x1", 0.0d.ToSvg(this.Units));
17✔
536
                                this.WriteAttributeString("y1", 0.0d.ToSvg(this.Units));
17✔
537
                                this.WriteAttributeString("x2", (x).ToSvg(this.Units));
17✔
538
                                this.WriteAttributeString("y2", (y).ToSvg(this.Units));
17✔
539

540
                                //Rotate the pattern after line
541
                                //this.WriteAttributeString("x1", 0.0d.ToSvg(this.Units));
542
                                //this.WriteAttributeString("y1", (item.LineOffset / 2).ToSvg(this.Units));
543
                                //this.WriteAttributeString("x2", 1.0d.ToSvg(this.Units));
544
                                //this.WriteAttributeString("y2", (item.LineOffset / 2).ToSvg(this.Units));
545

546
                                this.WriteAttributeString("stroke", this.colorSvg(hatch.GetActiveColor()));
17✔
547
                                this.WriteAttributeString("stroke-width", $"{this.Configuration.GetLineWeightValue(hatch.GetActiveLineWeightType(), this.Units).ToSvg(UnitsType.Millimeters)}");
17!
548

549
                                if (item.DashLengths.Any())
17✔
550
                                {
4✔
551
                                        this.writeDashes(item.DashLengths);
4✔
552
                                }
4✔
553

554
                                //Line
555
                                this.WriteEndElement();
17✔
556

557
                                if (false)
17✔
558
                                {
559
                                        this.WriteStartElement("rect");
560
                                        this.WriteAttributeString("width", (item.LineOffset).ToSvg(this.Units));
561
                                        this.WriteAttributeString("height", (item.LineOffset).ToSvg(this.Units));
562
                                        this.WriteAttributeString("fill", $"none");
563
                                        this.WriteAttributeString("stroke", $"red");
564
                                        this.WriteEndElement();
565
                                }
566

567
                                //Pattern
568
                                this.WriteEndElement();
17✔
569
                        }
17✔
570

571
                        string id = this.writePatternHeader(hatch);
13✔
572
                        var width = patterns.Values.Max(w => w.Width);
30✔
573
                        var height = patterns.Values.Max(w => w.Height);
30✔
574

575
                        this.WriteAttributeString("width", width.ToSvg(this.Units));
13✔
576
                        this.WriteAttributeString("height", height.ToSvg(this.Units));
13✔
577

578
                        foreach (var item in patterns)
73✔
579
                        {
17✔
580
                                this.WriteStartElement("rect");
17✔
581
                                this.WriteAttributeString("width", (item.Value.Width).ToSvg(this.Units));
17✔
582
                                this.WriteAttributeString("height", (item.Value.Height).ToSvg(this.Units));
17✔
583
                                this.WriteAttributeString("fill", $"url(#{item.Key})");
17✔
584
                                this.WriteEndElement();
17✔
585
                        }
17✔
586

587
                        //pattern
588
                        this.WriteEndElement();
13✔
589

590
                        return id;
13✔
591
                }
14✔
592

593
                private void writeInsert(Insert insert, Transform transform)
594
                {
1✔
595
                        var insertTransform = insert.GetTransform();
1✔
596
                        var merged = new Transform(transform.Matrix * insertTransform.Matrix);
1✔
597

598
                        this.WriteStartElement("g");
1✔
599
                        this.writeTransform(merged);
1✔
600

601
                        foreach (var e in insert.Block.Entities)
5✔
602
                        {
1✔
603
                                this.writeEntity(e);
1✔
604
                        }
1✔
605

606
                        this.WriteEndElement();
1✔
607
                }
1✔
608

609
                private void writeLine(Line line, Transform transform)
610
                {
84✔
611
                        this.WriteStartElement("line");
84✔
612

613
                        this.writeEntityHeader(line, transform);
84✔
614

615
                        this.WriteAttributeString("x1", line.StartPoint.X.ToSvg(this.Units));
84✔
616
                        this.WriteAttributeString("y1", line.StartPoint.Y.ToSvg(this.Units));
84✔
617
                        this.WriteAttributeString("x2", line.EndPoint.X.ToSvg(this.Units));
84✔
618
                        this.WriteAttributeString("y2", line.EndPoint.Y.ToSvg(this.Units));
84✔
619

620
                        this.WriteEndElement();
84✔
621
                }
84✔
622

623
                private void writePoint(Point point, Transform transform)
624
                {
1✔
625
                        this.WriteStartElement("circle");
1✔
626

627
                        this.writeEntityHeader(point, transform);
1✔
628

629
                        this.WriteAttributeString("r", this.Configuration.PointRadius);
1✔
630
                        this.WriteAttributeString("cx", point.Location.X);
1✔
631
                        this.WriteAttributeString("cy", point.Location.Y);
1✔
632

633
                        this.WriteAttributeString("fill", this.colorSvg(point.GetActiveColor()));
1✔
634

635
                        this.WriteEndElement();
1✔
636
                }
1✔
637

638
                private void writePolyline(IPolyline polyline, Transform transform)
639
                {
22✔
640
                        if (polyline.IsClosed)
22✔
641
                        {
15✔
642
                                this.WriteStartElement("polygon");
15✔
643
                        }
15✔
644
                        else
645
                        {
7✔
646
                                this.WriteStartElement("polyline");
7✔
647
                        }
7✔
648

649
                        this.writeEntityHeader(polyline, transform);
22✔
650

651
                        string pts = this.svgPoints(polyline.GetPoints<XY>(this.Configuration.ArcPoints), transform);
22✔
652

653
                        this.WriteAttributeString("points", pts);
22✔
654
                        this.WriteAttributeString("fill", "none");
22✔
655

656
                        this.WriteEndElement();
22✔
657
                }
22✔
658

659
                private void writeText(IText text, Transform transform)
660
                {
26✔
661
                        XYZ insert;
662

663
                        if (text is TextEntity lineText
26✔
664
                                && (lineText.HorizontalAlignment != TextHorizontalAlignment.Left
26✔
665
                                || lineText.VerticalAlignment != TextVerticalAlignmentType.Baseline)
26✔
666
                                && !(lineText.HorizontalAlignment == TextHorizontalAlignment.Fit
26✔
667
                                || lineText.HorizontalAlignment == TextHorizontalAlignment.Aligned))
26✔
668
                        {
12✔
669
                                insert = lineText.AlignmentPoint;
12✔
670
                        }
12✔
671
                        else
672
                        {
14✔
673
                                insert = text.InsertPoint;
14✔
674
                        }
14✔
675

676
                        this.WriteStartElement("g");
26✔
677
                        this.writeTransform(transform);
26✔
678

679
                        this.WriteStartElement("text");
26✔
680

681
                        this.writeTransform(translation: insert.ToPixelSize(this.Units), scale: new XYZ(1, -1, 0), rotation: text.Rotation != 0 ? text.Rotation : null);
26✔
682

683
                        this.WriteAttributeString("fill", this.colorSvg(text.GetActiveColor()));
26✔
684

685
                        //<text x="20" y="35" class="small">My</text>
686
                        this.WriteStartAttribute("style");
26✔
687
                        this.WriteValue("font:");
26✔
688
                        this.WriteValue(text.Height.ToSvg(this.Units));
26✔
689
                        if (this.Units == UnitsType.Unitless)
26✔
690
                        {
4✔
691
                                this.WriteValue("px");
4✔
692
                        }
4✔
693

694
                        if(text.Style.TrueType.HasFlag(FontFlags.Bold))
26!
NEW
695
                        {
×
NEW
696
                                this.WriteValue("bold");
×
NEW
697
                        }
×
698

699
                        if (text.Style.TrueType.HasFlag(FontFlags.Italic))
26!
NEW
700
                        {
×
NEW
701
                                this.WriteValue("italic");
×
NEW
702
                        }
×
703

704
                        this.WriteValue(" ");
26✔
705
                        this.WriteValue(Path.GetFileNameWithoutExtension(text.Style.Filename));
26✔
706
                        this.WriteEndAttribute();
26✔
707

708
                        switch (text)
26✔
709
                        {
710
                                case MText mtext:
711
                                        switch (mtext.AttachmentPoint)
5!
712
                                        {
713
                                                case AttachmentPointType.TopLeft:
714
                                                        this.WriteAttributeString("alignment-baseline", "hanging");
4✔
715
                                                        this.WriteAttributeString("text-anchor", "start");
4✔
716
                                                        break;
4✔
717
                                                case AttachmentPointType.TopCenter:
718
                                                        this.WriteAttributeString("alignment-baseline", "hanging");
×
719
                                                        this.WriteAttributeString("text-anchor", "middle");
×
720
                                                        break;
×
721
                                                case AttachmentPointType.TopRight:
722
                                                        this.WriteAttributeString("alignment-baseline", "hanging");
1✔
723
                                                        this.WriteAttributeString("text-anchor", "end");
1✔
724
                                                        break;
1✔
725
                                                case AttachmentPointType.MiddleLeft:
726
                                                        this.WriteAttributeString("alignment-baseline", "middle");
×
727
                                                        this.WriteAttributeString("text-anchor", "start");
×
728
                                                        break;
×
729
                                                case AttachmentPointType.MiddleCenter:
730
                                                        this.WriteAttributeString("alignment-baseline", "middle");
×
731
                                                        this.WriteAttributeString("text-anchor", "middle");
×
732
                                                        break;
×
733
                                                case AttachmentPointType.MiddleRight:
734
                                                        this.WriteAttributeString("alignment-baseline", "middle");
×
735
                                                        this.WriteAttributeString("text-anchor", "end");
×
736
                                                        break;
×
737
                                                case AttachmentPointType.BottomLeft:
738
                                                        this.WriteAttributeString("alignment-baseline", "baseline");
×
739
                                                        this.WriteAttributeString("text-anchor", "start");
×
740
                                                        break;
×
741
                                                case AttachmentPointType.BottomCenter:
742
                                                        this.WriteAttributeString("alignment-baseline", "baseline");
×
743
                                                        this.WriteAttributeString("text-anchor", "middle");
×
744
                                                        break;
×
745
                                                case AttachmentPointType.BottomRight:
746
                                                        this.WriteAttributeString("alignment-baseline", "baseline");
×
747
                                                        this.WriteAttributeString("text-anchor", "end");
×
748
                                                        break;
×
749
                                                default:
750
                                                        break;
×
751
                                        }
752

753
                                        foreach (var item in mtext.GetTextLines())
53✔
754
                                        {
19✔
755
                                                this.WriteStartElement("tspan");
19✔
756
                                                this.WriteAttributeString("x", 0);
19✔
757
                                                this.WriteAttributeString("dy", "1em");
19✔
758
                                                this.WriteString(item);
19✔
759
                                                this.WriteEndElement();
19✔
760
                                        }
19✔
761

762
                                        //Line to avoid the strange offset at the end
763
                                        this.WriteStartElement("tspan");
5✔
764
                                        this.WriteAttributeString("x", 0);
5✔
765
                                        this.WriteAttributeString("dy", "1em");
5✔
766
                                        this.WriteAttributeString("visibility", "hidden");
5✔
767
                                        this.WriteString(".");
5✔
768
                                        this.WriteEndElement();
5✔
769
                                        break;
5✔
770
                                case TextEntity textEntity:
771

772
                                        switch (textEntity.HorizontalAlignment)
21✔
773
                                        {
774
                                                case TextHorizontalAlignment.Left:
775
                                                        this.WriteAttributeString("text-anchor", "start");
10✔
776
                                                        break;
10✔
777
                                                case TextHorizontalAlignment.Middle:
778
                                                case TextHorizontalAlignment.Center:
779
                                                        this.WriteAttributeString("text-anchor", "middle");
5✔
780
                                                        break;
5✔
781
                                                case TextHorizontalAlignment.Right:
782
                                                        this.WriteAttributeString("text-anchor", "end");
4✔
783
                                                        break;
4✔
784
                                        }
785

786
                                        switch (textEntity.VerticalAlignment)
21✔
787
                                        {
788
                                                case TextVerticalAlignmentType.Baseline:
789
                                                case TextVerticalAlignmentType.Bottom:
790
                                                        this.WriteAttributeString("alignment-baseline", "baseline");
15✔
791
                                                        break;
15✔
792
                                                case TextVerticalAlignmentType.Middle:
793
                                                        this.WriteAttributeString("alignment-baseline", "middle");
3✔
794
                                                        break;
3✔
795
                                                case TextVerticalAlignmentType.Top:
796
                                                        this.WriteAttributeString("alignment-baseline", "hanging");
3✔
797
                                                        break;
3✔
798
                                        }
799

800
                                        this.WriteString(text.Value);
21✔
801
                                        break;
21✔
802
                        }
803

804
                        this.WriteEndElement();
26✔
805
                        this.WriteEndElement();
26✔
806
                }
26✔
807

808
                private void writeSpline(Spline spline, Transform transform)
809
                {
×
810
                        spline.UpdateFromFitPoints();
×
811
                        this.writeEntityAsPath(spline, transform, spline.PolygonalVertexes(this.Configuration.ArcPoints));
×
812
                }
×
813

814
                private void writeTransform(Transform transform)
815
                {
176✔
816
                        XYZ? translation = transform.Translation != XYZ.Zero ? transform.Translation : null;
176✔
817
                        XYZ? scale = transform.Scale != new XYZ(1) ? transform.Scale : null;
176✔
818
                        double? rotation = transform.EulerRotation.Z != 0 ? transform.EulerRotation.Z : null;
176!
819

820
                        this.writeTransform(translation: translation, scale: scale, rotation: rotation);
176✔
821
                }
176✔
822

823
                private void writeTransform(string name = "transform", XYZ? translation = null, XYZ? scale = null, double? rotation = null)
824
                {
219✔
825
                        StringBuilder sb = new StringBuilder();
219✔
826

827
                        if (translation.HasValue)
219✔
828
                        {
105✔
829
                                var t = translation.Value;
105✔
830

831
                                sb.Append($"translate(");
105✔
832
                                sb.Append($"{t.X.ToString(CultureInfo.InvariantCulture)},");
105✔
833
                                sb.Append($"{t.Y.ToString(CultureInfo.InvariantCulture)})");
105✔
834
                        }
105✔
835

836
                        if (scale.HasValue)
219✔
837
                        {
83✔
838
                                var s = scale.Value;
83✔
839

840
                                sb.Append($"scale(");
83✔
841
                                sb.Append($"{s.X.ToString(CultureInfo.InvariantCulture)},");
83✔
842
                                sb.Append($"{s.Y.ToString(CultureInfo.InvariantCulture)})");
83✔
843
                        }
83✔
844

845
                        if (rotation.HasValue)
219✔
846
                        {
2✔
847
                                var r = -MathHelper.RadToDeg(rotation.Value);
2✔
848

849
                                sb.Append($"rotate(");
2✔
850
                                sb.Append($"{r.ToString(CultureInfo.InvariantCulture)})");
2✔
851
                        }
2✔
852

853
                        if (sb.ToString().IsNullOrEmpty())
219✔
854
                        {
108✔
855
                                return;
108✔
856
                        }
857

858
                        this.WriteAttributeString(name, sb.ToString());
111✔
859
                }
219✔
860
        }
861
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc