SvgImage.cs 13 KB

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