FileType.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. using SmartCoalApplication.Core;
  2. using SmartCoalApplication.SystemLayer;
  3. using System;
  4. using System.IO;
  5. using System.Reflection;
  6. using System.Runtime.Serialization;
  7. using System.Runtime.Serialization.Formatters.Binary;
  8. using System.Text;
  9. namespace SmartCoalApplication
  10. {
  11. /// <summary>
  12. /// ÎļþÀàÐÍ
  13. /// </summary>
  14. public abstract class FileType
  15. {
  16. private string[] extensions;
  17. private string name;
  18. private FileTypeFlags flags;
  19. // should be of the format ".ext" ... like ".bmp" or ".jpg"
  20. // The first extension in this list is the default extension (".jpg" for JPEG,
  21. // for instance, as ".jfif" etc. are not seen very often)
  22. public string[] Extensions
  23. {
  24. get
  25. {
  26. return (string[])this.extensions.Clone();
  27. }
  28. }
  29. /// <summary>
  30. /// Gets the default extension for the FileType.
  31. /// </summary>
  32. /// <remarks>
  33. /// This is always the first extension that is supported
  34. /// </remarks>
  35. public string DefaultExtension
  36. {
  37. get
  38. {
  39. return this.extensions[0];
  40. }
  41. }
  42. /// <summary>
  43. /// Returns the friendly name of the file type, such as "Bitmap" or "JPEG".
  44. /// </summary>
  45. public string Name
  46. {
  47. get
  48. {
  49. return this.name;
  50. }
  51. }
  52. public FileTypeFlags Flags
  53. {
  54. get
  55. {
  56. return this.flags;
  57. }
  58. }
  59. /// <summary>
  60. /// Gets a flag indicating whether this FileType supports layers.
  61. /// </summary>
  62. /// <remarks>
  63. /// If a FileType is asked to save a Document that has more than one layer,
  64. /// it will flatten it before it saves it.
  65. /// </remarks>
  66. public bool SupportsLayers
  67. {
  68. get
  69. {
  70. return (this.flags & FileTypeFlags.SupportsLayers) != 0;
  71. }
  72. }
  73. /// <summary>
  74. /// Gets a flag indicating whether this FileType supports custom headers.
  75. /// </summary>
  76. /// <remarks>
  77. /// If this returns false, then the Document's CustomHeaders will be discarded
  78. /// on saving.
  79. /// </remarks>
  80. public bool SupportsCustomHeaders
  81. {
  82. get
  83. {
  84. return (this.flags & FileTypeFlags.SupportsCustomHeaders) != 0;
  85. }
  86. }
  87. /// <summary>
  88. /// Gets a flag indicating whether this FileType supports the Save() method.
  89. /// </summary>
  90. /// <remarks>
  91. /// If this property returns false, calling Save() will throw a NotSupportedException.
  92. /// </remarks>
  93. public bool SupportsSaving
  94. {
  95. get
  96. {
  97. return (this.flags & FileTypeFlags.SupportsSaving) != 0;
  98. }
  99. }
  100. /// <summary>
  101. /// Gets a flag indicating whether this FileType supports the Load() method.
  102. /// </summary>
  103. /// <remarks>
  104. /// If this property returns false, calling Load() will throw a NotSupportedException.
  105. /// </remarks>
  106. public bool SupportsLoading
  107. {
  108. get
  109. {
  110. return (this.flags & FileTypeFlags.SupportsLoading) != 0;
  111. }
  112. }
  113. /// <summary>
  114. /// Gets a flag indicating whether this FileType reports progress while saving.
  115. /// </summary>
  116. /// <remarks>
  117. /// If false, then the callback delegate passed to Save() will be ignored.
  118. /// </remarks>
  119. public bool SavesWithProgress
  120. {
  121. get
  122. {
  123. return (this.flags & FileTypeFlags.SavesWithProgress) != 0;
  124. }
  125. }
  126. [Obsolete("Use the FileType(string, FileTypeFlags, string[]) overload instead", true)]
  127. public FileType(string name, bool supportsLayers, bool supportsCustomHeaders, string[] extensions)
  128. : this(name, supportsLayers, supportsCustomHeaders, true, true, false, extensions)
  129. {
  130. }
  131. [Obsolete("Use the FileType(string, FileTypeFlags, string[]) overload instead", true)]
  132. public FileType(string name, bool supportsLayers, bool supportsCustomHeaders, bool supportsSaving,
  133. bool supportsLoading, bool savesWithProgress, string[] extensions)
  134. : this(name,
  135. (supportsLayers ? FileTypeFlags.SupportsLayers : 0) |
  136. (supportsCustomHeaders ? FileTypeFlags.SupportsCustomHeaders : 0) |
  137. (supportsSaving ? FileTypeFlags.SupportsSaving : 0) |
  138. (supportsLoading ? FileTypeFlags.SupportsLoading : 0) |
  139. (savesWithProgress ? FileTypeFlags.SavesWithProgress : 0),
  140. extensions)
  141. {
  142. }
  143. public FileType(string name, FileTypeFlags flags, string[] extensions)
  144. {
  145. this.name = name;
  146. this.flags = flags;
  147. this.extensions = (string[])extensions.Clone();
  148. }
  149. [Obsolete("Use the other Save() overload instead", true)]
  150. public void Save(Document input, Stream output, SaveConfigToken token, ProgressEventHandler callback, bool rememberToken)
  151. {
  152. using (Surface scratch = new Surface(input.Width, input.Height))
  153. {
  154. Save(input, output, token, callback, rememberToken);
  155. }
  156. }
  157. public void Save(
  158. Document input,
  159. Stream output,
  160. SaveConfigToken token,
  161. Surface scratchSurface,
  162. ProgressEventHandler callback,
  163. bool rememberToken)
  164. {
  165. Tracing.LogFeature("Save(" + GetType().FullName + ")");
  166. if (!this.SupportsSaving)
  167. {
  168. throw new NotImplementedException("Saving is not supported by this FileType");
  169. }
  170. else
  171. {
  172. Surface disposeMe = null;
  173. if (scratchSurface == null)
  174. {
  175. disposeMe = new Surface(input.Size);
  176. scratchSurface = disposeMe;
  177. }
  178. else if (scratchSurface.Size != input.Size)
  179. {
  180. throw new ArgumentException("scratchSurface.Size must equal input.Size");
  181. }
  182. if (rememberToken)
  183. {
  184. Type ourType = this.GetType();
  185. string savedTokenName = "SaveConfigToken." + ourType.Namespace + "." + ourType.Name + ".BinaryFormatter";
  186. MemoryStream ms = new MemoryStream();
  187. BinaryFormatter formatter = new BinaryFormatter();
  188. DeferredFormatter deferredFormatter = new DeferredFormatter(false, null);
  189. StreamingContext streamingContext = new StreamingContext(formatter.Context.State, deferredFormatter);
  190. formatter.Context = streamingContext;
  191. object tokenSubset = GetSerializablePortionOfSaveConfigToken(token);
  192. formatter.Serialize(ms, tokenSubset);
  193. deferredFormatter.FinishSerialization(ms);
  194. byte[] bytes = ms.GetBuffer();
  195. string base64Bytes = Convert.ToBase64String(bytes);
  196. Settings.CurrentUser.SetString(savedTokenName, base64Bytes);
  197. }
  198. try
  199. {
  200. OnSave(input, output, token, scratchSurface, callback);
  201. }
  202. catch (OnSaveNotImplementedException)
  203. {
  204. OldOnSaveTrampoline(input, output, token, callback);
  205. }
  206. if (disposeMe != null)
  207. {
  208. disposeMe.Dispose();
  209. disposeMe = null;
  210. }
  211. }
  212. }
  213. protected virtual SaveConfigToken GetSaveConfigTokenFromSerializablePortion(object portion)
  214. {
  215. return (SaveConfigToken)portion;
  216. }
  217. protected virtual object GetSerializablePortionOfSaveConfigToken(SaveConfigToken token)
  218. {
  219. return token;
  220. }
  221. private sealed class OnSaveNotImplementedException
  222. : Exception
  223. {
  224. public OnSaveNotImplementedException(string message)
  225. : base(message)
  226. {
  227. }
  228. }
  229. /// <summary>
  230. /// Because the old OnSave() method is obsolete, we must use reflection to call it.
  231. /// This is important for legacy FileType plugins. It allows us to ensure that no
  232. /// new plugins can be compiled using the old OnSave() overload.
  233. /// </summary>
  234. private void OldOnSaveTrampoline(Document input, Stream output, SaveConfigToken token, ProgressEventHandler callback)
  235. {
  236. MethodInfo onSave = GetType().GetMethod(
  237. "OnSave",
  238. BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy,
  239. Type.DefaultBinder,
  240. new Type[]
  241. {
  242. typeof(Document),
  243. typeof(Stream),
  244. typeof(SaveConfigToken),
  245. typeof(ProgressEventHandler)
  246. },
  247. null);
  248. onSave.Invoke(
  249. this,
  250. new object[]
  251. {
  252. input,
  253. output,
  254. token,
  255. callback
  256. });
  257. }
  258. [Obsolete("Use the other OnSave() overload. It provides a scratch rendering surface that may enable your plugin to conserve memory usage.")]
  259. protected virtual void OnSave(Document input, Stream output, SaveConfigToken token, ProgressEventHandler callback)
  260. {
  261. }
  262. protected virtual void OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback)
  263. {
  264. throw new OnSaveNotImplementedException("Derived classes must implement this method. It is virtual instead of abstract in order to maintain compatibility with legacy plugins.");
  265. }
  266. /// <summary>
  267. /// Determines if saving with a given SaveConfigToken would alter the image
  268. /// in any way. Put another way, if the document is saved with these settings
  269. /// and then immediately loaded, would it have exactly the same pixel values?
  270. /// Any lossy codec should return 'false'.
  271. /// This value is used to optimizing preview rendering memory usage, and as such
  272. /// flattening should not be taken in to consideration. For example, the codec
  273. /// for PNG returns true, even though it flattens the image.
  274. /// </summary>
  275. /// <param name="token">The SaveConfigToken to determine reflexiveness for.</param>
  276. /// <returns>true if the save would be reflexive, false if not</returns>
  277. /// <remarks>If the SaveConfigToken is for another FileType, the result is undefined.</remarks>
  278. public virtual bool IsReflexive(SaveConfigToken token)
  279. {
  280. return false;
  281. }
  282. [Serializable]
  283. private sealed class NoSaveConfigToken
  284. : SaveConfigToken
  285. {
  286. }
  287. /// <summary>
  288. /// Gets a flag indicating whether or not the file type supports configuration
  289. /// via a SaveConfigToken and SaveConfigWidget.
  290. /// </summary>
  291. /// <remarks>
  292. /// Implementers of FileType derived classes don't need to do anything special
  293. /// for this property to be accurate. If your FileType implements
  294. /// CreateDefaultSaveConfigToken, this will correctly return true.
  295. /// </remarks>
  296. public bool SupportsConfiguration
  297. {
  298. get
  299. {
  300. SaveConfigToken token = CreateDefaultSaveConfigToken();
  301. return !(token is NoSaveConfigToken);
  302. }
  303. }
  304. public SaveConfigToken GetLastSaveConfigToken()
  305. {
  306. Type ourType = this.GetType();
  307. string savedTokenName = "SaveConfigToken." + ourType.Namespace + "." + ourType.Name + ".BinaryFormatter";
  308. string savedToken = Settings.CurrentUser.GetString(savedTokenName, null);
  309. SaveConfigToken saveConfigToken = null;
  310. if (savedToken != null)
  311. {
  312. try
  313. {
  314. byte[] bytes = Convert.FromBase64String(savedToken);
  315. MemoryStream ms = new MemoryStream(bytes);
  316. BinaryFormatter formatter = new BinaryFormatter();
  317. DeferredFormatter deferred = new DeferredFormatter();
  318. StreamingContext streamingContext = new StreamingContext(formatter.Context.State, deferred);
  319. formatter.Context = streamingContext;
  320. SerializationFallbackBinder sfb = new SerializationFallbackBinder();
  321. sfb.AddAssembly(this.GetType().Assembly);
  322. sfb.AddAssembly(typeof(FileType).Assembly);
  323. formatter.Binder = sfb;
  324. object obj = formatter.Deserialize(ms);
  325. deferred.FinishDeserialization(ms);
  326. ms.Close();
  327. ms = null;
  328. //SaveConfigToken sct = new SaveConfigToken();
  329. //saveConfigToken = (SaveConfigToken)obj;
  330. saveConfigToken = GetSaveConfigTokenFromSerializablePortion(obj);
  331. }
  332. catch (Exception)
  333. {
  334. // Ignore erros and revert to default
  335. saveConfigToken = null;
  336. }
  337. }
  338. if (saveConfigToken == null)
  339. {
  340. saveConfigToken = CreateDefaultSaveConfigToken();
  341. }
  342. return saveConfigToken;
  343. }
  344. public SaveConfigToken CreateDefaultSaveConfigToken()
  345. {
  346. return OnCreateDefaultSaveConfigToken();
  347. }
  348. /// <summary>
  349. /// Creates a SaveConfigToken for this FileType with the default values.
  350. /// </summary>
  351. protected virtual SaveConfigToken OnCreateDefaultSaveConfigToken()
  352. {
  353. return new NoSaveConfigToken();
  354. }
  355. public Document Load(Stream input)
  356. {
  357. Tracing.LogFeature("Load(" + GetType().FullName + ")");
  358. if (!this.SupportsLoading)
  359. {
  360. throw new NotSupportedException("Loading not supported for this FileType");
  361. }
  362. else
  363. {
  364. return OnLoad(input);
  365. }
  366. }
  367. public abstract Document OnLoad(Stream input);
  368. public override bool Equals(object obj)
  369. {
  370. if (obj == null || !(obj is FileType))
  371. {
  372. return false;
  373. }
  374. return this.name.Equals(((FileType)obj).Name);
  375. }
  376. public override int GetHashCode()
  377. {
  378. return this.name.GetHashCode();
  379. }
  380. /// <summary>
  381. /// Returns a string that can be used for populating a *FileDialog common dialog.
  382. /// </summary>
  383. public override string ToString()
  384. {
  385. StringBuilder sb = new StringBuilder(name);
  386. sb.Append(" (");
  387. for (int i = 0; i < extensions.Length; ++i)
  388. {
  389. sb.Append("*");
  390. sb.Append(extensions[i]);
  391. if (i != extensions.Length - 1)
  392. {
  393. sb.Append("; ");
  394. }
  395. else
  396. {
  397. sb.Append(")");
  398. }
  399. }
  400. sb.Append("|");
  401. for (int i = 0; i < extensions.Length; ++i)
  402. {
  403. sb.Append("*");
  404. sb.Append(extensions[i]);
  405. if (i != extensions.Length - 1)
  406. {
  407. sb.Append(";");
  408. }
  409. }
  410. return sb.ToString();
  411. }
  412. }
  413. }