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

DomCR / ACadSharp / 16495641503

24 Jul 2025 11:28AM UTC coverage: 75.253% (+0.08%) from 75.178%
16495641503

Pull #721

github

web-flow
Merge bde129f74 into aea407c1e
Pull Request #721: SVG paper units

6049 of 8832 branches covered (68.49%)

Branch coverage included in aggregate %.

55 of 70 new or added lines in 5 files covered. (78.57%)

2 existing lines in 1 file now uncovered.

24008 of 31109 relevant lines covered (77.17%)

78757.26 hits per line

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

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

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

21
                public SvgConfiguration Configuration { get; } = new();
99✔
22

23
                public Layout Layout { get; set; }
2,503✔
24

25
                public PlotPaperUnits PlotPaperUnits
26
                {
27
                        get
28
                        {
2,234✔
29
                                if (this.Layout == null)
2,234✔
30
                                {
2,105✔
31
                                        return PlotPaperUnits.Milimeters;
2,105✔
32
                                }
33
                                else
34
                                {
129✔
35
                                        return this.Layout.PaperUnits;
129✔
36
                                }
37
                        }
2,234✔
38
                }
39

40
                public SvgXmlWriter(Stream w, Encoding encoding, SvgConfiguration configuration) : base(w, encoding)
6✔
41
                {
6✔
42
                        this.Configuration = configuration;
6✔
43
                }
6✔
44

45
                public void WriteAttributeString(string localName, double value, PlotPaperUnits? units = null)
46
                {
352✔
47
                        string unit = string.Empty;
352✔
48
                        if (units == null)
352✔
49
                        {
28✔
50
                                value = SvgConfiguration.ToPixelSize(value, this.PlotPaperUnits);
28✔
51
                        }
28✔
52
                        else
53
                        {
324✔
54
                                switch (units.Value)
324!
55
                                {
56
                                        case PlotPaperUnits.Milimeters:
57
                                                unit = "mm";
322✔
58
                                                break;
322✔
59
                                        case PlotPaperUnits.Inches:
NEW
UNCOV
60
                                                unit = "in";
×
NEW
UNCOV
61
                                                break;
×
62
                                }
63
                        }
324✔
64

65

66
                        this.WriteAttributeString(
352✔
67
                                localName,
352✔
68
                                $"{value.ToString(CultureInfo.InvariantCulture)}{unit}");
352✔
69
                }
352✔
70

71
                public void WriteBlock(BlockRecord record)
72
                {
1✔
73
                        BoundingBox box = record.GetBoundingBox();
1✔
74
                        this.startDocument(box);
1✔
75

76
                        Transform transform = new Transform(-box.Min, new XYZ(1), XYZ.Zero);
1✔
77
                        foreach (var e in record.Entities)
47✔
78
                        {
22✔
79
                                this.writeEntity(e);
22✔
80
                        }
22✔
81

82
                        this.endDocument();
1✔
83
                }
1✔
84

85
                public void WriteLayout(Layout layout)
86
                {
5✔
87
                        this.Layout = layout;
5✔
88

89
                        double paperWidth = layout.PaperWidth;
5✔
90
                        double paperHeight = layout.PaperHeight;
5✔
91

92
                        switch (layout.PaperRotation)
5✔
93
                        {
94
                                case PlotRotation.Degrees90:
95
                                case PlotRotation.Degrees270:
96
                                        paperWidth = layout.PaperHeight;
3✔
97
                                        paperHeight = layout.PaperWidth;
3✔
98
                                        break;
3✔
99
                        }
100

101
                        XYZ lowerCorner = XYZ.Zero;
5✔
102
                        XYZ upperCorner = new XYZ(paperWidth, paperHeight, 0.0);
5✔
103
                        BoundingBox paper = new BoundingBox(lowerCorner, upperCorner);
5✔
104

105
                        XYZ lowerMargin = this.Layout.UnprintableMargin.BottomLeftCorner.Convert<XYZ>();
5✔
106
                        BoundingBox margins = new BoundingBox(
5✔
107
                                lowerMargin,
5✔
108
                                this.Layout.UnprintableMargin.TopCorner.Convert<XYZ>());
5✔
109

110
                        this.startDocument(paper, PlotPaperUnits.Milimeters);
5✔
111

112
                        Transform transform = new Transform(
5✔
113
                                SvgConfiguration.ToPixelSize(lowerMargin, PlotPaperUnits.Milimeters),
5✔
114
                                new XYZ(layout.PrintScale),
5✔
115
                                XYZ.Zero);
5✔
116

117
                        foreach (var e in layout.AssociatedBlock.Entities)
223✔
118
                        {
104✔
119
                                this.writeEntity(e, transform);
104✔
120
                        }
104✔
121

122
                        this.endDocument();
5✔
123
                }
5✔
124

125
                public override void WriteValue(double value)
126
                {
50✔
127
                        base.WriteValue(SvgConfiguration.ToPixelSize(value, this.PlotPaperUnits));
50✔
128
                }
50✔
129

130
                private string colorSvg(Color color)
131
                {
119✔
132
                        if (this.Layout != null && color.Equals(Color.Default))
119✔
133
                        {
77✔
134
                                color = Color.Black;
77✔
135
                        }
77✔
136

137
                        return $"rgb({color.R},{color.G},{color.B})";
119✔
138
                }
119✔
139

140
                private void endDocument()
141
                {
6✔
142
                        this.WriteEndElement();
6✔
143
                        this.WriteEndDocument();
6✔
144
                        this.Close();
6✔
145
                }
6✔
146

147
                private void notify(string message, NotificationType type, Exception ex = null)
148
                {
8✔
149
                        this.OnNotification?.Invoke(this, new NotificationEventArgs(message, type, ex));
8!
150
                }
8✔
151

152
                private void startDocument(BoundingBox box, PlotPaperUnits unit = PlotPaperUnits.Pixels)
153
                {
6✔
154
                        this.WriteStartDocument();
6✔
155

156
                        this.WriteStartElement("svg");
6✔
157
                        this.WriteAttributeString("xmlns", "http://www.w3.org/2000/svg");
6✔
158

159
                        this.WriteAttributeString("width", box.Max.X - box.Min.X, unit);
6✔
160
                        this.WriteAttributeString("height", box.Max.Y - box.Min.Y, unit);
6✔
161

162
                        this.WriteStartAttribute("viewBox");
6✔
163
                        this.WriteValue(box.Min.X);
6✔
164
                        this.WriteValue(" ");
6✔
165
                        this.WriteValue(box.Min.Y);
6✔
166
                        this.WriteValue(" ");
6✔
167
                        this.WriteValue(box.Max.X - box.Min.X);
6✔
168
                        this.WriteValue(" ");
6✔
169
                        this.WriteValue(box.Max.Y - box.Min.Y);
6✔
170
                        this.WriteEndAttribute();
6✔
171

172
                        this.WriteAttributeString("transform", $"scale(1,-1)");
6✔
173

174
                        if (this.Layout != null)
6✔
175
                        {
5✔
176
                                this.WriteAttributeString("style", "background-color:white");
5✔
177
                        }
5✔
178
                }
6✔
179

180
                private string svgPoints(IEnumerable<IVector> points, Transform transform)
181
                {
11✔
182
                        if (!points.Any())
11!
183
                        {
×
184
                                return string.Empty;
×
185
                        }
186

187
                        StringBuilder sb = new StringBuilder();
11✔
188
                        sb.Append(this.toStringFormat(points.First()));
11✔
189
                        foreach (IVector point in points.Skip(1))
2,115✔
190
                        {
1,041✔
191
                                sb.Append(' ');
1,041✔
192
                                sb.Append(this.toStringFormat(point));
1,041✔
193
                        }
1,041✔
194

195
                        return sb.ToString();
11✔
196
                }
11✔
197

198
                private string toStringFormat(double value)
199
                {
2,156✔
200
                        return SvgConfiguration.ToPixelSize(value, this.PlotPaperUnits).ToString(CultureInfo.InvariantCulture);
2,156✔
201
                }
2,156✔
202

203
                private string toStringFormat(IVector vector)
204
                {
1,052✔
205
                        var value = vector.Convert<XY>();
1,052✔
206
                        string x = this.toStringFormat(value.X);
1,052✔
207
                        string y = this.toStringFormat(value.Y);
1,052✔
208

209
                        return $"{x},{y}";
1,052✔
210
                }
1,052✔
211

212
                private void writeArc(Arc arc, Transform transform)
213
                {
3✔
214
                        //A rx ry rotation large-arc-flag sweep-flag x y
215

216
                        this.WriteStartElement("polyline");
3✔
217

218
                        this.writeEntityHeader(arc, transform);
3✔
219

220
                        IEnumerable<IVector> vertices = arc.PolygonalVertexes(256).OfType<IVector>();
3✔
221
                        string pts = this.svgPoints(vertices, transform);
3✔
222
                        this.WriteAttributeString("points", pts);
3✔
223
                        this.WriteAttributeString("fill", "none");
3✔
224

225
                        this.WriteEndElement();
3✔
226
                }
3✔
227

228
                private void writeCircle(Circle circle, Transform transform)
229
                {
2✔
230
                        var loc = transform.ApplyTransform(circle.Center);
2✔
231

232
                        this.WriteStartElement("circle");
2✔
233

234
                        this.writeEntityHeader(circle, transform);
2✔
235

236
                        this.WriteAttributeString("r", circle.Radius);
2✔
237
                        this.WriteAttributeString("cx", loc.X);
2✔
238
                        this.WriteAttributeString("cy", loc.Y);
2✔
239

240
                        this.WriteAttributeString("fill", "none");
2✔
241

242
                        this.WriteEndElement();
2✔
243
                }
2✔
244

245
                private void writeEllipse(Ellipse ellipse, Transform transform)
246
                {
1✔
247
                        this.WriteStartElement("polygon");
1✔
248

249
                        this.writeEntityHeader(ellipse, transform);
1✔
250

251
                        IEnumerable<IVector> vertices = ellipse.PolygonalVertexes(256).OfType<IVector>();
1✔
252
                        string pts = this.svgPoints(vertices, transform);
1✔
253
                        this.WriteAttributeString("points", pts);
1✔
254
                        this.WriteAttributeString("fill", "none");
1✔
255

256
                        this.WriteEndElement();
1✔
257
                }
1✔
258

259
                private void writeEntity(Entity entity)
260
                {
23✔
261
                        this.writeEntity(entity, new Transform());
23✔
262
                }
23✔
263

264
                private void writeEntity(Entity entity, Transform transform)
265
                {
127✔
266
                        switch (entity)
127✔
267
                        {
268
                                case Arc arc:
269
                                        this.writeArc(arc, transform);
3✔
270
                                        break;
3✔
271
                                case Line line:
272
                                        this.writeLine(line, transform);
78✔
273
                                        break;
78✔
274
                                case Point point:
275
                                        this.writePoint(point, transform);
1✔
276
                                        break;
1✔
277
                                case Circle circle:
278
                                        this.writeCircle(circle, transform);
2✔
279
                                        break;
2✔
280
                                case Ellipse ellipse:
281
                                        this.writeEllipse(ellipse, transform);
1✔
282
                                        break;
1✔
283
                                //case Hatch hatch:
284
                                //        this.writeHatch(hatch, transform);
285
                                //        break;
286
                                case Insert insert:
287
                                        this.writeInsert(insert, transform);
1✔
288
                                        break;
1✔
289
                                case IPolyline polyline:
290
                                        this.writePolyline(polyline, transform);
7✔
291
                                        break;
7✔
292
                                case IText text:
293
                                        this.writeText(text, transform);
26✔
294
                                        break;
26✔
295
                                default:
296
                                        this.notify($"[{entity.ObjectName}] Entity not implemented.", NotificationType.NotImplemented);
8✔
297
                                        break;
8✔
298
                        }
299
                }
127✔
300

301
                private void writeEntityHeader(IEntity entity, Transform transform)
302
                {
92✔
303
                        Color color = entity.GetActiveColor();
92✔
304

305
                        this.WriteAttributeString("stroke", this.colorSvg(color));
92✔
306

307
                        var lineWeight = entity.LineWeight;
92✔
308
                        switch (lineWeight)
92✔
309
                        {
310
                                case LineweightType.ByLayer:
311
                                        lineWeight = entity.Layer.LineWeight;
77✔
312
                                        break;
77✔
313
                        }
314

315
                        this.WriteAttributeString("stroke-width", $"{this.Configuration.GetLineWeightValue(lineWeight).ToString(CultureInfo.InvariantCulture)}mm");
92✔
316

317
                        this.writeTransform(transform);
92✔
318
                }
92✔
319

320
                private void writeHatch(Hatch hatch, Transform transform)
321
                {
×
322
                        this.WriteStartElement("g");
×
323

324
                        this.writePattern(hatch.Pattern);
×
325

326
                        foreach (Hatch.BoundaryPath path in hatch.Paths)
×
327
                        {
×
328
                                this.WriteStartElement("polyline");
×
329

330
                                this.writeEntityHeader(hatch, transform);
×
331

332
                                foreach (var item in path.Edges)
×
333
                                {
×
334
                                        //TODO: svg edges for hatch drawing
335
                                }
×
336

337
                                //this.WriteAttributeString("points", pts);
338

339
                                this.WriteAttributeString("fill", "none");
×
340

NEW
341
                                this.WriteEndElement();
×
NEW
342
                        }
×
343

NEW
344
                        this.WriteEndElement();
×
345
                }
×
346

347
                private void writeInsert(Insert insert, Transform transform)
348
                {
1✔
349
                        var insertTransform = insert.GetTransform();
1✔
350
                        var merged = new Transform(transform.Matrix * insertTransform.Matrix);
1✔
351

352
                        this.WriteStartElement("g");
1✔
353
                        this.writeTransform(merged);
1✔
354

355
                        foreach (var e in insert.Block.Entities)
5✔
356
                        {
1✔
357
                                this.writeEntity(e);
1✔
358
                        }
1✔
359

360
                        this.WriteEndElement();
1✔
361
                }
1✔
362

363
                private void writeLine(Line line, Transform transform)
364
                {
78✔
365
                        this.WriteStartElement("line");
78✔
366

367
                        this.writeEntityHeader(line, transform);
78✔
368

369
                        this.WriteAttributeString("x1", line.StartPoint.X, PlotPaperUnits.Milimeters);
78✔
370
                        this.WriteAttributeString("y1", line.StartPoint.Y, PlotPaperUnits.Milimeters);
78✔
371
                        this.WriteAttributeString("x2", line.EndPoint.X, PlotPaperUnits.Milimeters);
78✔
372
                        this.WriteAttributeString("y2", line.EndPoint.Y, PlotPaperUnits.Milimeters);
78✔
373

374
                        this.WriteEndElement();
78✔
375
                }
78✔
376

377
                private void writePattern(HatchPattern pattern)
378
                {
×
379
                        this.WriteStartElement("pattern");
×
380

381
                        this.WriteEndElement();
×
382
                }
×
383

384
                private void writePoint(Point point, Transform transform)
385
                {
1✔
386
                        this.WriteStartElement("circle");
1✔
387

388
                        this.writeEntityHeader(point, transform);
1✔
389

390
                        this.WriteAttributeString("r", this.Configuration.PointRadius);
1✔
391
                        this.WriteAttributeString("cx", point.Location.X);
1✔
392
                        this.WriteAttributeString("cy", point.Location.Y);
1✔
393

394
                        this.WriteAttributeString("fill", this.colorSvg(point.GetActiveColor()));
1✔
395

396
                        this.WriteEndElement();
1✔
397
                }
1✔
398

399
                private void writePolyline(IPolyline polyline, Transform transform)
400
                {
7✔
401
                        if (polyline.IsClosed)
7✔
402
                        {
6✔
403
                                this.WriteStartElement("polygon");
6✔
404
                        }
6✔
405
                        else
406
                        {
1✔
407
                                this.WriteStartElement("polyline");
1✔
408
                        }
1✔
409

410
                        this.writeEntityHeader(polyline, transform);
7✔
411

412
                        var vertices = polyline.Vertices.Select(v => v.Location).ToList();
35✔
413

414
                        string pts = this.svgPoints(polyline.Vertices.Select(v => v.Location), transform);
49✔
415
                        this.WriteAttributeString("points", pts);
7✔
416
                        this.WriteAttributeString("fill", "none");
7✔
417

418
                        this.WriteEndElement();
7✔
419
                }
7✔
420

421
                private void writeText(IText text, Transform transform)
422
                {
26✔
423
                        XYZ insert;
424

425
                        if (text is TextEntity lineText
26✔
426
                                && (lineText.HorizontalAlignment != TextHorizontalAlignment.Left
26✔
427
                                || lineText.VerticalAlignment != TextVerticalAlignmentType.Baseline)
26✔
428
                                && !(lineText.HorizontalAlignment == TextHorizontalAlignment.Fit
26✔
429
                                || lineText.HorizontalAlignment == TextHorizontalAlignment.Aligned))
26✔
430
                        {
12✔
431
                                insert = lineText.AlignmentPoint;
12✔
432
                        }
12✔
433
                        else
434
                        {
14✔
435
                                insert = text.InsertPoint;
14✔
436
                        }
14✔
437

438
                        this.WriteStartElement("g");
26✔
439
                        this.WriteAttributeString("transform", $"translate({this.toStringFormat(insert.X)},{this.toStringFormat(insert.Y)})");
26✔
440

441
                        this.WriteStartElement("text");
26✔
442

443
                        this.writeTransform(scale: new XYZ(1, -1, 0), rotation: text.Rotation != 0 ? text.Rotation : null);
26✔
444

445
                        this.WriteAttributeString("fill", this.colorSvg(text.GetActiveColor()));
26✔
446

447
                        //<text x="20" y="35" class="small">My</text>
448
                        this.WriteStartAttribute("style");
26✔
449
                        this.WriteValue("font:");
26✔
450
                        this.WriteValue(text.Height);
26✔
451
                        this.WriteValue("px");
26✔
452
                        this.WriteValue(" ");
26✔
453
                        this.WriteValue(Path.GetFileNameWithoutExtension(text.Style.Filename));
26✔
454
                        this.WriteEndAttribute();
26✔
455

456
                        switch (text)
26✔
457
                        {
458
                                case MText mtext:
459
                                        switch (mtext.AttachmentPoint)
5!
460
                                        {
461
                                                case AttachmentPointType.TopLeft:
462
                                                        this.WriteAttributeString("alignment-baseline", "hanging");
4✔
463
                                                        this.WriteAttributeString("text-anchor", "start");
4✔
464
                                                        break;
4✔
465
                                                case AttachmentPointType.TopCenter:
466
                                                        this.WriteAttributeString("alignment-baseline", "hanging");
×
467
                                                        this.WriteAttributeString("text-anchor", "middle");
×
468
                                                        break;
×
469
                                                case AttachmentPointType.TopRight:
470
                                                        this.WriteAttributeString("alignment-baseline", "hanging");
1✔
471
                                                        this.WriteAttributeString("text-anchor", "end");
1✔
472
                                                        break;
1✔
473
                                                case AttachmentPointType.MiddleLeft:
474
                                                        this.WriteAttributeString("alignment-baseline", "middle");
×
475
                                                        this.WriteAttributeString("text-anchor", "start");
×
476
                                                        break;
×
477
                                                case AttachmentPointType.MiddleCenter:
478
                                                        this.WriteAttributeString("alignment-baseline", "middle");
×
479
                                                        this.WriteAttributeString("text-anchor", "middle");
×
480
                                                        break;
×
481
                                                case AttachmentPointType.MiddleRight:
482
                                                        this.WriteAttributeString("alignment-baseline", "middle");
×
483
                                                        this.WriteAttributeString("text-anchor", "end");
×
NEW
484
                                                        break;
×
485
                                                case AttachmentPointType.BottomLeft:
NEW
486
                                                        this.WriteAttributeString("alignment-baseline", "baseline");
×
NEW
487
                                                        this.WriteAttributeString("text-anchor", "start");
×
NEW
488
                                                        break;
×
489
                                                case AttachmentPointType.BottomCenter:
NEW
490
                                                        this.WriteAttributeString("alignment-baseline", "baseline");
×
NEW
491
                                                        this.WriteAttributeString("text-anchor", "middle");
×
492
                                                        break;
×
493
                                                case AttachmentPointType.BottomRight:
494
                                                        this.WriteAttributeString("alignment-baseline", "baseline");
×
495
                                                        this.WriteAttributeString("text-anchor", "end");
×
496
                                                        break;
×
497
                                                default:
498
                                                        break;
×
499
                                        }
500

501
                                        foreach (var item in mtext.GetTextLines())
53✔
502
                                        {
19✔
503
                                                this.WriteStartElement("tspan");
19✔
504
                                                this.WriteAttributeString("x", 0);
19✔
505
                                                this.WriteAttributeString("dy", "1em");
19✔
506
                                                this.WriteString(item);
19✔
507
                                                this.WriteEndElement();
19✔
508
                                        }
19✔
509
                                        break;
5✔
510
                                case TextEntity textEntity:
511

512
                                        switch (textEntity.HorizontalAlignment)
21✔
513
                                        {
514
                                                case TextHorizontalAlignment.Left:
515
                                                        this.WriteAttributeString("text-anchor", "start");
10✔
516
                                                        break;
10✔
517
                                                case TextHorizontalAlignment.Middle:
518
                                                case TextHorizontalAlignment.Center:
519
                                                        this.WriteAttributeString("text-anchor", "middle");
5✔
520
                                                        break;
5✔
521
                                                case TextHorizontalAlignment.Right:
522
                                                        this.WriteAttributeString("text-anchor", "end");
4✔
523
                                                        break;
4✔
524
                                        }
525

526
                                        switch (textEntity.VerticalAlignment)
21✔
527
                                        {
528
                                                case TextVerticalAlignmentType.Baseline:
529
                                                case TextVerticalAlignmentType.Bottom:
530
                                                        this.WriteAttributeString("alignment-baseline", "baseline");
15✔
531
                                                        break;
15✔
532
                                                case TextVerticalAlignmentType.Middle:
533
                                                        this.WriteAttributeString("alignment-baseline", "middle");
3✔
534
                                                        break;
3✔
535
                                                case TextVerticalAlignmentType.Top:
536
                                                        this.WriteAttributeString("alignment-baseline", "hanging");
3✔
537
                                                        break;
3✔
538
                                        }
539

540
                                        this.WriteString(text.Value);
21✔
541
                                        break;
21✔
542
                        }
543

544
                        this.WriteEndElement();
26✔
545
                        this.WriteEndElement();
26✔
546
                }
26✔
547

548
                private void writeTransform(Transform transform)
549
                {
93✔
550
                        XYZ? translation = transform.Translation != XYZ.Zero ? transform.Translation : null;
93✔
551
                        XYZ? scale = transform.Scale != new XYZ(1) ? transform.Scale : null;
93✔
552
                        double? rotation = transform.EulerRotation.Z != 0 ? transform.EulerRotation.Z : null;
93!
553

554
                        this.writeTransform(translation, scale, rotation);
93✔
555
                }
93✔
556

557
                private void writeTransform(XYZ? translation = null, XYZ? scale = null, double? rotation = null)
558
                {
119✔
559
                        StringBuilder sb = new StringBuilder();
119✔
560

561
                        if (translation.HasValue)
119✔
562
                        {
40✔
563
                                var t = translation.Value;
40✔
564

565
                                sb.Append($"translate(");
40✔
566
                                sb.Append($"{t.X.ToString(CultureInfo.InvariantCulture)},");
40✔
567
                                sb.Append($"{t.Y.ToString(CultureInfo.InvariantCulture)})");
40✔
568
                                sb.Append(' ');
40✔
569
                        }
40✔
570

571
                        if (scale.HasValue)
119✔
572
                        {
63✔
573
                                var s = scale.Value;
63✔
574

575
                                sb.Append($"scale(");
63✔
576
                                sb.Append($"{s.X.ToString(CultureInfo.InvariantCulture)},");
63✔
577
                                sb.Append($"{s.Y.ToString(CultureInfo.InvariantCulture)})");
63✔
578
                                sb.Append(' ');
63✔
579
                        }
63✔
580

581
                        if (rotation.HasValue)
119✔
582
                        {
2✔
583
                                var r = -MathHelper.RadToDeg(rotation.Value);
2✔
584

585
                                sb.Append($"rotate(");
2✔
586
                                sb.Append($"{r.ToString(CultureInfo.InvariantCulture)})");
2✔
587
                        }
2✔
588

589
                        if (sb.ToString().IsNullOrEmpty())
119✔
590
                        {
47✔
591
                                return;
47✔
592
                        }
593

594
                        this.WriteAttributeString("transform", sb.ToString());
72✔
595
                }
119✔
596
        }
597
}
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