// Spludlow Software
// Copyright © Samuel P. Ludlow 2020 All Rights Reserved
// Distributed under the terms of the GNU General Public License version 3
// Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
// https://www.spludlow.co.uk/LICENCE.TXT
// The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission
// v1.14
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.IO;
using System.Drawing;
namespace Spludlow.Drawing
{
public class SVG : IDisposable
{
public static void Test()
{
string targetFilename = @"D:\out.svg";
//Spludlow.Printing.PrintDoc doc = new Printing.PrintDoc("A4");
using (SVG doc = new SVG(targetFilename, new SizeF(200, 100)))
{
doc.Rectangle(10, 10, 40, 20, 1, Color.Black, Color.BlueViolet);
doc.NewPage();
doc.Rectangle(10, 10, 40, 20, 1, Color.Black, Color.Red);
}
//Spludlow.Printing.Printer.Print(doc, "svg:" + targetFilename);
}
private string _Filename;
private StringBuilder _Output;
private System.Drawing.ColorConverter _CC = new ColorConverter();
public SVG(string filename, System.Drawing.SizeF size)
{
this.StartNew(filename, size);
}
public SVG(string filename, string pageSize)
{
System.Drawing.SizeF size = Spludlow.Printing.PageSizes.PageSizeMillimeters(pageSize);
this.StartNew(filename, size);
}
public SVG(string filename, string pageSize, bool landscape)
{
System.Drawing.SizeF size = Spludlow.Printing.PageSizes.PageSizeMillimeters(pageSize, landscape);
this.StartNew(filename, size);
}
private void StartNew(string filename, System.Drawing.SizeF size)
{
this._Filename = filename;
this._Output = new StringBuilder();
// baseProfile=\"tiny\"
string width = size.Width.ToString();
string height = size.Height.ToString();
_Output.AppendLine("");
_Output.AppendLine("");
_Output.AppendLine("");
File.WriteAllText(this._Filename, this._Output.ToString(), Encoding.UTF8);
}
public void Dispose()
{
Close();
}
}
}
public class SvgDocument : XmlDocument
{
public SvgDocument(float width, float height, string stylePath)
{
if (stylePath == "")
{
this.LoadXml("" + Environment.NewLine +
"");
}
else
{
this.LoadXml("" + Environment.NewLine +
"" + Environment.NewLine +
"");
}
AddAtribToNode(this.DocumentElement, "width", width.ToString());
AddAtribToNode(this.DocumentElement, "height", height.ToString());
AddAtribToNode(this.DocumentElement, "viewbox", "0 0 " + width.ToString() + " " + height.ToString());
}
public void DrawRectangle(float x, float y, float width, float height, string style)
{
XmlNode node = this.CreateElement("rect");
AddAtribToNode(node, "x", x.ToString());
AddAtribToNode(node, "y", y.ToString());
AddAtribToNode(node, "width", width.ToString());
AddAtribToNode(node, "height", height.ToString());
AddAtribToNode(node, "style", style);
this.DocumentElement.AppendChild(node);
}
public void DrawRoundedRectangle(
float x,
float y,
float width,
float height,
float rx,
float ry,
string style)
{
XmlNode node = this.CreateElement("rect");
AddAtribToNode(node, "x", x.ToString());
AddAtribToNode(node, "y", y.ToString());
AddAtribToNode(node, "width", width.ToString());
AddAtribToNode(node, "height", height.ToString());
AddAtribToNode(node, "rx", rx.ToString());
AddAtribToNode(node, "ry", ry.ToString());
AddAtribToNode(node, "style", style);
this.DocumentElement.AppendChild(node);
}
public void DrawCircle(float cx, float cy, float radius, string style)
{
XmlNode node = this.CreateElement("circle");
AddAtribToNode(node, "cx", cx.ToString());
AddAtribToNode(node, "cy", cy.ToString());
AddAtribToNode(node, "r", radius.ToString());
AddAtribToNode(node, "style", style);
this.DocumentElement.AppendChild(node);
}
public void DrawEllipse(decimal cx, decimal cy, decimal rx, decimal ry, string style)
{
XmlNode node = this.CreateElement("ellipse");
AddAtribToNode(node, "cx", cx.ToString());
AddAtribToNode(node, "cy", cy.ToString());
AddAtribToNode(node, "rx", rx.ToString());
AddAtribToNode(node, "ry", ry.ToString());
AddAtribToNode(node, "style", style);
this.DocumentElement.AppendChild(node);
}
public void DrawLine(decimal x1, decimal y1, decimal x2, decimal y2, string style)
{
XmlNode node = this.CreateElement("line");
AddAtribToNode(node, "x1", x1.ToString());
AddAtribToNode(node, "y1", y1.ToString());
AddAtribToNode(node, "x2", x2.ToString());
AddAtribToNode(node, "y2", y2.ToString());
AddAtribToNode(node, "style", style);
this.DocumentElement.AppendChild(node);
}
public void DrawPolyLine(PointF[] points, string style)
{
string pointString = "";
for (int i = 0; i < points.Length; ++i)
{
if (pointString != "")
{
pointString += ", ";
}
pointString += points[i].X.ToString() + ", " + points[i].Y.ToString();
}
XmlNode node = this.CreateElement("polyline");
AddAtribToNode(node, "points", pointString);
AddAtribToNode(node, "style", style);
this.DocumentElement.AppendChild(node);
}
public void DrawPolygon(PointF[] points, string style)
{
string pointString = "";
for (int i = 0; i < points.Length; ++i)
{
if (pointString != "")
{
pointString += ", ";
}
pointString += points[i].X.ToString() + ", " + points[i].Y.ToString();
}
XmlNode node = this.CreateElement("polygon");
AddAtribToNode(node, "points", pointString);
AddAtribToNode(node, "style", style);
this.DocumentElement.AppendChild(node);
}
public void DrawText(string text, decimal x, decimal y, string style)
{
XmlNode node = this.CreateElement("text");
AddAtribToNode(node, "x", x.ToString());
AddAtribToNode(node, "y", y.ToString());
AddAtribToNode(node, "style", style);
node.InnerText = text;
this.DocumentElement.AppendChild(node);
}
public void DrawArc(decimal x1, decimal y1, decimal x2, decimal y2, decimal rx, decimal ry,
decimal rotation, bool largArcFlag, bool sweepFlag, string style)
{
int laf = 0;
if (largArcFlag == true)
{
laf = 1;
}
int sf = 0;
if (sweepFlag == true)
{
sf = 1;
}
XmlNode node = this.CreateElement("path");
string arcString =
"M " + x1.ToString() + ", " + y1.ToString() +
" A " + rx.ToString() + ", " + ry.ToString() +
" " + rotation.ToString() +
" " + laf.ToString() + ", " + sf.ToString() +
" " + x2.ToString() + ", " + y2.ToString();
AddAtribToNode(node, "d", arcString);
AddAtribToNode(node, "style", style);
this.DocumentElement.AppendChild(node);
}
public void DrawSlice(decimal x, decimal y, decimal rx, decimal ry,
decimal startAngle, decimal endAngle, decimal push, string style, string classAtrib)
{
// Convert angles to radians
decimal radStartAngle = (startAngle - 90) * (decimal)(Math.PI / 180);
decimal radEndAngle = (endAngle - 90) * (decimal)(Math.PI / 180);
// If want to push the slice away from the centre
if (push > 0)
{
// Work out angle for vector of push away from the middle
decimal pushAngle = radStartAngle + (radEndAngle - radStartAngle) / 2;
// Adjust X and Y for push
x += (decimal)Math.Cos((double)pushAngle) * (rx * push);
y += (decimal)Math.Sin((double)pushAngle) * (ry * push);
}
// Get first point of arc
decimal x1 = x + (decimal)Math.Cos((double)radStartAngle) * rx;
decimal y1 = y + (decimal)Math.Sin((double)radStartAngle) * ry;
// Get second point of arc
decimal x2 = x + (decimal)Math.Cos((double)radEndAngle) * rx;
decimal y2 = y + (decimal)Math.Sin((double)radEndAngle) * ry;
// Work out the Large arc flag
int laf = 0;
if (endAngle - startAngle >= 180)
{
laf = 1;
}
// Create a node
XmlNode node = this.CreateElement("path");
// Build the path
string arcString =
"M " + x.ToString() + ", " + y.ToString() + // Move to centre
" L " + x1.ToString() + ", " + y1.ToString() + // Line to First Point
" A " + rx.ToString() + ", " + ry.ToString() + // Arc to second point
" 0 " + laf.ToString() + ", 1" + " " + x2.ToString() +
", " + y2.ToString() +
" Z"; // Close back to centre
// Add attributes to the node
AddAtribToNode(node, "d", arcString);
if (style != "")
{
AddAtribToNode(node, "style", style);
}
if (classAtrib != "")
{
AddAtribToNode(node, "class", classAtrib);
}
this.DocumentElement.AppendChild(node);
}
private void AddAtribToNode(XmlNode node, string name, string val)
{
XmlNode attr = this.CreateNode(XmlNodeType.Attribute, name, null);
attr.Value = val;
node.Attributes.SetNamedItem(attr);
}
}