12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244 |
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Drawing.Imaging;
- using System.Drawing.Text;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Text;
- using FastReport.Utils;
- namespace FastReport
- {
- /// <summary>
- /// Specifies the image format in SVG export.
- /// </summary>
- public enum SVGImageFormat
- {
- /// <summary>
- /// Specifies the .png format.
- /// </summary>
- Png,
- /// <summary>
- /// Specifies the .jpg format.
- /// </summary>
- Jpeg
- }
- /// <summary>
- /// Drawing objects to a svg
- /// </summary>
- public class SvgGraphics : IGraphics
- {
- private XmlDocument xmlDocument;
- private Graphics graphics;
- private Bitmap internalImage;
- private XmlItem root;
- private XmlItem oldRoot;
- private SizeF sizeCache;
- private bool disposedValue = false; // To detect redundant calls
- private NumberFormatInfo numberFormat = CultureInfo.InvariantCulture.NumberFormat;
- private RectangleF viewBox;
- private RectangleF viewPort;
- private StringFormat measureFormat;
- private int imageId, patternId, clipId, imageFileId;
- private System.Drawing.Drawing2D.Matrix oldTransform;
- private Region oldClip;
- private bool needStateCheck;
- private Dictionary<Font, string> fontStyles;
- private readonly float fontDpiRatio = DrawUtils.ScreenDpi / 96f;
- private string prefixStyle = string.Empty;
- public XmlDocument XmlDocument { get { return xmlDocument; } }
- public SizeF Size
- {
- get { return sizeCache; }
- set
- {
- sizeCache = value;
- xmlDocument.Root.SetProp("width", GetString(value.Width));
- xmlDocument.Root.SetProp("height", GetString(value.Height));
- }
- }
- public RectangleF ViewBox
- {
- get { return viewBox; }
- set
- {
- viewBox = value;
- xmlDocument.Root.SetProp("viewBox", String.Format("{0} {1} {2} {3}",
- GetString(value.Left),
- GetString(value.Top),
- GetString(value.Width),
- GetString(value.Height)
- ));
- }
- }
- public RectangleF ViewPort
- {
- get { return viewPort; }
- set
- {
- viewPort = value;
- xmlDocument.Root.SetProp("viewPort", String.Format("{0} {1} {2} {3}",
- GetString(value.Left),
- GetString(value.Top),
- GetString(value.Width),
- GetString(value.Height)
- ));
- }
- }
- /// <summary>
- /// For setting namespace, clear all attributes on setting, therefore use this property before setting other svg options
- /// </summary>
- public IEnumerable<KeyValuePair<string, string>> Attributes
- {
- get
- {
- foreach (XmlProperty prop in xmlDocument.Root.Properties)
- {
- yield return new KeyValuePair<string, string>(prop.Key, prop.Value);
- }
- }
- set
- {
- xmlDocument.Root.Properties = null;
- foreach (KeyValuePair<string, string> kv in value)
- xmlDocument.Root.SetProp(kv.Key, kv.Value);
- }
- }
- public float DpiX => 96;
- public float DpiY => 96;
- // TODO: the following 4 properties
- public TextRenderingHint TextRenderingHint { get; set; }
- public InterpolationMode InterpolationMode { get; set; }
- public SmoothingMode SmoothingMode { get; set; }
- public CompositingQuality CompositingQuality { get; set; }
- public Graphics Graphics => graphics;
- public System.Drawing.Drawing2D.Matrix Transform
- {
- get
- {
- needStateCheck = true;
- return graphics.Transform;
- }
- set
- {
- needStateCheck = true;
- graphics.Transform = value;
- }
- }
- public GraphicsUnit PageUnit { get; set; }
- public bool IsClipEmpty => graphics.IsClipEmpty;
- public Region Clip
- {
- get
- {
- needStateCheck = true;
- return graphics.Clip;
- }
- set
- {
- needStateCheck = true;
- graphics.Clip = value;
- }
- }
- public bool EmbeddedImages { get; set; }
- public SVGImageFormat SvgImageFormat { get; set; }
- public string ImageFilePrefix { get; set; }
- /// <summary>
- /// Initialize a new Graphics for SVG, it's rendered to xml, layer by layer, not one image,
- /// set the Size of this graphics in Size property
- /// </summary>
- public SvgGraphics(XmlDocument xmlDocument)
- {
- this.xmlDocument = xmlDocument;
- root = this.xmlDocument.Root;
- oldRoot = root;
- root.Name = "svg";
- root.SetProp("xmlns", "http://www.w3.org/2000/svg");
- // create "style" and "defs" items at the top of document
- root.FindItem("style").SetProp("type", "text/css");
- root.FindItem("defs");
- this.internalImage = new Bitmap(1, 1);
- this.graphics = Graphics.FromImage(internalImage);
- this.measureFormat = StringFormat.GenericTypographic;
- oldTransform = new System.Drawing.Drawing2D.Matrix();
- oldClip = new Region();
- fontStyles = new Dictionary<Font, string>();
- needStateCheck = true;
- EmbeddedImages = true;
- ImageFilePrefix = "Image";
- }
- /// <summary>
- /// Sets or gets prefix for style and object ids
- /// </summary>
- public string PrefixStyle
- {
- get
- {
- return prefixStyle;
- }
- set
- {
- prefixStyle = value;
- }
- }
- #region IDisposable Support
- protected virtual void Dispose(bool disposing)
- {
- if (!disposedValue)
- {
- if (disposing)
- {
- if (graphics != null)
- graphics.Dispose();
- graphics = null;
- if (internalImage != null)
- internalImage.Dispose();
- internalImage = null;
- if (oldTransform != null)
- oldTransform.Dispose();
- oldTransform = null;
- if (oldClip != null)
- oldClip.Dispose();
- oldClip = null;
- }
- disposedValue = true;
- }
- }
- // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
- // ~SVGGraphicsRenderer() {
- // // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
- // Dispose(false);
- // }
- // This code added to correctly implement the disposable pattern.
- public void Dispose()
- {
- // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
- Dispose(true);
- // TODO: uncomment the following line if the finalizer is overridden above.
- // GC.SuppressFinalize(this);
- }
- #endregion
- #region Internal methods
- private string GetString(float value)
- {
- return Math.Round(value, 2).ToString(numberFormat);
- }
- private float SpaceWidth(Font drawFont)
- {
- return graphics.MeasureString("1 2", drawFont, int.MaxValue, measureFormat).Width -
- graphics.MeasureString("12", drawFont, int.MaxValue, measureFormat).Width;
- }
- private string GetStringAlpha(Color color)
- {
- return GetString(color.A / 255f);
- }
- private string GetStringRGB(Color color)
- {
- return ColorTranslator.ToHtml(color);
- }
- internal XmlItem OpenContainer(string name)
- {
- // AddItem can change root node
- XmlItem item = AddItem(name);
- oldRoot = root;
- root = item;
- return item;
- }
- internal void CloseContainer()
- {
- root = oldRoot;
- }
- private XmlItem AddItem(string name)
- {
- // these comparisons are quite expensive, try to avoid them with needStateCheck
- bool transformChanged = needStateCheck ? !oldTransform.Equals(Transform) : false;
- bool clipChanged = needStateCheck ? !oldClip.Equals(Clip, graphics) : false;
- if (transformChanged || clipChanged)
- {
- root = xmlDocument.Root.Add();
- root.Name = "g";
- // text positioning compatible with .net (commented out due to problems in some SVG readers). Replaced with top offset in AddString
- //root.SetProp("dominant-baseline", "text-before-edge");
- // transform
- float[] m = Transform.Elements;
- if (m.Length == 6 && (m[0] != 1f || m[1] != 0f || m[2] != 0f || m[3] != 1f || m[4] != 0f || m[5] != 0f))
- {
- root.SetProp("transform", String.Format("matrix({0},{1},{2},{3},{4},{5})",
- GetString(m[0]), GetString(m[1]), GetString(m[2]), GetString(m[3]), GetString(m[4]), GetString(m[5])));
- }
- // clip
- if (!Clip.IsInfinite(graphics))
- {
- root.SetProp("clip-path", "url(#" + AddDefsClip() + ")");
- }
- oldTransform.Dispose();
- oldTransform = Transform.Clone();
- oldClip.Dispose();
- oldClip = Clip.Clone();
- }
- needStateCheck = false;
- XmlItem result = root.Add();
- result.Name = name;
- return result;
- }
- private string AddDefsClip()
- {
- System.Drawing.Drawing2D.Matrix identityMatrix = new System.Drawing.Drawing2D.Matrix();
- RectangleF[] clipRects = Clip.GetRegionScans(identityMatrix);
- identityMatrix.Dispose();
- XmlItem defsItem = xmlDocument.Root.FindItem("defs");
- // search for existing clip
- if (clipRects.Length == 1)
- {
- string xStr = GetString(clipRects[0].X);
- string yStr = GetString(clipRects[0].Y);
- string widthStr = GetString(clipRects[0].Width);
- string heightStr = GetString(clipRects[0].Height);
- foreach (XmlItem item in defsItem.Items)
- {
- if (item.Name == "clipPath" && item.Count == 1)
- {
- if (item[0].GetProp("x") == xStr &&
- item[0].GetProp("y") == yStr &&
- item[0].GetProp("width") == widthStr &&
- item[0].GetProp("height") == heightStr)
- return item.GetProp("id");
- }
- }
- }
- clipId++;
- string clipIdStr = prefixStyle + "clip" + clipId.ToString();
- XmlItem clipItem = defsItem.Add();
- clipItem.Name = "clipPath";
- clipItem.SetProp("id", clipIdStr);
- foreach (RectangleF rect in clipRects)
- {
- XmlItem clipRectItem = clipItem.Add();
- clipRectItem.Name = "rect";
- clipRectItem.SetProp("x", GetString(rect.X));
- clipRectItem.SetProp("y", GetString(rect.Y));
- clipRectItem.SetProp("width", GetString(rect.Width));
- clipRectItem.SetProp("height", GetString(rect.Height));
- }
- return clipIdStr;
- }
- private string AddDefsImage(Image bmp, float width, float height)
- {
- string href = "";
- using (MemoryStream stream = new MemoryStream())
- {
- bmp.Save(stream, ImageFormat.Png);
- href = "data:image/png; base64," + Convert.ToBase64String(stream.ToArray());
- }
- string widthStr = GetString(width);
- string heightStr = GetString(height);
- XmlItem defsItem = xmlDocument.Root.FindItem("defs");
- // search for existing image
- foreach (XmlItem item in defsItem.Items)
- {
- if (item.Name == "image")
- {
- if (item.GetProp("width") == widthStr &&
- item.GetProp("height") == heightStr &&
- item.GetProp("href") == href)
- return item.GetProp("id");
- }
- }
- imageId++;
- string result = prefixStyle + "img" + imageId.ToString();
- XmlItem imgItem = defsItem.Add();
- imgItem.Name = "image";
- imgItem.SetProp("id", result);
- imgItem.SetProp("width", widthStr);
- imgItem.SetProp("height", heightStr);
- imgItem.SetProp("href", href);
- return result;
- }
- private string AddDefsImagePattern(Image bmp, float x, float y, float width, float height)
- {
- string xStr = GetString(x);
- string yStr = GetString(y);
- string widthStr = GetString(width);
- string heightStr = GetString(height);
- string imgId = AddDefsImage(bmp, width, height);
- XmlItem defsItem = xmlDocument.Root.FindItem("defs");
- // search for existing pattern
- foreach (XmlItem item in defsItem.Items)
- {
- if (item.Name == "pattern")
- {
- if (item.GetProp("x") == xStr &&
- item.GetProp("y") == yStr &&
- item.GetProp("width") == widthStr &&
- item.GetProp("height") == heightStr)
- {
- if (item.Count == 1 && item.Items[0].Name == "use" && item.Items[0].GetProp("href") == "#" + imgId)
- return item.GetProp("id");
- }
- }
- }
- patternId++;
- string result = prefixStyle + "p" + patternId.ToString();
- XmlItem pItem = defsItem.Add();
- pItem.Name = "pattern";
- pItem.SetProp("id", result);
- pItem.SetProp("patternUnits", "userSpaceOnUse");
- pItem.SetProp("x", xStr);
- pItem.SetProp("y", yStr);
- pItem.SetProp("width", widthStr);
- pItem.SetProp("height", heightStr);
- XmlItem imgItem = pItem.Add();
- imgItem.Name = "use";
- imgItem.SetProp("href", "#" + imgId);
- return result;
- }
- private void AddStroke(List<XmlProperty> properties, Pen pen)
- {
- if (pen != null)
- {
- if (pen.Width != 1)
- properties.Add(XmlProperty.Create("stroke-width", GetString(pen.Width)));
- properties.Add(XmlProperty.Create("stroke", GetStringRGB(pen.Color)));
- if (pen.Color.A < 255)
- properties.Add(XmlProperty.Create("stroke-opacity", GetStringAlpha(pen.Color)));
- if (pen.DashStyle != DashStyle.Solid)
- {
- string strokeDashArray = "";
- foreach (float f in pen.DashPattern)
- {
- strokeDashArray += GetString(f * pen.Width) + " ";
- }
- strokeDashArray = strokeDashArray.Trim();
- properties.Add(XmlProperty.Create("stroke-dasharray", strokeDashArray));
- }
- }
- }
- private void AddFill(List<XmlProperty> properties, Brush brush)
- {
- if (brush == null)
- {
- properties.Add(XmlProperty.Create("fill", "none"));
- }
- else if (brush is SolidBrush)
- {
- SolidBrush b = brush as SolidBrush;
- if (b.Color != Color.Black)
- {
- properties.Add(XmlProperty.Create("fill", GetStringRGB(b.Color)));
- if (b.Color.A < 255)
- properties.Add(XmlProperty.Create("fill-opacity", GetStringAlpha(b.Color)));
- }
- }
- else if (brush is LinearGradientBrush || brush is PathGradientBrush)
- {
- RectangleF rect = new RectangleF();
- WrapMode wrapMode = WrapMode.Tile;
- if (brush is LinearGradientBrush)
- {
- rect = (brush as LinearGradientBrush).Rectangle;
- wrapMode = (brush as LinearGradientBrush).WrapMode;
- }
- else if (brush is PathGradientBrush)
- {
- rect = (brush as PathGradientBrush).Rectangle;
- wrapMode = (brush as PathGradientBrush).WrapMode;
- }
- switch (wrapMode)
- {
- case WrapMode.TileFlipX:
- rect = new RectangleF(rect.X, rect.Y, rect.Width * 2, rect.Height);
- break;
- case WrapMode.TileFlipY:
- rect = new RectangleF(rect.X, rect.Y, rect.Width, rect.Height * 2);
- break;
- case WrapMode.TileFlipXY:
- rect = new RectangleF(rect.X, rect.Y, rect.Width * 2, rect.Height * 2);
- break;
- }
- using (Bitmap bmp = new Bitmap((int)rect.Width, (int)rect.Height))
- {
- using (Graphics g = Graphics.FromImage(bmp))
- {
- g.Clear(Color.Transparent);
- g.TranslateTransform(-rect.X, -rect.Y);
- g.FillRectangle(brush, rect);
- }
- properties.Add(XmlProperty.Create("fill", "url(#" + AddDefsImagePattern(bmp, rect.X, rect.Y, rect.Width, rect.Height) + ")"));
- }
- }
- else if (brush is HatchBrush)
- {
- HatchBrush b = brush as HatchBrush;
- using (Bitmap bmp = new Bitmap(8, 8))
- {
- using (Graphics g = Graphics.FromImage(bmp))
- {
- g.Clear(Color.Transparent);
- g.FillRectangle(b, 0, 0, 8, 8);
- }
- properties.Add(XmlProperty.Create("fill", "url(#" + AddDefsImagePattern(bmp, 0, 0, 8, 8) + ")"));
- }
- }
- else if (brush is TextureBrush)
- {
- TextureBrush b = brush as TextureBrush;
- //create a new Bitmap object with twice the height and width of the original image
- using (Bitmap bmp = new Bitmap(b.Image.Width * 2, b.Image.Height * 2))
- {
- using (Graphics g = Graphics.FromImage(bmp))
- {
- g.Clear(Color.Transparent);
- g.FillRectangle(b, new RectangleF(0, 0, bmp.Width, bmp.Height));
- }
- properties.Add(XmlProperty.Create("fill", "url(#" + AddDefsImagePattern(bmp, 0, 0, bmp.Width, bmp.Height) + ")"));
- }
- }
- }
- private void AddEllipse(Pen pen, Brush brush, float left, float top, float width, float height)
- {
- XmlItem obj = AddItem("ellipse");
- List<XmlProperty> properties = new List<XmlProperty>();
- properties.Add(XmlProperty.Create("cx", GetString(left + width / 2)));
- properties.Add(XmlProperty.Create("cy", GetString(top + height / 2)));
- properties.Add(XmlProperty.Create("rx", GetString(width / 2)));
- properties.Add(XmlProperty.Create("ry", GetString(height / 2)));
- AddStroke(properties, pen);
- AddFill(properties, brush);
- obj.Properties = properties.ToArray();
- }
- private void AddRectangle(Pen pen, Brush brush, float left, float top, float width, float height)
- {
- XmlItem obj = AddItem("rect");
- List<XmlProperty> properties = new List<XmlProperty>();
- properties.Add(XmlProperty.Create("x", GetString(left)));
- properties.Add(XmlProperty.Create("y", GetString(top)));
- properties.Add(XmlProperty.Create("width", GetString(width)));
- properties.Add(XmlProperty.Create("height", GetString(height)));
- AddStroke(properties, pen);
- AddFill(properties, brush);
- obj.Properties = properties.ToArray();
- }
- private void AddPolygon(Pen pen, Brush brush, PointF[] points)
- {
- if (points.Length == 0)
- return;
- XmlItem obj = AddItem("polygon");
- List<XmlProperty> properties = new List<XmlProperty>();
- StringBuilder sbPoints = new StringBuilder();
- foreach (PointF point in points)
- sbPoints.Append(GetString(point.X)).Append(" ").Append(GetString(point.Y)).Append(" ");
- if (sbPoints.Length > 0)
- sbPoints.Length--;
- properties.Add(XmlProperty.Create("points", sbPoints.ToString()));
- AddStroke(properties, pen);
- AddFill(properties, brush);
- obj.Properties = properties.ToArray();
- }
- private void AddPath(Pen pen, Brush brush, GraphicsPath path)
- {
- if (path.PointCount == 0)
- return;
- XmlItem obj = AddItem("path");
- List<XmlProperty> properties = new List<XmlProperty>();
- StringBuilder sbd = new StringBuilder();
- byte[] types = path.PathTypes;
- PointF[] points = path.PathPoints;
- int c_count = 0;
- for (int i = 0; i < points.Length; i++)
- {
- byte t = types[i];
- if ((t & 0x7) == 0)
- {
- // MoveTo command
- c_count = 0;
- sbd.Append("M").Append(GetString(points[i].X)).Append(",").Append(GetString(points[i].Y)).Append(" ");
- }
- else if ((t & 0x7) == 1)
- {
- // LineTo command
- c_count = 0;
- sbd.Append("L").Append(GetString(points[i].X)).Append(",").Append(GetString(points[i].Y)).Append(" ");
- }
- else if ((t & 0x7) == 3)
- {
- // Cubic bezier curve command has 3 points. The C symbol must be added once at the start of sequence
- if (c_count == 0)
- sbd.Append("C");
- c_count++;
- if (c_count >= 3)
- c_count = 0;
- sbd.Append(GetString(points[i].X)).Append(",").Append(GetString(points[i].Y)).Append(" ");
- }
- if ((t & 0x80) != 0)
- {
- // Close figure flag
- sbd.Append("z ");
- }
- }
- if (sbd.Length > 0)
- sbd.Length--;
- properties.Add(XmlProperty.Create("d", sbd.ToString()));
- AddStroke(properties, pen);
- AddFill(properties, brush);
- obj.Properties = properties.ToArray();
- }
- private void AddFontStyle(List<XmlProperty> properties, Font font)
- {
- if (!fontStyles.ContainsKey(font))
- {
- fontStyles.Add(font, prefixStyle + "st" + fontStyles.Count.ToString());
- string styles = "\r\n ";
- foreach (KeyValuePair<Font, string> kv in fontStyles)
- {
- styles += " ." + kv.Value + "{" +
- "font-family:" + kv.Key.Name + ";" +
- "font-size:" + GetString(kv.Key.Size / 0.75f * fontDpiRatio) + "px;";
- if ((kv.Key.Style & FontStyle.Italic) == FontStyle.Italic)
- styles += "font-style:italic;";
- if ((kv.Key.Style & FontStyle.Bold) == FontStyle.Bold)
- styles += "font-weight:bold;";
- string decoration = "";
- if ((kv.Key.Style & FontStyle.Strikeout) != 0)
- decoration = "line-through";
- if ((kv.Key.Style & FontStyle.Underline) != 0)
- decoration += " underline";
- if (decoration != "")
- styles += "text-decoration:" + decoration + ";";
- styles += "}\r\n ";
- }
- xmlDocument.Root.FindItem("style").Value = styles;
- }
- properties.Add(XmlProperty.Create("class", fontStyles[font]));
- }
- private void AddString(string text, Font font, Brush brush, float left, float top, string anchor, string direction)
- {
- XmlItem obj = AddItem("text");
- List<XmlProperty> properties = new List<XmlProperty>();
- properties.Add(XmlProperty.Create("x", GetString(left + SpaceWidth(font) / 2)));
- properties.Add(XmlProperty.Create("y", GetString(top + font.Height * 0.75f)));
- properties.Add(XmlProperty.Create("xml:space", "preserve"));
- if (anchor != "")
- properties.Add(XmlProperty.Create("text-anchor", anchor));
- if (direction != "")
- properties.Add(XmlProperty.Create("direction", direction));
- AddFontStyle(properties, font);
- AddFill(properties, brush);
- obj.Value = text;
- obj.Properties = properties.ToArray();
- }
- #endregion
- #region Draw and measure text
- public void DrawString(string text, Font font, Brush brush, float left, float top)
- {
- AddString(text, font, brush, left, top, "", "");
- }
- public void DrawString(string text, Font font, Brush brush, RectangleF rect, StringFormat format)
- {
- // TODO: trimming
- float lineHeight = font.GetHeight(graphics);
- List<string> lines = new List<string>();
- if (rect.Width == 0 && rect.Height == 0)
- {
- lines.Add(text);
- }
- else
- {
- if ((format.FormatFlags & StringFormatFlags.NoWrap) == 0)
- format.Trimming = StringTrimming.Word;
- format.FormatFlags |= StringFormatFlags.LineLimit;
- SizeF bound = new SizeF(rect.Width, lineHeight * 1.25f);
- string[] paragraphs = text.Split('\n');
- for (int i = 0; i < paragraphs.Length; i++)
- {
- string s = paragraphs[i];
- s = s.Trim('\r');
- if (s.Length == 0)
- lines.Add("");
- while (s.Length > 0)
- {
- int charFill;
- int linesFill;
- graphics.MeasureString(s, font, bound, format, out charFill, out linesFill);
- if (linesFill == 0)
- break;
- lines.Add(s.Substring(0, charFill));
- s = s.Substring(charFill);
- }
- }
- }
- float dx = 0;
- string anchor = "";
- if (format.Alignment == StringAlignment.Center)
- {
- dx = (rect.Width - SpaceWidth(font)) / 2;
- anchor = "middle";
- }
- else if (format.Alignment == StringAlignment.Far)
- {
- dx = rect.Width - SpaceWidth(font);
- anchor = "end";
- }
- float dy = 0;
- if (format.LineAlignment == StringAlignment.Center)
- {
- dy = (rect.Height - lines.Count * lineHeight) / 2;
- }
- else if (format.LineAlignment == StringAlignment.Far)
- {
- dy = rect.Height - lines.Count * lineHeight;
- }
- string direction = "";
- if ((format.FormatFlags & StringFormatFlags.DirectionRightToLeft) != 0)
- {
- direction = "rtl";
- if (format.Alignment == StringAlignment.Far)
- {
- dx = 0;
- anchor = "end";
- }
- else if (format.Alignment == StringAlignment.Near)
- {
- dx = rect.Width - SpaceWidth(font);
- anchor = "";
- }
- }
- for (int i = 0; i < lines.Count; i++)
- {
- AddString(lines[i], font, brush, rect.Left + dx, rect.Top + dy + lineHeight * i, anchor, direction);
- }
- }
- public void DrawString(string text, Font font, Brush brush, RectangleF rect)
- {
- DrawString(text, font, brush, rect, StringFormat.GenericDefault);
- }
- public void DrawString(string text, Font font, Brush brush, float left, float top, StringFormat format)
- {
- DrawString(text, font, brush, new RectangleF(left, top, 0, 0), format);
- }
- public void DrawString(string text, Font font, Brush brush, PointF point, StringFormat format)
- {
- DrawString(text, font, brush, point.X, point.Y, format);
- }
- public Region[] MeasureCharacterRanges(string text, Font font, RectangleF textRect, StringFormat format)
- {
- return this.graphics.MeasureCharacterRanges(text, font, textRect, format);
- }
- public SizeF MeasureString(string text, Font font, SizeF layoutArea, StringFormat format)
- {
- return this.graphics.MeasureString(text, font, layoutArea, format);
- }
- public SizeF MeasureString(string text, Font font)
- {
- return this.graphics.MeasureString(text, font);
- }
- public SizeF MeasureString(string text, Font font, SizeF size)
- {
- return this.graphics.MeasureString(text, font, size);
- }
- public SizeF MeasureString(string text, Font font, int width, StringFormat format)
- {
- return this.graphics.MeasureString(text, font, width, format);
- }
- public void MeasureString(string text, Font font, SizeF size, StringFormat format, out int charsFit, out int linesFit)
- {
- this.graphics.MeasureString(text, font, size, format, out charsFit, out linesFit);
- }
- #endregion
- #region Draw images
- public void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit srcUnit)
- {
- if (image == null || image.Width == 0 || image.Height == 0)
- return;
- if (srcRect.X != 0 || srcRect.Y != 0 || srcRect.Width != image.Width || srcRect.Height != image.Height)
- {
- // crop source image
- using (Bitmap newImage = new Bitmap((int)srcRect.Width, (int)srcRect.Height))
- {
- using (Graphics g = Graphics.FromImage(newImage))
- {
- g.Clear(Color.Transparent);
- g.DrawImageUnscaled(image, 0, 0);
- }
- DrawImage(newImage, destRect);
- }
- }
- else
- {
- DrawImage(image, destRect);
- }
- }
- public void DrawImage(Image image, RectangleF rect)
- {
- DrawImage(image, rect.X, rect.Y, rect.Width, rect.Height);
- }
- public void DrawImage(Image image, float x, float y, float width, float height)
- {
- if (image == null || image.Width == 0 || image.Height == 0)
- return;
- XmlItem obj = AddItem("image");
- List<XmlProperty> properties = new List<XmlProperty>();
- properties.Add(XmlProperty.Create("x", GetString(x)));
- properties.Add(XmlProperty.Create("y", GetString(y)));
- properties.Add(XmlProperty.Create("width", GetString(width)));
- properties.Add(XmlProperty.Create("height", GetString(height)));
- string href = "";
- if (EmbeddedImages)
- {
- using (MemoryStream stream = new MemoryStream())
- {
- image.Save(stream, SvgImageFormat == SVGImageFormat.Png ? ImageFormat.Png : ImageFormat.Jpeg);
- href = "data:image/" + (SvgImageFormat == SVGImageFormat.Png ? "png" : "jpg") + "; base64," + Convert.ToBase64String(stream.ToArray());
- }
- }
- else
- {
- href = ImageFilePrefix + "." + imageFileId.ToString() + "." + (SvgImageFormat == SVGImageFormat.Png ? "png" : "jpg");
- image.Save(href);
- href = Path.GetFileName(href);
- imageFileId++;
- }
- properties.Add(XmlProperty.Create("href", href));
- obj.Properties = properties.ToArray();
- }
- public void DrawImage(Image image, PointF[] points)
- {
- if (image == null || image.Width == 0 || image.Height == 0)
- return;
- if (points.Length != 3)
- return;
- PointF p0 = points[0];
- PointF p1 = points[1];
- PointF p2 = points[2];
- RectangleF rect = new RectangleF(0, 0, image.Width, image.Height);
- float m11 = (p1.X - p0.X) / rect.Width;
- float m12 = (p1.Y - p0.Y) / rect.Width;
- float m21 = (p2.X - p0.X) / rect.Height;
- float m22 = (p2.Y - p0.Y) / rect.Height;
- IGraphicsState state = Save();
- MultiplyTransform(new System.Drawing.Drawing2D.Matrix(m11, m12, m21, m22, p0.X, p0.Y), MatrixOrder.Prepend);
- DrawImage(image, rect);
- Restore(state);
- }
- public void DrawImage(Image image, float x, float y)
- {
- if (image == null)
- return;
- DrawImage(image, x, y, image.Width, image.Height);
- }
- public void DrawImage(Image image, Rectangle destRect, int srcX, int srcY, int srcWidth, int srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttr)
- {
- DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttr);
- }
- public void DrawImage(Image image, Rectangle destRect, float srcX, float srcY, float srcWidth, float srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttrs)
- {
- using (Image newImage = new Bitmap(image.Width, image.Height))
- {
- using (Graphics g = Graphics.FromImage(newImage))
- {
- g.Clear(Color.Transparent);
- g.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height), 0, 0, image.Width, image.Height, srcUnit, imageAttrs);
- }
- DrawImage(newImage, destRect, new RectangleF(srcX, srcY, srcWidth, srcHeight), srcUnit);
- }
- }
- public void DrawImageUnscaled(Image image, Rectangle rect)
- {
- if (image == null)
- return;
- DrawImage(image, rect, 0, 0, rect.Width, rect.Height, GraphicsUnit.Pixel, null);
- }
- #endregion
- #region Draw geometry
- public void DrawArc(Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle)
- {
- using (GraphicsPath path = new GraphicsPath())
- {
- path.AddArc(x, y, width, height, startAngle, sweepAngle);
- AddPath(pen, null, path);
- }
- }
- public void DrawCurve(Pen pen, PointF[] points, int offset, int numberOfSegments, float tension)
- {
- using (GraphicsPath path = new GraphicsPath())
- {
- path.AddCurve(points, offset, numberOfSegments, tension);
- AddPath(pen, null, path);
- }
- }
- public void DrawEllipse(Pen pen, float left, float top, float width, float height)
- {
- AddEllipse(pen, null, left, top, width, height);
- }
- public void DrawEllipse(Pen pen, RectangleF rect)
- {
- DrawEllipse(pen, rect.Left, rect.Top, rect.Width, rect.Height);
- }
- public void DrawLine(Pen pen, float x1, float y1, float x2, float y2)
- {
- //TODO add caps
- XmlItem obj = AddItem("line");
- List<XmlProperty> properties = new List<XmlProperty>();
- properties.Add(XmlProperty.Create("x1", GetString(x1)));
- properties.Add(XmlProperty.Create("y1", GetString(y1)));
- properties.Add(XmlProperty.Create("x2", GetString(x2)));
- properties.Add(XmlProperty.Create("y2", GetString(y2)));
- AddStroke(properties, pen);
- obj.Properties = properties.ToArray();
- }
- public void DrawLine(Pen pen, PointF p1, PointF p2)
- {
- DrawLine(pen, p1.X, p1.Y, p2.X, p2.Y);
- }
- public void DrawLines(Pen pen, PointF[] points)
- {
- if (points.Length < 2)
- return;
- for (int i = 0; i < points.Length - 1; i++)
- {
- DrawLine(pen, points[i].X, points[i].Y, points[i + 1].X, points[i + 1].Y);
- }
- }
- public void DrawPath(Pen outlinePen, GraphicsPath path)
- {
- AddPath(outlinePen, null, path);
- }
- public void DrawPie(Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle)
- {
- using (GraphicsPath path = new GraphicsPath())
- {
- path.AddPie(x, y, width, height, startAngle, sweepAngle);
- AddPath(pen, null, path);
- }
- }
- public void DrawPolygon(Pen pen, PointF[] points)
- {
- AddPolygon(pen, null, points);
- }
- public void DrawPolygon(Pen pen, Point[] points)
- {
- if (points.Length == 0)
- return;
- PointF[] pointsF = new PointF[points.Length];
- for (int i = 0; i < points.Length; i++)
- pointsF[i] = points[i];
- DrawPolygon(pen, pointsF);
- }
- public void DrawRectangle(Pen pen, float left, float top, float width, float height)
- {
- AddRectangle(pen, null, left, top, width, height);
- }
- public void DrawRectangle(Pen pen, Rectangle rect)
- {
- DrawRectangle(pen, rect.Left, rect.Top, rect.Width, rect.Height);
- }
- #endregion
- #region Fill geometry
- public void FillEllipse(Brush brush, float left, float top, float width, float height)
- {
- AddEllipse(null, brush, left, top, width, height);
- }
- public void FillEllipse(Brush brush, RectangleF rect)
- {
- FillEllipse(brush, rect.Left, rect.Top, rect.Width, rect.Height);
- }
- public void FillPath(Brush brush, GraphicsPath path)
- {
- AddPath(null, brush, path);
- }
- public void FillPie(Brush brush, float x, float y, float width, float height, float startAngle, float sweepAngle)
- {
- using (GraphicsPath path = new GraphicsPath())
- {
- path.AddPie(x, y, width, height, startAngle, sweepAngle);
- AddPath(null, brush, path);
- }
- }
- public void FillPolygon(Brush brush, PointF[] points)
- {
- AddPolygon(null, brush, points);
- }
- public void FillPolygon(Brush brush, Point[] points)
- {
- if (points.Length == 0)
- return;
- PointF[] pointsF = new PointF[points.Length];
- for (int i = 0; i < points.Length; i++)
- pointsF[i] = points[i];
- FillPolygon(brush, pointsF);
- }
- public void FillRectangle(Brush brush, float left, float top, float width, float height)
- {
- AddRectangle(null, brush, left, top, width, height);
- }
- public void FillRectangle(Brush brush, RectangleF rect)
- {
- FillRectangle(brush, rect.Left, rect.Top, rect.Width, rect.Height);
- }
- public void FillRegion(Brush brush, Region region)
- {
- // TODO: check this case
- //throw new NotImplementedException();
- }
- public void FillAndDrawPath(Pen pen, Brush brush, GraphicsPath path)
- {
- AddPath(pen, brush, path);
- }
- public void FillAndDrawEllipse(Pen pen, Brush brush, RectangleF rect)
- {
- AddEllipse(pen, brush, rect.Left, rect.Top, rect.Width, rect.Height);
- }
- public void FillAndDrawEllipse(Pen pen, Brush brush, float left, float top, float width, float height)
- {
- AddEllipse(pen, brush, left, top, width, height);
- }
- public void FillAndDrawPolygon(Pen pen, Brush brush, Point[] points)
- {
- var pointsF = points.Select(point => (PointF)point).ToArray();
- AddPolygon(pen, brush, pointsF);
- }
- public void FillAndDrawPolygon(Pen pen, Brush brush, PointF[] points)
- {
- AddPolygon(pen, brush, points);
- }
- public void FillAndDrawRectangle(Pen pen, Brush brush, float left, float top, float width, float height)
- {
- AddRectangle(pen, brush, left, top, width, height);
- }
- #endregion
- #region Transform
- public void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix, MatrixOrder order)
- {
- needStateCheck = true;
- graphics.MultiplyTransform(matrix, order);
- }
- public void ScaleTransform(float scaleX, float scaleY)
- {
- needStateCheck = true;
- graphics.ScaleTransform(scaleX, scaleY);
- }
- public void TranslateTransform(float left, float top)
- {
- needStateCheck = true;
- graphics.TranslateTransform(left, top);
- }
- public void RotateTransform(float angle)
- {
- needStateCheck = true;
- graphics.RotateTransform(angle);
- }
- #endregion
- #region State
- public IGraphicsState Save()
- {
- return new SvgGraphicsState(graphics.Save());
- }
- public void Restore(IGraphicsState state)
- {
- needStateCheck = true;
- if (state is SvgGraphicsState)
- {
- graphics.Restore((state as SvgGraphicsState).State);
- }
- }
- #endregion
- #region Clip
- public bool IsVisible(RectangleF rect)
- {
- return true;// measureGraphics.IsVisible(rect);
- }
- public void ResetClip()
- {
- needStateCheck = true;
- graphics.ResetClip();
- }
- public void SetClip(RectangleF rect)
- {
- needStateCheck = true;
- graphics.SetClip(rect);
- }
- public void SetClip(RectangleF rect, CombineMode combineMode)
- {
- needStateCheck = true;
- graphics.SetClip(rect, combineMode);
- }
- public void SetClip(GraphicsPath path, CombineMode combineMode)
- {
- needStateCheck = true;
- graphics.SetClip(path, combineMode);
- }
- #endregion
- public class SvgGraphicsState : IGraphicsState
- {
- public GraphicsState State { get; }
- public SvgGraphicsState(GraphicsState state)
- {
- State = state;
- }
- }
- }
- }
|