// Copyright © 2018 Paddy Xu // // This file is part of QuickLook program. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Windows; namespace QuickLook.Plugin.ImageViewer { public enum Orientation { Undefined = 0, TopLeft = 1, TopRight = 2, BottomRight = 3, BottomLeft = 4, LeftTop = 5, RightTop = 6, RightBottom = 7, Leftbottom = 8 } public class NConvert { private static readonly string NConvertPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "NConvert\\nconvert.exe"); private readonly List> _metaBasic = new List>(); private readonly List> _metaExif = new List>(); private readonly string _path; public NConvert(string path) { _path = path; GetMeta(); } public List> GetExif() { return _metaExif; } public MemoryStream GetTiffStream(bool thumbnail) { var temp = Path.GetTempFileName(); File.Delete(temp); var sony = Path.GetExtension(_path)?.ToLower() == ".arw" ? "-autolevels" : ""; var thumb = thumbnail ? "-embedded_jpeg" : ""; var d = RunInternal( $"-quiet {thumb} {sony} -raw_camerabalance -raw_autobright -icc -out tiff -o \"{temp}\" \"{_path}\"", 10000); var ms = new MemoryStream(File.ReadAllBytes(temp)); File.Delete(temp); return ms; } public Size GetSize() { var ws = _metaBasic.Find(t => t.Item1 == "Width")?.Item2; var hs = _metaBasic.Find(t => t.Item1 == "Height")?.Item2; int.TryParse(ws, out var w); int.TryParse(hs, out var h); if (w == 0 || h == 0) return Size.Empty; switch (GetOrientation()) { case Orientation.LeftTop: case Orientation.RightTop: case Orientation.RightBottom: case Orientation.Leftbottom: return new Size(h, w); default: return new Size(w, h); } } public Orientation GetOrientation() { var o = _metaExif.Find(t => t.Item1 == "Orientation")?.Item2; if (!string.IsNullOrEmpty(o)) return (Orientation) int.Parse(o.Substring(o.Length - 2, 1)); return Orientation.TopLeft; } private void GetMeta() { var lines = RunInternal($"-quiet -fullinfo \"{_path}\"") .Replace("\r\n", "\n") .Split('\n'); var crtDict = _metaBasic; for (var i = 0; i < lines.Length; i++) { var segs = lines[i].Split(':'); if (segs.Length != 2) continue; var k = segs[0]; var v = segs[1]; if (k == "EXIF") crtDict = _metaExif; if (k.StartsWith(" ")) { if (crtDict == _metaBasic) // trim all 4 spaces { if (!string.IsNullOrWhiteSpace(v)) crtDict.Add(new Tuple(k.Trim(), v.Trim())); } else // in exif { if (!string.IsNullOrWhiteSpace(v)) { var kk = k.Substring(0, k.Length - 8).Trim(); crtDict.Add(new Tuple(kk, v.Trim())); // -8 to remove "(0xa001)" } } } else if (k.StartsWith(" ")) { crtDict.Add(new Tuple(k.Trim(), string.Empty)); } } } private string RunInternal(string arg, int timeout = 2000) { try { string result; using (var p = new Process()) { p.StartInfo.UseShellExecute = false; p.StartInfo.CreateNoWindow = true; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.FileName = NConvertPath; p.StartInfo.Arguments = arg; p.Start(); result = p.StandardOutput.ReadToEnd(); p.WaitForExit(timeout); return result; } } catch (Exception) { return string.Empty; } } } internal static class Extensions { public static byte[] ReadToEnd(this Stream stream) { using (var ms = new MemoryStream()) { var buffer = new byte[8192]; int count; while ((count = stream.Read(buffer, 0, buffer.Length)) > 0) ms.Write(buffer, 0, count); return ms.ToArray(); } } } }