ImageDetector.cs 28 KB

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