ImageDetectExt.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.IO.Compression;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Xml;
  8. namespace Masuit.Tools.Media;
  9. public static class ImageDetectExt
  10. {
  11. private const float MToInch = 39.3700787F;
  12. private const float CmToInch = MToInch * 0.01F;
  13. private const float MmToInch = CmToInch * 0.1F;
  14. private const float HundredthThMmToInch = MmToInch * 0.01F;
  15. internal const float StandardDPI = 96f;
  16. internal struct TifIfd
  17. {
  18. public short Tag;
  19. public short Type;
  20. public int Count;
  21. public int ValueOffset;
  22. }
  23. public static bool IsImage(this Stream s)
  24. {
  25. return GetImageType(s) != null;
  26. }
  27. /// <summary>
  28. /// 获取图像格式
  29. /// </summary>
  30. /// <param name="ms"></param>
  31. /// <returns></returns>
  32. public static ImageFormat? GetImageType(this Stream ms)
  33. {
  34. using var br = new BinaryReader(ms);
  35. if (IsJpg(br))
  36. {
  37. return ImageFormat.Jpg;
  38. }
  39. if (IsBmp(br, out _))
  40. {
  41. return ImageFormat.Bmp;
  42. }
  43. if (IsGif(br))
  44. {
  45. return ImageFormat.Gif;
  46. }
  47. if (IsPng(br))
  48. {
  49. return ImageFormat.Png;
  50. }
  51. if (IsTif(br, out _))
  52. {
  53. return ImageFormat.Tif;
  54. }
  55. if (IsIco(br))
  56. {
  57. return ImageFormat.Ico;
  58. }
  59. if (IsWebP(br))
  60. {
  61. return ImageFormat.WebP;
  62. }
  63. if (IsEmf(br))
  64. {
  65. return ImageFormat.Emf;
  66. }
  67. if (IsWmf(br))
  68. {
  69. return ImageFormat.Wmf;
  70. }
  71. if (IsSvg(ms))
  72. {
  73. return ImageFormat.Svg;
  74. }
  75. if (IsGZip(br))
  76. {
  77. _ = ExtractImage(ToArray(ms), out ImageFormat? pt);
  78. return pt;
  79. }
  80. return null;
  81. }
  82. /// <summary>
  83. ///
  84. /// </summary>
  85. /// <param name="stream"></param>
  86. /// <returns></returns>
  87. public static byte[] ToArray(Stream stream)
  88. {
  89. stream.Position = 0;
  90. byte[] bytes = new byte[stream.Length];
  91. stream.Read(bytes, 0, bytes.Length);
  92. // 设置当前流的位置为流的开始
  93. stream.Seek(0, SeekOrigin.Begin);
  94. return bytes;
  95. }
  96. private static bool IsGZip(BinaryReader br)
  97. {
  98. br.BaseStream.Position = 0;
  99. var sign = br.ReadBytes(2);
  100. return IsGZip(sign);
  101. }
  102. private static bool IsGZip(byte[] sign)
  103. {
  104. return sign.Length >= 2 && sign[0] == 0x1F && sign[1] == 0x8B;
  105. }
  106. internal static bool TryGetImageBounds(ImageFormat imageFormat, MemoryStream ms, ref double width, ref double height, out double horizontalResolution, out double verticalResolution)
  107. {
  108. width = 0;
  109. height = 0;
  110. horizontalResolution = verticalResolution = StandardDPI;
  111. try
  112. {
  113. ms.Seek(0, SeekOrigin.Begin);
  114. if (imageFormat == ImageFormat.Bmp && IsBmp(ms, ref width, ref height, ref horizontalResolution, ref verticalResolution))
  115. {
  116. return true;
  117. }
  118. if (imageFormat == ImageFormat.Jpg && IsJpg(ms, ref width, ref height, ref horizontalResolution, ref verticalResolution))
  119. {
  120. return true;
  121. }
  122. if (imageFormat == ImageFormat.Gif && IsGif(ms, ref width, ref height))
  123. {
  124. return true;
  125. }
  126. if (imageFormat == ImageFormat.Png && IsPng(ms, ref width, ref height, ref horizontalResolution, ref verticalResolution))
  127. {
  128. return true;
  129. }
  130. if (imageFormat == ImageFormat.Emf && IsEmf(ms, ref width, ref height, ref horizontalResolution, ref verticalResolution))
  131. {
  132. return true;
  133. }
  134. if (imageFormat == ImageFormat.Wmf && IsWmf(ms, ref width, ref height, ref horizontalResolution, ref verticalResolution))
  135. {
  136. return true;
  137. }
  138. else if (imageFormat == ImageFormat.Svg && IsSvg(ms, ref width, ref height))
  139. {
  140. return true;
  141. }
  142. else if (imageFormat == ImageFormat.Tif && IsTif(ms, ref width, ref height, ref horizontalResolution, ref verticalResolution))
  143. {
  144. return true;
  145. }
  146. else if (imageFormat == ImageFormat.WebP && IsWebP(ms, ref width, ref height, ref horizontalResolution, ref verticalResolution))
  147. {
  148. return true;
  149. }
  150. else if (imageFormat == ImageFormat.Ico && IsIcon(ms, ref width, ref height))
  151. {
  152. return true;
  153. }
  154. return false;
  155. }
  156. catch
  157. {
  158. return false;
  159. }
  160. }
  161. internal static byte[] ExtractImage(byte[] img, out ImageFormat? type)
  162. {
  163. if (IsGZip(img))
  164. {
  165. try
  166. {
  167. var ms = new MemoryStream(img);
  168. var msOut = new MemoryStream();
  169. const int bufferSize = 4096;
  170. var buffer = new byte[bufferSize];
  171. using var z = new GZipStream(ms, CompressionMode.Decompress);
  172. int size = 0;
  173. do
  174. {
  175. size = z.Read(buffer, 0, bufferSize);
  176. if (size > 0)
  177. {
  178. msOut.Write(buffer, 0, size);
  179. }
  180. }
  181. while (size == bufferSize);
  182. msOut.Position = 0;
  183. var br = new BinaryReader(msOut);
  184. if (IsEmf(br))
  185. {
  186. type = ImageFormat.Emf;
  187. }
  188. else if (IsWmf(br))
  189. {
  190. type = ImageFormat.Wmf;
  191. }
  192. else
  193. {
  194. type = null;
  195. }
  196. msOut.Position = 0;
  197. return msOut.ToArray();
  198. }
  199. catch
  200. {
  201. type = null;
  202. return img;
  203. }
  204. }
  205. type = null;
  206. return img;
  207. }
  208. private static bool IsJpg(MemoryStream ms, ref double width, ref double height, ref double horizontalResolution, ref double verticalResolution)
  209. {
  210. using (var br = new BinaryReader(ms))
  211. {
  212. if (IsJpg(br))
  213. {
  214. float xDensity = 1, yDensity = 1;
  215. while (ms.Position < ms.Length)
  216. {
  217. var id = GetUInt16BigEndian(br);
  218. var length = (int)GetInt16BigEndian(br);
  219. switch (id)
  220. {
  221. case 0xFFE0:
  222. var identifier = br.ReadBytes(5); //JFIF\0
  223. var version = br.ReadBytes(2);
  224. var unit = br.ReadByte();
  225. xDensity = (int)GetInt16BigEndian(br);
  226. yDensity = (int)GetInt16BigEndian(br);
  227. if (unit == 1)
  228. {
  229. horizontalResolution = xDensity;
  230. verticalResolution = yDensity;
  231. }
  232. else if (unit == 2)
  233. {
  234. horizontalResolution = xDensity * CmToInch;
  235. verticalResolution = yDensity * CmToInch;
  236. }
  237. ms.Position += length - 14;
  238. break;
  239. case 0xFFE1:
  240. var pos = ms.Position;
  241. identifier = br.ReadBytes(6); //EXIF\0\0 or //EXIF\FF\FF
  242. double w = 0, h = 0;
  243. ReadTiffHeader(br, ref w, ref h, ref horizontalResolution, ref verticalResolution);
  244. ms.Position = pos + length - 2;
  245. break;
  246. case 0xFFC0:
  247. case 0xFFC1:
  248. case 0xFFC2:
  249. var precision = br.ReadByte(); //Bits
  250. height = GetUInt16BigEndian(br);
  251. width = GetUInt16BigEndian(br);
  252. br.Close();
  253. return true;
  254. case 0xFFD9:
  255. return height != 0 && width != 0;
  256. default:
  257. ms.Position += length - 2;
  258. break;
  259. }
  260. }
  261. }
  262. return false;
  263. }
  264. }
  265. private static bool IsJpg(BinaryReader br)
  266. {
  267. br.BaseStream.Position = 0;
  268. var sign = br.ReadBytes(2); //FF D8
  269. return sign.Length >= 2 && sign[0] == 0xFF && sign[1] == 0xD8;
  270. }
  271. private static bool IsGif(MemoryStream ms, ref double width, ref double height)
  272. {
  273. using (var br = new BinaryReader(ms))
  274. {
  275. if (IsGif(br))
  276. {
  277. width = br.ReadUInt16();
  278. height = br.ReadUInt16();
  279. br.Close();
  280. return true;
  281. }
  282. }
  283. return false;
  284. }
  285. private static bool IsGif(BinaryReader br)
  286. {
  287. br.BaseStream.Seek(0, SeekOrigin.Begin);
  288. var b = br.ReadBytes(6);
  289. return b[0] == 0x47 && b[1] == 0x49 && b[2] == 0x46; //byte 4-6 contains the version, but we don't check them here.
  290. }
  291. private static bool IsBmp(MemoryStream ms, ref double width, ref double height, ref double horizontalResolution, ref double verticalResolution)
  292. {
  293. using (var br = new BinaryReader(ms))
  294. {
  295. if (IsBmp(br, out string sign))
  296. {
  297. var size = br.ReadInt32();
  298. var reserved = br.ReadBytes(4);
  299. var offsetData = br.ReadInt32();
  300. //Info Header
  301. var ihSize = br.ReadInt32(); //Should be 40
  302. width = br.ReadInt32();
  303. height = br.ReadInt32();
  304. if (sign == "BM")
  305. {
  306. br.ReadBytes(12);
  307. horizontalResolution = br.ReadInt32() / MToInch;
  308. verticalResolution = br.ReadInt32() / MToInch;
  309. }
  310. else
  311. {
  312. horizontalResolution = verticalResolution = 1;
  313. }
  314. return true;
  315. }
  316. }
  317. return false;
  318. }
  319. internal static bool IsBmp(BinaryReader br, out string sign)
  320. {
  321. try
  322. {
  323. br.BaseStream.Seek(0, SeekOrigin.Begin);
  324. sign = Encoding.ASCII.GetString(br.ReadBytes(2)); //BM for a Windows bitmap
  325. return (sign == "BM" || sign == "BA" || sign == "CI" || sign == "CP" || sign == "IC" || sign == "PT");
  326. }
  327. catch
  328. {
  329. sign = null;
  330. return false;
  331. }
  332. }
  333. #region Ico
  334. private static bool IsIcon(MemoryStream ms, ref double width, ref double height)
  335. {
  336. using (var br = new BinaryReader(ms))
  337. {
  338. if (IsIco(br))
  339. {
  340. var imageCount = br.ReadInt16();
  341. width = br.ReadByte();
  342. if (width == 0) width = 256;
  343. height = br.ReadByte();
  344. if (height == 0) height = 256;
  345. br.Close();
  346. return true;
  347. }
  348. br.Close();
  349. return false;
  350. }
  351. }
  352. internal static bool IsIco(BinaryReader br)
  353. {
  354. br.BaseStream.Seek(0, SeekOrigin.Begin);
  355. var type0 = br.ReadInt16();
  356. var type1 = br.ReadInt16();
  357. return type0 == 0 && type1 == 1;
  358. }
  359. #endregion Ico
  360. #region WebP
  361. private static bool IsWebP(MemoryStream ms, ref double width, ref double height, ref double horizontalResolution, ref double verticalResolution)
  362. {
  363. width = height = 0;
  364. horizontalResolution = verticalResolution = StandardDPI * (1 + 1 / 3); //Excel seems to render webp at 1 1/3 size.
  365. using (var br = new BinaryReader(ms))
  366. {
  367. if (IsWebP(br))
  368. {
  369. var vp8 = Encoding.ASCII.GetString(br.ReadBytes(4));
  370. switch (vp8)
  371. {
  372. case "VP8 ":
  373. var b = br.ReadBytes(10);
  374. var w = br.ReadInt16();
  375. width = w & 0x3FFF;
  376. var hScale = w >> 14;
  377. var h = br.ReadInt16();
  378. height = h & 0x3FFF;
  379. hScale = h >> 14;
  380. break;
  381. case "VP8X":
  382. br.ReadBytes(8);
  383. b = br.ReadBytes(6);
  384. width = BitConverter.ToInt32(new byte[] { b[0], b[1], b[2], 0 }, 0) + 1;
  385. height = BitConverter.ToInt32(new byte[] { b[3], b[4], b[5], 0 }, 0) + 1;
  386. break;
  387. case "VP8L":
  388. br.ReadBytes(5);
  389. b = br.ReadBytes(4);
  390. width = (b[0] | (b[1] & 0x3F) << 8) + 1;
  391. height = (b[1] >> 6 | b[2] << 2 | (b[3] & 0x0F) << 10) + 1;
  392. break;
  393. }
  394. }
  395. }
  396. return width != 0 && height != 0;
  397. }
  398. internal static bool IsWebP(BinaryReader br)
  399. {
  400. try
  401. {
  402. br.BaseStream.Seek(0, SeekOrigin.Begin);
  403. var riff = Encoding.ASCII.GetString(br.ReadBytes(4));
  404. var length = GetInt32BigEndian(br);
  405. var webP = Encoding.ASCII.GetString(br.ReadBytes(4));
  406. return riff == "RIFF" && webP == "WEBP";
  407. }
  408. catch
  409. {
  410. return false;
  411. }
  412. }
  413. #endregion WebP
  414. #region Tiff
  415. private static bool IsTif(MemoryStream ms, ref double width, ref double height, ref double horizontalResolution, ref double verticalResolution)
  416. {
  417. using (var br = new BinaryReader(ms))
  418. {
  419. return ReadTiffHeader(br, ref width, ref height, ref horizontalResolution, ref verticalResolution);
  420. }
  421. }
  422. private static bool ReadTiffHeader(BinaryReader br, ref double width, ref double height, ref double horizontalResolution, ref double verticalResolution)
  423. {
  424. var ms = br.BaseStream;
  425. var pos = ms.Position;
  426. if (IsTif(br, out bool isBigEndian, false))
  427. {
  428. var offset = GetTifInt32(br, isBigEndian);
  429. ms.Position = pos + offset;
  430. var numberOfIdf = GetTifInt16(br, isBigEndian);
  431. var ifds = new List<TifIfd>();
  432. for (int i = 0; i < numberOfIdf; i++)
  433. {
  434. var ifd = new TifIfd()
  435. {
  436. Tag = GetTifInt16(br, isBigEndian),
  437. Type = GetTifInt16(br, isBigEndian),
  438. Count = GetTifInt32(br, isBigEndian),
  439. };
  440. if (ifd.Type == 1 || ifd.Type == 2 || ifd.Type == 6 || ifd.Type == 7)
  441. {
  442. ifd.ValueOffset = br.ReadByte();
  443. br.ReadBytes(3);
  444. }
  445. else if (ifd.Type == 3 || ifd.Type == 8)
  446. {
  447. ifd.ValueOffset = GetTifInt16(br, isBigEndian);
  448. br.ReadBytes(2);
  449. }
  450. else
  451. {
  452. ifd.ValueOffset = GetTifInt32(br, isBigEndian);
  453. }
  454. ifds.Add(ifd);
  455. }
  456. int resolutionUnit = 2;
  457. foreach (var ifd in ifds)
  458. {
  459. switch (ifd.Tag)
  460. {
  461. case 0x100:
  462. width = ifd.ValueOffset;
  463. break;
  464. case 0x101:
  465. height = ifd.ValueOffset;
  466. break;
  467. case 0x11A:
  468. ms.Position = ifd.ValueOffset + pos;
  469. var l1 = GetTifInt32(br, isBigEndian);
  470. var l2 = GetTifInt32(br, isBigEndian);
  471. horizontalResolution = l1 / l2;
  472. break;
  473. case 0x11B:
  474. ms.Position = ifd.ValueOffset + pos;
  475. l1 = GetTifInt32(br, isBigEndian);
  476. l2 = GetTifInt32(br, isBigEndian);
  477. verticalResolution = l1 / l2;
  478. break;
  479. case 0x128:
  480. resolutionUnit = ifd.ValueOffset;
  481. break;
  482. }
  483. }
  484. if (resolutionUnit == 1)
  485. {
  486. horizontalResolution *= CmToInch;
  487. verticalResolution *= CmToInch;
  488. }
  489. }
  490. return width != 0 && height != 0;
  491. }
  492. private static bool IsTif(BinaryReader br, out bool isBigEndian, bool resetPos = false)
  493. {
  494. try
  495. {
  496. if (resetPos)
  497. {
  498. br.BaseStream.Position = 0;
  499. }
  500. var b = br.ReadBytes(2);
  501. isBigEndian = Encoding.ASCII.GetString(b) == "MM";
  502. var identifier = GetTifInt16(br, isBigEndian);
  503. if (identifier == 42)
  504. {
  505. return true;
  506. }
  507. }
  508. catch
  509. {
  510. isBigEndian = false;
  511. return false;
  512. }
  513. return false;
  514. }
  515. private static short GetTifInt16(BinaryReader br, bool isBigEndian)
  516. {
  517. if (isBigEndian)
  518. {
  519. return GetInt16BigEndian(br);
  520. }
  521. else
  522. {
  523. return br.ReadInt16();
  524. }
  525. }
  526. private static int GetTifInt32(BinaryReader br, bool isBigEndian)
  527. {
  528. if (isBigEndian)
  529. {
  530. return GetInt32BigEndian(br);
  531. }
  532. else
  533. {
  534. return br.ReadInt32();
  535. }
  536. }
  537. #endregion Tiff
  538. #region Emf
  539. private static bool IsEmf(MemoryStream ms, ref double width, ref double height, ref double horizontalResolution, ref double verticalResolution)
  540. {
  541. using (var br = new BinaryReader(ms))
  542. {
  543. if (IsEmf(br))
  544. {
  545. var length = br.ReadInt32();
  546. var bounds = new int[4];
  547. bounds[0] = br.ReadInt32();
  548. bounds[1] = br.ReadInt32();
  549. bounds[2] = br.ReadInt32();
  550. bounds[3] = br.ReadInt32();
  551. var frame = new int[4];
  552. frame[0] = br.ReadInt32();
  553. frame[1] = br.ReadInt32();
  554. frame[2] = br.ReadInt32();
  555. frame[3] = br.ReadInt32();
  556. var signatureBytes = br.ReadBytes(4);
  557. var signature = Encoding.ASCII.GetString(signatureBytes);
  558. if (signature.Trim() == "EMF")
  559. {
  560. var version = br.ReadUInt32();
  561. var size = br.ReadUInt32();
  562. var records = br.ReadUInt32();
  563. var handles = br.ReadUInt16();
  564. var reserved = br.ReadUInt16();
  565. var nDescription = br.ReadUInt32();
  566. var offDescription = br.ReadUInt32();
  567. var nPalEntries = br.ReadUInt32();
  568. var device = new uint[2];
  569. device[0] = br.ReadUInt32();
  570. device[1] = br.ReadUInt32();
  571. var mm = new uint[2];
  572. mm[0] = br.ReadUInt32();
  573. mm[1] = br.ReadUInt32();
  574. //Extension 1
  575. var cbPixelFormat = br.ReadUInt32();
  576. var offPixelFormat = br.ReadUInt32();
  577. var bOpenGL = br.ReadUInt32();
  578. //Extension 2
  579. var hr = br.ReadUInt32();
  580. var vr = br.ReadUInt32();
  581. var id = br.ReadInt32();
  582. var size2 = br.ReadInt32();
  583. width = (bounds[2] - bounds[0] + 1);
  584. height = (bounds[3] - bounds[1] + 1);
  585. horizontalResolution = width / ((frame[2] - frame[0]) * HundredthThMmToInch * StandardDPI) * StandardDPI;
  586. verticalResolution = height / ((frame[3] - frame[1]) * HundredthThMmToInch * StandardDPI) * StandardDPI;
  587. return true;
  588. }
  589. }
  590. }
  591. return false;
  592. }
  593. private static bool IsEmf(BinaryReader br)
  594. {
  595. br.BaseStream.Position = 0;
  596. var type = br.ReadInt32();
  597. return type == 1;
  598. }
  599. #endregion Emf
  600. #region Wmf
  601. private const double PIXELS_PER_TWIPS = 1D / 15D;
  602. private const double DEFAULT_TWIPS = 1440D;
  603. private static bool IsWmf(MemoryStream ms, ref double width, ref double height, ref double horizontalResolution, ref double verticalResolution)
  604. {
  605. using (var br = new BinaryReader(ms))
  606. {
  607. if (IsWmf(br))
  608. {
  609. var HWmf = br.ReadInt16();
  610. var bounds = new ushort[4];
  611. bounds[0] = br.ReadUInt16();
  612. bounds[1] = br.ReadUInt16();
  613. bounds[2] = br.ReadUInt16();
  614. bounds[3] = br.ReadUInt16();
  615. var inch = br.ReadInt16();
  616. width = bounds[2] - bounds[0];
  617. height = bounds[3] - bounds[1];
  618. if (inch != 0)
  619. {
  620. width *= (DEFAULT_TWIPS / inch) * PIXELS_PER_TWIPS;
  621. height *= (DEFAULT_TWIPS / inch) * PIXELS_PER_TWIPS;
  622. }
  623. return width != 0 && height != 0;
  624. }
  625. }
  626. return false;
  627. }
  628. private static bool IsWmf(BinaryReader br)
  629. {
  630. br.BaseStream.Position = 0;
  631. var key = br.ReadUInt32();
  632. return key == 0x9AC6CDD7;
  633. }
  634. #endregion Wmf
  635. #region Png
  636. private static bool IsPng(MemoryStream ms, ref double width, ref double height, ref double horizontalResolution, ref double verticalResolution)
  637. {
  638. using (var br = new BinaryReader(ms))
  639. {
  640. return IsPng(br, ref width, ref height, ref horizontalResolution, ref verticalResolution);
  641. }
  642. }
  643. private static bool IsPng(BinaryReader br, ref double width, ref double height, ref double horizontalResolution, ref double verticalResolution, long fileEndPosition = long.MinValue)
  644. {
  645. if (IsPng(br))
  646. {
  647. if (fileEndPosition == long.MinValue)
  648. {
  649. fileEndPosition = br.BaseStream.Length;
  650. }
  651. while (br.BaseStream.Position < fileEndPosition)
  652. {
  653. var chunkType = ReadPngChunkHeader(br, out int length);
  654. switch (chunkType)
  655. {
  656. case "IHDR":
  657. width = GetInt32BigEndian(br);
  658. height = GetInt32BigEndian(br);
  659. br.ReadBytes(5); //Ignored bytes, Depth compression etc.
  660. break;
  661. case "pHYs":
  662. horizontalResolution = GetInt32BigEndian(br);
  663. verticalResolution = GetInt32BigEndian(br);
  664. var unitSpecifier = br.ReadByte();
  665. if (unitSpecifier == 1)
  666. {
  667. horizontalResolution /= MToInch;
  668. verticalResolution /= MToInch;
  669. }
  670. br.Close();
  671. return true;
  672. default:
  673. br.ReadBytes(length);
  674. break;
  675. }
  676. var crc = br.ReadInt32();
  677. }
  678. }
  679. br.Close();
  680. return width != 0 && height != 0;
  681. }
  682. private static bool IsPng(BinaryReader br)
  683. {
  684. br.BaseStream.Position = 0;
  685. var signature = br.ReadBytes(8);
  686. return signature.SequenceEqual(new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 });
  687. }
  688. private static string ReadPngChunkHeader(BinaryReader br, out int length)
  689. {
  690. length = GetInt32BigEndian(br);
  691. var b = br.ReadBytes(4);
  692. var type = Encoding.ASCII.GetString(b);
  693. return type;
  694. }
  695. #endregion Png
  696. #region Svg
  697. private static bool IsSvg(MemoryStream ms, ref double width, ref double height)
  698. {
  699. try
  700. {
  701. var reader = new XmlTextReader(ms);
  702. while (reader.Read())
  703. {
  704. if (reader.LocalName == "svg" && reader.NodeType == XmlNodeType.Element)
  705. {
  706. var w = reader.GetAttribute("width");
  707. var h = reader.GetAttribute("height");
  708. var vb = reader.GetAttribute("viewBox");
  709. reader.Close();
  710. if (w == null || h == null)
  711. {
  712. if (vb == null)
  713. {
  714. return false;
  715. }
  716. var bounds = vb.Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
  717. if (bounds.Length < 4)
  718. {
  719. return false;
  720. }
  721. if (string.IsNullOrEmpty(w))
  722. {
  723. w = bounds[2];
  724. }
  725. if (string.IsNullOrEmpty(h))
  726. {
  727. h = bounds[3];
  728. }
  729. }
  730. width = GetSvgUnit(w);
  731. if (double.IsNaN(width)) return false;
  732. height = GetSvgUnit(h);
  733. if (double.IsNaN(height)) return false;
  734. return true;
  735. }
  736. }
  737. return false;
  738. }
  739. catch
  740. {
  741. return false;
  742. }
  743. }
  744. private static bool IsSvg(Stream ms)
  745. {
  746. try
  747. {
  748. ms.Position = 0;
  749. var reader = new XmlTextReader(ms);
  750. while (reader.Read())
  751. {
  752. if (reader.LocalName == "svg" && reader.NodeType == XmlNodeType.Element)
  753. {
  754. return true;
  755. }
  756. }
  757. return false;
  758. }
  759. catch
  760. {
  761. return false;
  762. }
  763. }
  764. private static double GetSvgUnit(string v)
  765. {
  766. var factor = 1D;
  767. if (v.EndsWith("px", StringComparison.OrdinalIgnoreCase))
  768. {
  769. v = v.Substring(0, v.Length - 2);
  770. }
  771. else if (v.EndsWith("pt", StringComparison.OrdinalIgnoreCase))
  772. {
  773. factor = 1.25;
  774. v = v.Substring(0, v.Length - 2);
  775. }
  776. else if (v.EndsWith("pc", StringComparison.OrdinalIgnoreCase))
  777. {
  778. factor = 15;
  779. v = v.Substring(0, v.Length - 2);
  780. }
  781. else if (v.EndsWith("mm", StringComparison.OrdinalIgnoreCase))
  782. {
  783. factor = 3.543307;
  784. v = v.Substring(0, v.Length - 2);
  785. }
  786. else if (v.EndsWith("cm", StringComparison.OrdinalIgnoreCase))
  787. {
  788. factor = 35.43307;
  789. v = v.Substring(0, v.Length - 2);
  790. }
  791. else if (v.EndsWith("in", StringComparison.OrdinalIgnoreCase))
  792. {
  793. factor = 90;
  794. v = v.Substring(0, v.Length - 2);
  795. }
  796. if (double.TryParse(v, out double value))
  797. {
  798. return value * factor;
  799. }
  800. return double.NaN;
  801. }
  802. #endregion Svg
  803. private static ushort GetUInt16BigEndian(BinaryReader br)
  804. {
  805. var b = br.ReadBytes(2);
  806. return BitConverter.ToUInt16(new byte[] { b[1], b[0] }, 0);
  807. }
  808. private static short GetInt16BigEndian(BinaryReader br)
  809. {
  810. var b = br.ReadBytes(2);
  811. return BitConverter.ToInt16(new byte[] { b[1], b[0] }, 0);
  812. }
  813. private static int GetInt32BigEndian(BinaryReader br)
  814. {
  815. var b = br.ReadBytes(4);
  816. return BitConverter.ToInt32(new byte[] { b[3], b[2], b[1], b[0] }, 0);
  817. }
  818. }