SvgImage.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. using System;
  2. using System.Diagnostics;
  3. using System.Drawing;
  4. using System.Drawing.Drawing2D;
  5. using System.IO;
  6. using System.Net;
  7. using Svg.Transforms;
  8. #pragma warning disable
  9. namespace Svg
  10. {
  11. /// <summary>
  12. /// Represents and SVG image
  13. /// </summary>
  14. [SvgElement("image")]
  15. public class SvgImage : SvgVisualElement
  16. {
  17. /// <summary>
  18. /// Initializes a new instance of the <see cref="SvgImage"/> class.
  19. /// </summary>
  20. public SvgImage()
  21. {
  22. Width = new SvgUnit(0.0f);
  23. Height = new SvgUnit(0.0f);
  24. }
  25. private GraphicsPath _path;
  26. /// <summary>
  27. /// Gets an <see cref="SvgPoint"/> representing the top left point of the rectangle.
  28. /// </summary>
  29. public SvgPoint Location
  30. {
  31. get { return new SvgPoint(X, Y); }
  32. }
  33. /// <summary>
  34. /// Gets or sets the aspect of the viewport.
  35. /// </summary>
  36. /// <value></value>
  37. [SvgAttribute("preserveAspectRatio")]
  38. public SvgAspectRatio AspectRatio
  39. {
  40. get { return this.Attributes.GetAttribute<SvgAspectRatio>("preserveAspectRatio"); }
  41. set { this.Attributes["preserveAspectRatio"] = value; }
  42. }
  43. [SvgAttribute("x")]
  44. public virtual SvgUnit X
  45. {
  46. get { return this.Attributes.GetAttribute<SvgUnit>("x"); }
  47. set { this.Attributes["x"] = value; }
  48. }
  49. [SvgAttribute("y")]
  50. public virtual SvgUnit Y
  51. {
  52. get { return this.Attributes.GetAttribute<SvgUnit>("y"); }
  53. set { this.Attributes["y"] = value; }
  54. }
  55. [SvgAttribute("width")]
  56. public virtual SvgUnit Width
  57. {
  58. get { return this.Attributes.GetAttribute<SvgUnit>("width"); }
  59. set { this.Attributes["width"] = value; }
  60. }
  61. [SvgAttribute("height")]
  62. public virtual SvgUnit Height
  63. {
  64. get { return this.Attributes.GetAttribute<SvgUnit>("height"); }
  65. set { this.Attributes["height"] = value; }
  66. }
  67. [SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
  68. public virtual string Href
  69. {
  70. get { return this.Attributes.GetAttribute<string>("href"); }
  71. set { this.Attributes["href"] = value; }
  72. }
  73. /// <summary>
  74. /// Gets the bounds of the element.
  75. /// </summary>
  76. /// <value>The bounds.</value>
  77. public override RectangleF Bounds
  78. {
  79. get { return new RectangleF(this.Location.ToDeviceValue(null, this),
  80. new SizeF(this.Width.ToDeviceValue(null, UnitRenderingType.Horizontal, this),
  81. this.Height.ToDeviceValue(null, UnitRenderingType.Vertical, this))); }
  82. }
  83. /// <summary>
  84. /// Gets the <see cref="GraphicsPath"/> for this element.
  85. /// </summary>
  86. public override GraphicsPath Path(ISvgRenderer renderer)
  87. {
  88. if (_path == null)
  89. {
  90. // Same size of rectangle can suffice to provide bounds of the image
  91. var rectangle = new RectangleF(Location.ToDeviceValue(renderer, this),
  92. SvgUnit.GetDeviceSize(Width, Height, renderer, this));
  93. _path = new GraphicsPath();
  94. _path.StartFigure();
  95. _path.AddRectangle(rectangle);
  96. _path.CloseFigure();
  97. }
  98. return _path;
  99. }
  100. /// <summary>
  101. /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
  102. /// </summary>
  103. protected override void Render(ISvgRenderer renderer)
  104. {
  105. if (!Visible || !Displayable)
  106. return;
  107. if (Width.Value > 0.0f && Height.Value > 0.0f && this.Href != null)
  108. {
  109. var img = GetImage();
  110. if (img != null)
  111. {
  112. RectangleF srcRect;
  113. var bmp = img as Image;
  114. var svg = img as SvgFragment;
  115. if (bmp != null)
  116. {
  117. srcRect = new RectangleF(0, 0, bmp.Width, bmp.Height);
  118. }
  119. else if (svg != null)
  120. {
  121. srcRect = new RectangleF(new PointF(0, 0), svg.GetDimensions());
  122. }
  123. else
  124. {
  125. return;
  126. }
  127. var destClip = new RectangleF(this.Location.ToDeviceValue(renderer, this),
  128. new SizeF(Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
  129. Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this)));
  130. RectangleF destRect = destClip;
  131. this.PushTransforms(renderer);
  132. renderer.SetClip(new Region(destClip), CombineMode.Intersect);
  133. this.SetClip(renderer);
  134. if (AspectRatio != null && AspectRatio.Align != SvgPreserveAspectRatio.none)
  135. {
  136. var fScaleX = destClip.Width / srcRect.Width;
  137. var fScaleY = destClip.Height / srcRect.Height;
  138. var xOffset = 0.0f;
  139. var yOffset = 0.0f;
  140. if (AspectRatio.Slice)
  141. {
  142. fScaleX = Math.Max(fScaleX, fScaleY);
  143. fScaleY = Math.Max(fScaleX, fScaleY);
  144. }
  145. else
  146. {
  147. fScaleX = Math.Min(fScaleX, fScaleY);
  148. fScaleY = Math.Min(fScaleX, fScaleY);
  149. }
  150. switch (AspectRatio.Align)
  151. {
  152. case SvgPreserveAspectRatio.xMinYMin:
  153. break;
  154. case SvgPreserveAspectRatio.xMidYMin:
  155. xOffset = (destClip.Width - srcRect.Width * fScaleX) / 2;
  156. break;
  157. case SvgPreserveAspectRatio.xMaxYMin:
  158. xOffset = (destClip.Width - srcRect.Width * fScaleX);
  159. break;
  160. case SvgPreserveAspectRatio.xMinYMid:
  161. yOffset = (destClip.Height - srcRect.Height * fScaleY) / 2;
  162. break;
  163. case SvgPreserveAspectRatio.xMidYMid:
  164. xOffset = (destClip.Width - srcRect.Width * fScaleX) / 2;
  165. yOffset = (destClip.Height - srcRect.Height * fScaleY) / 2;
  166. break;
  167. case SvgPreserveAspectRatio.xMaxYMid:
  168. xOffset = (destClip.Width - srcRect.Width * fScaleX);
  169. yOffset = (destClip.Height - srcRect.Height * fScaleY) / 2;
  170. break;
  171. case SvgPreserveAspectRatio.xMinYMax:
  172. yOffset = (destClip.Height - srcRect.Height * fScaleY);
  173. break;
  174. case SvgPreserveAspectRatio.xMidYMax:
  175. xOffset = (destClip.Width - srcRect.Width * fScaleX) / 2;
  176. yOffset = (destClip.Height - srcRect.Height * fScaleY);
  177. break;
  178. case SvgPreserveAspectRatio.xMaxYMax:
  179. xOffset = (destClip.Width - srcRect.Width * fScaleX);
  180. yOffset = (destClip.Height - srcRect.Height * fScaleY);
  181. break;
  182. }
  183. destRect = new RectangleF(destClip.X + xOffset, destClip.Y + yOffset,
  184. srcRect.Width * fScaleX, srcRect.Height * fScaleY);
  185. }
  186. if (bmp != null)
  187. {
  188. renderer.DrawImage(bmp, destRect, srcRect, GraphicsUnit.Pixel);
  189. bmp.Dispose();
  190. }
  191. else if (svg != null)
  192. {
  193. var currOffset = new PointF(renderer.Transform.OffsetX, renderer.Transform.OffsetY);
  194. renderer.TranslateTransform(-currOffset.X, -currOffset.Y);
  195. renderer.ScaleTransform(destRect.Width / srcRect.Width, destRect.Height / srcRect.Height);
  196. renderer.TranslateTransform(currOffset.X + destRect.X, currOffset.Y + destRect.Y);
  197. renderer.SetBoundable(new GenericBoundable(srcRect));
  198. svg.RenderElement(renderer);
  199. renderer.PopBoundable();
  200. }
  201. this.ResetClip(renderer);
  202. this.PopTransforms(renderer);
  203. }
  204. // TODO: cache images... will need a shared context for this
  205. // TODO: support preserveAspectRatio, etc
  206. }
  207. }
  208. public object GetImage()
  209. {
  210. return this.GetImage(this.Href);
  211. }
  212. public object GetImage(string uriString)
  213. {
  214. System.Net.ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00);
  215. string safeUriString;
  216. if (uriString.Length > 65519)
  217. {
  218. //Uri MaxLength is 65519 (https://msdn.microsoft.com/en-us/library/z6c2z492.aspx)
  219. safeUriString = uriString.Substring(0, 65519);
  220. }
  221. else
  222. {
  223. safeUriString = uriString;
  224. }
  225. try
  226. {
  227. var uri = new Uri(safeUriString, UriKind.RelativeOrAbsolute);
  228. // handle data/uri embedded images (http://en.wikipedia.org/wiki/Data_URI_scheme)
  229. if (uri.IsAbsoluteUri && uri.Scheme == "data")
  230. {
  231. int dataIdx = uriString.IndexOf(",") + 1;
  232. if (dataIdx <= 0 || dataIdx + 1 > uriString.Length)
  233. throw new Exception("Invalid data URI");
  234. // we're assuming base64, as ascii encoding would be *highly* unsusual for images
  235. // also assuming it's png or jpeg mimetype
  236. byte[] imageBytes = Convert.FromBase64String(uriString.Substring(dataIdx));
  237. using (var stream = new MemoryStream(imageBytes))
  238. {
  239. return Image.FromStream(stream);
  240. }
  241. }
  242. if (!uri.IsAbsoluteUri)
  243. {
  244. uri = new Uri(OwnerDocument.BaseUri, uri);
  245. }
  246. // should work with http: and file: protocol urls
  247. var httpRequest = WebRequest.Create(uri);
  248. using (WebResponse webResponse = httpRequest.GetResponse())
  249. {
  250. using (var stream = webResponse.GetResponseStream())
  251. {
  252. if (stream.CanSeek)
  253. {
  254. stream.Position = 0;
  255. }
  256. if (uri.LocalPath.EndsWith(".svg", StringComparison.InvariantCultureIgnoreCase))
  257. {
  258. var doc = SvgDocument.Open<SvgDocument>(stream);
  259. doc.BaseUri = uri;
  260. return doc;
  261. }
  262. else
  263. {
  264. return Bitmap.FromStream(stream);
  265. }
  266. }
  267. }
  268. }
  269. catch (Exception ex)
  270. {
  271. Trace.TraceError("Error loading image: '{0}', error: {1} ", uriString, ex.Message);
  272. return null;
  273. }
  274. }
  275. protected static MemoryStream BufferToMemoryStream(Stream input)
  276. {
  277. MemoryStream ms = new MemoryStream();
  278. const int BUFFER_SIZE = 4 * 1024;
  279. input.CopyTo(ms, BUFFER_SIZE);
  280. ms.Seek(0, SeekOrigin.Begin);
  281. return ms;
  282. }
  283. public override SvgElement DeepCopy()
  284. {
  285. return DeepCopy<SvgImage>();
  286. }
  287. public override SvgElement DeepCopy<T>()
  288. {
  289. var newObj = base.DeepCopy<T>() as SvgImage;
  290. newObj.Height = this.Height;
  291. newObj.Width = this.Width;
  292. newObj.X = this.X;
  293. newObj.Y = this.Y;
  294. newObj.Href = this.Href;
  295. return newObj;
  296. }
  297. }
  298. }
  299. #pragma warning restore