using SmartCoalApplication.Core; using SmartCoalApplication.SystemLayer; using System; using System.IO; using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Text; namespace SmartCoalApplication { /// /// ÎļþÀàÐÍ /// public abstract class FileType { private string[] extensions; private string name; private FileTypeFlags flags; // should be of the format ".ext" ... like ".bmp" or ".jpg" // The first extension in this list is the default extension (".jpg" for JPEG, // for instance, as ".jfif" etc. are not seen very often) public string[] Extensions { get { return (string[])this.extensions.Clone(); } } /// /// Gets the default extension for the FileType. /// /// /// This is always the first extension that is supported /// public string DefaultExtension { get { return this.extensions[0]; } } /// /// Returns the friendly name of the file type, such as "Bitmap" or "JPEG". /// public string Name { get { return this.name; } } public FileTypeFlags Flags { get { return this.flags; } } /// /// Gets a flag indicating whether this FileType supports layers. /// /// /// If a FileType is asked to save a Document that has more than one layer, /// it will flatten it before it saves it. /// public bool SupportsLayers { get { return (this.flags & FileTypeFlags.SupportsLayers) != 0; } } /// /// Gets a flag indicating whether this FileType supports custom headers. /// /// /// If this returns false, then the Document's CustomHeaders will be discarded /// on saving. /// public bool SupportsCustomHeaders { get { return (this.flags & FileTypeFlags.SupportsCustomHeaders) != 0; } } /// /// Gets a flag indicating whether this FileType supports the Save() method. /// /// /// If this property returns false, calling Save() will throw a NotSupportedException. /// public bool SupportsSaving { get { return (this.flags & FileTypeFlags.SupportsSaving) != 0; } } /// /// Gets a flag indicating whether this FileType supports the Load() method. /// /// /// If this property returns false, calling Load() will throw a NotSupportedException. /// public bool SupportsLoading { get { return (this.flags & FileTypeFlags.SupportsLoading) != 0; } } /// /// Gets a flag indicating whether this FileType reports progress while saving. /// /// /// If false, then the callback delegate passed to Save() will be ignored. /// public bool SavesWithProgress { get { return (this.flags & FileTypeFlags.SavesWithProgress) != 0; } } [Obsolete("Use the FileType(string, FileTypeFlags, string[]) overload instead", true)] public FileType(string name, bool supportsLayers, bool supportsCustomHeaders, string[] extensions) : this(name, supportsLayers, supportsCustomHeaders, true, true, false, extensions) { } [Obsolete("Use the FileType(string, FileTypeFlags, string[]) overload instead", true)] public FileType(string name, bool supportsLayers, bool supportsCustomHeaders, bool supportsSaving, bool supportsLoading, bool savesWithProgress, string[] extensions) : this(name, (supportsLayers ? FileTypeFlags.SupportsLayers : 0) | (supportsCustomHeaders ? FileTypeFlags.SupportsCustomHeaders : 0) | (supportsSaving ? FileTypeFlags.SupportsSaving : 0) | (supportsLoading ? FileTypeFlags.SupportsLoading : 0) | (savesWithProgress ? FileTypeFlags.SavesWithProgress : 0), extensions) { } public FileType(string name, FileTypeFlags flags, string[] extensions) { this.name = name; this.flags = flags; this.extensions = (string[])extensions.Clone(); } [Obsolete("Use the other Save() overload instead", true)] public void Save(Document input, Stream output, SaveConfigToken token, ProgressEventHandler callback, bool rememberToken) { using (Surface scratch = new Surface(input.Width, input.Height)) { Save(input, output, token, callback, rememberToken); } } public void Save( Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback, bool rememberToken) { Tracing.LogFeature("Save(" + GetType().FullName + ")"); if (!this.SupportsSaving) { throw new NotImplementedException("Saving is not supported by this FileType"); } else { Surface disposeMe = null; if (scratchSurface == null) { disposeMe = new Surface(input.Size); scratchSurface = disposeMe; } else if (scratchSurface.Size != input.Size) { throw new ArgumentException("scratchSurface.Size must equal input.Size"); } if (rememberToken) { Type ourType = this.GetType(); string savedTokenName = "SaveConfigToken." + ourType.Namespace + "." + ourType.Name + ".BinaryFormatter"; MemoryStream ms = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); DeferredFormatter deferredFormatter = new DeferredFormatter(false, null); StreamingContext streamingContext = new StreamingContext(formatter.Context.State, deferredFormatter); formatter.Context = streamingContext; object tokenSubset = GetSerializablePortionOfSaveConfigToken(token); formatter.Serialize(ms, tokenSubset); deferredFormatter.FinishSerialization(ms); byte[] bytes = ms.GetBuffer(); string base64Bytes = Convert.ToBase64String(bytes); Settings.CurrentUser.SetString(savedTokenName, base64Bytes); } try { OnSave(input, output, token, scratchSurface, callback); } catch (OnSaveNotImplementedException) { OldOnSaveTrampoline(input, output, token, callback); } if (disposeMe != null) { disposeMe.Dispose(); disposeMe = null; } } } protected virtual SaveConfigToken GetSaveConfigTokenFromSerializablePortion(object portion) { return (SaveConfigToken)portion; } protected virtual object GetSerializablePortionOfSaveConfigToken(SaveConfigToken token) { return token; } private sealed class OnSaveNotImplementedException : Exception { public OnSaveNotImplementedException(string message) : base(message) { } } /// /// Because the old OnSave() method is obsolete, we must use reflection to call it. /// This is important for legacy FileType plugins. It allows us to ensure that no /// new plugins can be compiled using the old OnSave() overload. /// private void OldOnSaveTrampoline(Document input, Stream output, SaveConfigToken token, ProgressEventHandler callback) { MethodInfo onSave = GetType().GetMethod( "OnSave", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, Type.DefaultBinder, new Type[] { typeof(Document), typeof(Stream), typeof(SaveConfigToken), typeof(ProgressEventHandler) }, null); onSave.Invoke( this, new object[] { input, output, token, callback }); } [Obsolete("Use the other OnSave() overload. It provides a scratch rendering surface that may enable your plugin to conserve memory usage.")] protected virtual void OnSave(Document input, Stream output, SaveConfigToken token, ProgressEventHandler callback) { } protected virtual void OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback) { throw new OnSaveNotImplementedException("Derived classes must implement this method. It is virtual instead of abstract in order to maintain compatibility with legacy plugins."); } /// /// Determines if saving with a given SaveConfigToken would alter the image /// in any way. Put another way, if the document is saved with these settings /// and then immediately loaded, would it have exactly the same pixel values? /// Any lossy codec should return 'false'. /// This value is used to optimizing preview rendering memory usage, and as such /// flattening should not be taken in to consideration. For example, the codec /// for PNG returns true, even though it flattens the image. /// /// The SaveConfigToken to determine reflexiveness for. /// true if the save would be reflexive, false if not /// If the SaveConfigToken is for another FileType, the result is undefined. public virtual bool IsReflexive(SaveConfigToken token) { return false; } [Serializable] private sealed class NoSaveConfigToken : SaveConfigToken { } /// /// Gets a flag indicating whether or not the file type supports configuration /// via a SaveConfigToken and SaveConfigWidget. /// /// /// Implementers of FileType derived classes don't need to do anything special /// for this property to be accurate. If your FileType implements /// CreateDefaultSaveConfigToken, this will correctly return true. /// public bool SupportsConfiguration { get { SaveConfigToken token = CreateDefaultSaveConfigToken(); return !(token is NoSaveConfigToken); } } public SaveConfigToken GetLastSaveConfigToken() { Type ourType = this.GetType(); string savedTokenName = "SaveConfigToken." + ourType.Namespace + "." + ourType.Name + ".BinaryFormatter"; string savedToken = Settings.CurrentUser.GetString(savedTokenName, null); SaveConfigToken saveConfigToken = null; if (savedToken != null) { try { byte[] bytes = Convert.FromBase64String(savedToken); MemoryStream ms = new MemoryStream(bytes); BinaryFormatter formatter = new BinaryFormatter(); DeferredFormatter deferred = new DeferredFormatter(); StreamingContext streamingContext = new StreamingContext(formatter.Context.State, deferred); formatter.Context = streamingContext; SerializationFallbackBinder sfb = new SerializationFallbackBinder(); sfb.AddAssembly(this.GetType().Assembly); sfb.AddAssembly(typeof(FileType).Assembly); formatter.Binder = sfb; object obj = formatter.Deserialize(ms); deferred.FinishDeserialization(ms); ms.Close(); ms = null; //SaveConfigToken sct = new SaveConfigToken(); //saveConfigToken = (SaveConfigToken)obj; saveConfigToken = GetSaveConfigTokenFromSerializablePortion(obj); } catch (Exception) { // Ignore erros and revert to default saveConfigToken = null; } } if (saveConfigToken == null) { saveConfigToken = CreateDefaultSaveConfigToken(); } return saveConfigToken; } public SaveConfigToken CreateDefaultSaveConfigToken() { return OnCreateDefaultSaveConfigToken(); } /// /// Creates a SaveConfigToken for this FileType with the default values. /// protected virtual SaveConfigToken OnCreateDefaultSaveConfigToken() { return new NoSaveConfigToken(); } public Document Load(Stream input) { Tracing.LogFeature("Load(" + GetType().FullName + ")"); if (!this.SupportsLoading) { throw new NotSupportedException("Loading not supported for this FileType"); } else { return OnLoad(input); } } public abstract Document OnLoad(Stream input); public override bool Equals(object obj) { if (obj == null || !(obj is FileType)) { return false; } return this.name.Equals(((FileType)obj).Name); } public override int GetHashCode() { return this.name.GetHashCode(); } /// /// Returns a string that can be used for populating a *FileDialog common dialog. /// public override string ToString() { StringBuilder sb = new StringBuilder(name); sb.Append(" ("); for (int i = 0; i < extensions.Length; ++i) { sb.Append("*"); sb.Append(extensions[i]); if (i != extensions.Length - 1) { sb.Append("; "); } else { sb.Append(")"); } } sb.Append("|"); for (int i = 0; i < extensions.Length; ++i) { sb.Append("*"); sb.Append(extensions[i]); if (i != extensions.Length - 1) { sb.Append(";"); } } return sb.ToString(); } } }