diff --git a/src/Greenshot.Base/Core/ClipboardHelper.cs b/src/Greenshot.Base/Core/ClipboardHelper.cs index bfe13c121..789feb417 100644 --- a/src/Greenshot.Base/Core/ClipboardHelper.cs +++ b/src/Greenshot.Base/Core/ClipboardHelper.cs @@ -610,6 +610,7 @@ EndSelection:<<<<<<<4 } var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances(); var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadDrawableFromStream).ToList(); + var foundContainer = false; foreach (var (stream, filename) in IterateClipboardContent(dataObject)) { @@ -622,7 +623,8 @@ EndSelection:<<<<<<<4 IEnumerable drawableContainers; try { - drawableContainers = fileFormatHandlers.LoadDrawablesFromStream(stream, extension); + // without toList() here, LoadDrawablesFromStream() are called after the stream has been disposed + drawableContainers = fileFormatHandlers.LoadDrawablesFromStream(stream, extension).ToList(); } catch (Exception ex) { @@ -636,10 +638,14 @@ EndSelection:<<<<<<<4 // If we get here, there is an image foreach (var container in drawableContainers) { + foundContainer = true; yield return container; } } + // we found sth., prevent multiple imports of the same content + if (foundContainer) yield break; + // check if files are supplied foreach (string imageFile in GetImageFilenames(dataObject)) { diff --git a/src/Greenshot.Base/Core/ImageIO.cs b/src/Greenshot.Base/Core/ImageIO.cs index 9d6bbf1cd..fc918cdc1 100644 --- a/src/Greenshot.Base/Core/ImageIO.cs +++ b/src/Greenshot.Base/Core/ImageIO.cs @@ -264,35 +264,6 @@ namespace Greenshot.Base.Core } } - /// - /// Load a Greenshot surface - /// - /// - /// - /// - public static ISurface LoadGreenshotSurface(string fullPath, ISurface returnSurface) - { - if (string.IsNullOrEmpty(fullPath)) - { - return null; - } - - Log.InfoFormat("Loading image from file {0}", fullPath); - // Fixed lock problem Bug #3431881 - using (Stream surfaceFileStream = File.OpenRead(fullPath)) - { - returnSurface = LoadGreenshotSurface(surfaceFileStream, returnSurface); - } - - if (returnSurface != null) - { - Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", fullPath, returnSurface.Image.Width, returnSurface.Image.Height, - returnSurface.Image.PixelFormat, returnSurface.Image.HorizontalResolution, returnSurface.Image.VerticalResolution); - } - - return returnSurface; - } - /// /// Saves image to specific path with specified quality /// @@ -602,54 +573,5 @@ namespace Greenshot.Base.Core return null; } - /// - /// Load a Greenshot surface from a stream - /// - /// Stream - /// - /// ISurface - public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface) - { - Image fileImage; - // Fixed problem that the bitmap stream is disposed... by Cloning the image - // This also ensures the bitmap is correctly created - - // We create a copy of the bitmap, so everything else can be disposed - surfaceFileStream.Position = 0; - using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true)) - { - Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); - fileImage = ImageHelper.Clone(tmpImage); - } - - // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) - const int markerSize = 14; - surfaceFileStream.Seek(-markerSize, SeekOrigin.End); - using (StreamReader streamReader = new StreamReader(surfaceFileStream)) - { - var greenshotMarker = streamReader.ReadToEnd(); - if (!greenshotMarker.StartsWith("Greenshot")) - { - throw new ArgumentException("Stream is not a Greenshot file!"); - } - - Log.InfoFormat("Greenshot file format: {0}", greenshotMarker); - const int filesizeLocation = 8 + markerSize; - surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End); - using BinaryReader reader = new BinaryReader(surfaceFileStream); - long bytesWritten = reader.ReadInt64(); - surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End); - returnSurface.LoadElementsFromStream(surfaceFileStream); - } - - if (fileImage != null) - { - returnSurface.Image = fileImage; - Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", fileImage.Width, fileImage.Height, fileImage.PixelFormat, - fileImage.HorizontalResolution, fileImage.VerticalResolution); - } - - return returnSurface; - } } } \ No newline at end of file diff --git a/src/Greenshot.Base/Core/SimpleServiceProvider.cs b/src/Greenshot.Base/Core/SimpleServiceProvider.cs index 084f5b1fd..ed64022bb 100644 --- a/src/Greenshot.Base/Core/SimpleServiceProvider.cs +++ b/src/Greenshot.Base/Core/SimpleServiceProvider.cs @@ -12,8 +12,12 @@ namespace Greenshot.Base.Core { private readonly Dictionary> _services = new(); + /// + /// Gets the current instance of the service locator. + /// public static IServiceLocator Current { get; } = new SimpleServiceProvider(); + /// public IReadOnlyList GetAllInstances() { var typeOfService = typeof(TService); @@ -25,11 +29,13 @@ namespace Greenshot.Base.Core return results.Cast().ToArray(); } + /// public TService GetInstance() { return GetAllInstances().SingleOrDefault(); } + /// public void AddService(IEnumerable services) { var serviceType = typeof(TService); @@ -50,9 +56,40 @@ namespace Greenshot.Base.Core } } + /// public void AddService(params TService[] services) { AddService(services.AsEnumerable()); } + + /// + public void RemoveService(IEnumerable services) + { + var serviceType = typeof(TService); + if (!_services.TryGetValue(serviceType, out var currentServices)) + { + return; + } + + foreach (var service in services) + { + if (service == null) + { + continue; + } + currentServices.Remove(service); + } + + if (currentServices.Count == 0) + { + _services.Remove(serviceType); + } + } + + /// + public void RemoveService(params TService[] services) + { + RemoveService(services.AsEnumerable()); + } } } \ No newline at end of file diff --git a/src/Greenshot.Base/IniFile/IniConfig.cs b/src/Greenshot.Base/IniFile/IniConfig.cs index cbe04e11b..e5fccbe5e 100644 --- a/src/Greenshot.Base/IniFile/IniConfig.cs +++ b/src/Greenshot.Base/IniFile/IniConfig.cs @@ -41,6 +41,11 @@ namespace Greenshot.Base.IniFile /// private static readonly object IniLock = new object(); + /// + /// A lock object for the section map + /// + private static readonly object SectionMapLock = new object(); + /// /// As the ini implementation is kept someone generic, for reusing, this holds the name of the application /// @@ -290,23 +295,26 @@ namespace Greenshot.Base.IniFile // Load the fixed settings _fixedProperties = Read(CreateIniLocation(_configName + FixedPostfix + IniExtension, true)); - foreach (IniSection section in SectionMap.Values) + lock (SectionMapLock) { - try + foreach (IniSection section in SectionMap.Values) { - section.Fill(PropertiesForSection(section)); - FixProperties(section); - } - catch (Exception ex) - { - string sectionName = "unknown"; - if (section?.IniSectionAttribute?.Name != null) + try { - sectionName = section.IniSectionAttribute.Name; + section.Fill(PropertiesForSection(section)); + FixProperties(section); } + catch (Exception ex) + { + string sectionName = "unknown"; + if (section?.IniSectionAttribute?.Name != null) + { + sectionName = section.IniSectionAttribute.Name; + } - Log.WarnFormat("Problem reading the ini section {0}", sectionName); - Log.Warn("Exception", ex); + Log.WarnFormat("Problem reading the ini section {0}", sectionName); + Log.Warn("Exception", ex); + } } } } @@ -389,7 +397,12 @@ namespace Greenshot.Base.IniFile { get { - foreach (string sectionName in SectionMap.Keys) + List sectionNames; + lock (SectionMapLock) + { + sectionNames = [.. SectionMap.Keys]; + } + foreach (string sectionName in sectionNames) { yield return sectionName; } @@ -403,10 +416,13 @@ namespace Greenshot.Base.IniFile /// public static IniSection GetIniSection(string sectionName) { - SectionMap.TryGetValue(sectionName, out var returnValue); - return returnValue; + lock (SectionMapLock) + { + SectionMap.TryGetValue(sectionName, out var returnValue); + return returnValue; + } } - + /// /// A generic method which returns an instance of the supplied type, filled with it's configuration /// @@ -429,20 +445,24 @@ namespace Greenshot.Base.IniFile Type iniSectionType = typeof(T); string sectionName = IniSection.GetIniSectionAttribute(iniSectionType).Name; - if (SectionMap.ContainsKey(sectionName)) + + lock (SectionMapLock) { + if (SectionMap.ContainsKey(sectionName)) + { //LOG.Debug("Returning pre-mapped section " + sectionName); - section = (T) SectionMap[sectionName]; - } - else - { + section = (T)SectionMap[sectionName]; + } + else + { // Create instance of this type - section = (T) Activator.CreateInstance(iniSectionType); + section = (T)Activator.CreateInstance(iniSectionType); // Store for later save & retrieval - SectionMap.Add(sectionName, section); - section.Fill(PropertiesForSection(section)); - FixProperties(section); + SectionMap.Add(sectionName, section); + section.Fill(PropertiesForSection(section)); + FixProperties(section); + } } if (allowSave && section.IsDirty) diff --git a/src/Greenshot.Base/Interfaces/IFileFormatHandler.cs b/src/Greenshot.Base/Interfaces/IFileFormatHandler.cs index bd5558857..c2fbc05e9 100644 --- a/src/Greenshot.Base/Interfaces/IFileFormatHandler.cs +++ b/src/Greenshot.Base/Interfaces/IFileFormatHandler.cs @@ -60,7 +60,7 @@ namespace Greenshot.Base.Interfaces /// Try to save the specified bitmap to the stream in the format belonging to the extension /// /// Bitmap - /// Stream + /// Stream for destination /// extension /// ISurface with the elements for those file types which can store a surface (.greenshot) /// SurfaceOutputSettings @@ -68,21 +68,22 @@ namespace Greenshot.Base.Interfaces public bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null); /// - /// + /// Try to load an Image from the stream /// /// /// /// - /// bool true if it was successful + /// if the image was successfully loaded into a ; otherwise, . public bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap); /// - /// Try to load a drawable container from the stream + /// Try to load drawable container from the stream /// /// Stream /// string - /// ISurface - /// IEnumerable{IDrawableContainer} + /// Parent to initialize the container + /// All that could be loaded from the stream public IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface parentSurface = null); } } diff --git a/src/Greenshot.Base/Interfaces/IServiceLocator.cs b/src/Greenshot.Base/Interfaces/IServiceLocator.cs index 5cf1fb529..9a0081fee 100644 --- a/src/Greenshot.Base/Interfaces/IServiceLocator.cs +++ b/src/Greenshot.Base/Interfaces/IServiceLocator.cs @@ -54,5 +54,19 @@ namespace Greenshot.Base.Interfaces /// Type of the service /// IEnumerable{TService} with services to add void AddService(IEnumerable services); + + /// + /// Remove one or more services from the registry + /// + /// Type of the service + /// One or more services which need to be removed + void RemoveService(params TService[] services); + + /// + /// Remove multiple services from the registry + /// + /// Type of the service + /// IEnumerable{TService} with services to remove + void RemoveService(IEnumerable services); } } \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/ISurface.cs b/src/Greenshot.Base/Interfaces/ISurface.cs index 4b39fb9b8..f0648c172 100644 --- a/src/Greenshot.Base/Interfaces/ISurface.cs +++ b/src/Greenshot.Base/Interfaces/ISurface.cs @@ -99,8 +99,13 @@ namespace Greenshot.Base.Interfaces IImageContainer AddImageContainer(string filename, int x, int y); ICursorContainer AddCursorContainer(string filename, int x, int y); IIconContainer AddIconContainer(string filename, int x, int y); - long SaveElementsToStream(Stream stream); - void LoadElementsFromStream(Stream stream); + + /// + /// Adds all container to the Surface. + /// Ensure that all new container are correctly integrated into Surface + /// + /// + void LoadElements(IDrawableContainerList containerList); /// /// Provides the selected elements diff --git a/src/Greenshot.Editor/Drawing/ArrowContainer.cs b/src/Greenshot.Editor/Drawing/ArrowContainer.cs index 91c76366b..dede03f14 100644 --- a/src/Greenshot.Editor/Drawing/ArrowContainer.cs +++ b/src/Greenshot.Editor/Drawing/ArrowContainer.cs @@ -33,7 +33,6 @@ namespace Greenshot.Editor.Drawing /// /// Description of LineContainer. /// - [Serializable()] public class ArrowContainer : LineContainer { public enum ArrowHeadCombination @@ -56,7 +55,6 @@ namespace Greenshot.Editor.Drawing protected override void InitializeFields() { AddField(GetType(), FieldType.LINE_THICKNESS, 2); - AddField(GetType(), FieldType.ARROWHEADS, 2); AddField(GetType(), FieldType.LINE_COLOR, Color.Red); AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); AddField(GetType(), FieldType.SHADOW, true); diff --git a/src/Greenshot.Editor/Drawing/CropContainer.cs b/src/Greenshot.Editor/Drawing/CropContainer.cs index eb2f7179e..22ed9d432 100644 --- a/src/Greenshot.Editor/Drawing/CropContainer.cs +++ b/src/Greenshot.Editor/Drawing/CropContainer.cs @@ -21,7 +21,6 @@ using System.Drawing; -using System.Runtime.Serialization; using Dapplo.Windows.Common.Extensions; using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces; @@ -65,12 +64,6 @@ namespace Greenshot.Editor.Drawing Init(); } - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } - private void Init() { switch (GetFieldValue(FieldType.CROPMODE)) diff --git a/src/Greenshot.Editor/Drawing/CursorContainer.cs b/src/Greenshot.Editor/Drawing/CursorContainer.cs index e8914eb75..b3fdaaf3a 100644 --- a/src/Greenshot.Editor/Drawing/CursorContainer.cs +++ b/src/Greenshot.Editor/Drawing/CursorContainer.cs @@ -19,11 +19,9 @@ * along with this program. If not, see . */ -using System; using System.Drawing; using System.Drawing.Drawing2D; using System.IO; -using System.Runtime.Serialization; using System.Windows.Forms; using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces; @@ -34,22 +32,21 @@ namespace Greenshot.Editor.Drawing { /// /// Description of CursorContainer. + /// This Container is not in use. For a capture with mouse cursor the IconContainer is used cctor Surface(ICapture capture) in . /// - [Serializable] public class CursorContainer : DrawableContainer, ICursorContainer { private static readonly ILog LOG = LogManager.GetLogger(typeof(CursorContainer)); protected Cursor cursor; - public CursorContainer(ISurface parent) : base(parent) + public CursorContainer(ISurface parent, string filename) : this(parent) { - Init(); + Load(filename); } - protected override void OnDeserialized(StreamingContext streamingContext) + public CursorContainer(ISurface parent) : base(parent) { - base.OnDeserialized(streamingContext); Init(); } @@ -58,11 +55,6 @@ namespace Greenshot.Editor.Drawing CreateDefaultAdorners(); } - public CursorContainer(ISurface parent, string filename) : this(parent) - { - Load(filename); - } - public Cursor Cursor { set diff --git a/src/Greenshot.Editor/Drawing/DrawableContainer.cs b/src/Greenshot.Editor/Drawing/DrawableContainer.cs index cac23dc3a..374ba3965 100644 --- a/src/Greenshot.Editor/Drawing/DrawableContainer.cs +++ b/src/Greenshot.Editor/Drawing/DrawableContainer.cs @@ -25,7 +25,6 @@ using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; -using System.Runtime.Serialization; using System.Windows.Forms; using Dapplo.Windows.Common.Extensions; using Dapplo.Windows.Common.Structs; @@ -49,7 +48,6 @@ namespace Greenshot.Editor.Drawing /// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call /// OnPropertyChanged whenever a public property has been changed. /// - [Serializable] public abstract class DrawableContainer : AbstractFieldHolderWithChildren, IDrawableContainer { private static readonly ILog LOG = LogManager.GetLogger(typeof(DrawableContainer)); @@ -57,18 +55,18 @@ namespace Greenshot.Editor.Drawing private const int M11 = 0; private const int M22 = 3; - [OnDeserialized] - private void OnDeserializedInit(StreamingContext context) + public DrawableContainer(ISurface parent) { - _adorners = new List(); - OnDeserialized(context); + InitializeFields(); + _parent = parent; } /// - /// Override to implement your own deserialization logic, like initializing properties which are not serialized + /// Performs post-deserialization initialization for the object. + /// This method is called after the object has been deserialized to restore or initialize + /// state that depends on deserialization. /// - /// - protected virtual void OnDeserialized(StreamingContext streamingContext) + public virtual void OnDeserialized() { } @@ -104,7 +102,7 @@ namespace Greenshot.Editor.Drawing Dispose(false); } - [NonSerialized] private PropertyChangedEventHandler _propertyChanged; + private PropertyChangedEventHandler _propertyChanged; public event PropertyChangedEventHandler PropertyChanged { @@ -129,7 +127,7 @@ namespace Greenshot.Editor.Drawing } } - [NonSerialized] internal ISurface _parent; + internal ISurface _parent; public ISurface Parent { @@ -142,10 +140,10 @@ namespace Greenshot.Editor.Drawing get => (Surface)_parent; } - [NonSerialized] private TargetAdorner _targetAdorner; + private TargetAdorner _targetAdorner; public TargetAdorner TargetAdorner => _targetAdorner; - [NonSerialized] private bool _selected; + private bool _selected; public bool Selected { @@ -157,7 +155,7 @@ namespace Greenshot.Editor.Drawing } } - [NonSerialized] private EditStatus _status = EditStatus.UNDRAWN; + private EditStatus _status = EditStatus.UNDRAWN; public EditStatus Status { @@ -253,15 +251,13 @@ namespace Greenshot.Editor.Drawing /// /// List of available Adorners /// - [NonSerialized] private IList _adorners = new List(); + private IList _adorners = new List(); public IList Adorners => _adorners; - [NonSerialized] // will store current bounds of this DrawableContainer before starting a resize protected NativeRect _boundsBeforeResize = NativeRect.Empty; - [NonSerialized] // "workbench" rectangle - used for calculating bounds during resizing (to be applied to this DrawableContainer afterwards) protected NativeRectFloat _boundsAfterResize = NativeRectFloat.Empty; @@ -285,12 +281,6 @@ namespace Greenshot.Editor.Drawing Height = Round(newBounds.Height); } - public DrawableContainer(ISurface parent) - { - InitializeFields(); - _parent = parent; - } - public void Add(IFilter filter) { AddChild(filter); diff --git a/src/Greenshot.Editor/Drawing/DrawableContainerList.cs b/src/Greenshot.Editor/Drawing/DrawableContainerList.cs index ef30c037f..4d894e17b 100644 --- a/src/Greenshot.Editor/Drawing/DrawableContainerList.cs +++ b/src/Greenshot.Editor/Drawing/DrawableContainerList.cs @@ -42,7 +42,6 @@ namespace Greenshot.Editor.Drawing /// /// Dispatches most of a DrawableContainer's public properties and methods to a list of DrawableContainers. /// - [Serializable] public class DrawableContainerList : List, IDrawableContainerList { private static readonly ComponentResourceManager EditorFormResources = new(typeof(ImageEditorForm)); diff --git a/src/Greenshot.Editor/Drawing/EllipseContainer.cs b/src/Greenshot.Editor/Drawing/EllipseContainer.cs index c421515d9..0925640bc 100644 --- a/src/Greenshot.Editor/Drawing/EllipseContainer.cs +++ b/src/Greenshot.Editor/Drawing/EllipseContainer.cs @@ -22,7 +22,6 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; -using System.Runtime.Serialization; using Dapplo.Windows.Common.Extensions; using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces; @@ -35,7 +34,6 @@ namespace Greenshot.Editor.Drawing /// /// Description of EllipseContainer. /// - [Serializable()] public class EllipseContainer : DrawableContainer { public EllipseContainer(ISurface parent) : base(parent) @@ -43,12 +41,6 @@ namespace Greenshot.Editor.Drawing Init(); } - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } - private void Init() { CreateDefaultAdorners(); diff --git a/src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolder.cs b/src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolder.cs index a3f3c92da..ab40bd425 100644 --- a/src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolder.cs +++ b/src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolder.cs @@ -23,7 +23,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; -using System.Runtime.Serialization; +using System.Linq; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Configuration; @@ -34,40 +34,28 @@ namespace Greenshot.Editor.Drawing.Fields /// /// Basic IFieldHolder implementation, providing access to a set of fields /// - [Serializable] public abstract class AbstractFieldHolder : IFieldHolder { private static readonly ILog LOG = LogManager.GetLogger(typeof(AbstractFieldHolder)); private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); - [NonSerialized] private readonly IDictionary _handlers = new Dictionary(); + private readonly IDictionary _handlers = new Dictionary(); /// /// called when a field's value has changed /// - [NonSerialized] private FieldChangedEventHandler _fieldChanged; + private FieldChangedEventHandler _fieldChanged; + /// public event FieldChangedEventHandler FieldChanged { add { _fieldChanged += value; } remove { _fieldChanged -= value; } } - // we keep two Collections of our fields, dictionary for quick access, list for serialization - // this allows us to use default serialization - [NonSerialized] private IDictionary _fieldsByType = new Dictionary(); - private readonly IList fields = new List(); - - [OnDeserialized] - private void OnDeserialized(StreamingContext context) - { - _fieldsByType = new Dictionary(); - // listen to changing properties - foreach (var field in fields) - { - field.PropertyChanged += delegate { _fieldChanged?.Invoke(this, new FieldChangedEventArgs(field)); }; - _fieldsByType[field.FieldType] = field; - } - } + /// + /// The dictionary represents the base collection of fields. It allows quick access by . + /// + private readonly IDictionary _fieldsByType = new Dictionary(); public void AddField(Type requestingType, IFieldType fieldType, object fieldValue) { @@ -76,7 +64,6 @@ namespace Greenshot.Editor.Drawing.Fields public virtual void AddField(IField field) { - fields.Add(field); if (_fieldsByType == null) { return; @@ -98,18 +85,19 @@ namespace Greenshot.Editor.Drawing.Fields public void RemoveField(IField field) { - fields.Remove(field); _fieldsByType.Remove(field.FieldType); field.PropertyChanged -= _handlers[field]; _handlers.Remove(field); } + /// + /// Retrieves a list of all fields currently stored in the internal dictionary. + /// public IList GetFields() { - return fields; + return _fieldsByType.Select(x => x.Value).ToList(); } - public IField GetField(IFieldType fieldType) { try diff --git a/src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolderWithChildren.cs b/src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolderWithChildren.cs index 968edef46..cc3a6ad9d 100644 --- a/src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolderWithChildren.cs +++ b/src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolderWithChildren.cs @@ -21,7 +21,6 @@ using System; using System.Collections.Generic; -using System.Runtime.Serialization; using Greenshot.Base.Interfaces.Drawing; namespace Greenshot.Editor.Drawing.Fields @@ -31,12 +30,11 @@ namespace Greenshot.Editor.Drawing.Fields /// but has a List of IFieldHolder for children. /// Field values are passed to and from children as well. /// - [Serializable] public abstract class AbstractFieldHolderWithChildren : AbstractFieldHolder { - [NonSerialized] private readonly FieldChangedEventHandler _fieldChangedEventHandler; + private readonly FieldChangedEventHandler _fieldChangedEventHandler; - [NonSerialized] private EventHandler childrenChanged; + private EventHandler childrenChanged; public event EventHandler ChildrenChanged { @@ -46,23 +44,11 @@ namespace Greenshot.Editor.Drawing.Fields public IList Children = new List(); - public AbstractFieldHolderWithChildren() + protected AbstractFieldHolderWithChildren() { _fieldChangedEventHandler = OnFieldChanged; } - [OnDeserialized()] - private void OnDeserialized(StreamingContext context) - { - // listen to changing properties - foreach (IFieldHolder fieldHolder in Children) - { - fieldHolder.FieldChanged += _fieldChangedEventHandler; - } - - childrenChanged?.Invoke(this, EventArgs.Empty); - } - public void AddChild(IFieldHolder fieldHolder) { Children.Add(fieldHolder); diff --git a/src/Greenshot.Editor/Drawing/Fields/Field.cs b/src/Greenshot.Editor/Drawing/Fields/Field.cs index c3870c54c..cea2bea3e 100644 --- a/src/Greenshot.Editor/Drawing/Fields/Field.cs +++ b/src/Greenshot.Editor/Drawing/Fields/Field.cs @@ -29,10 +29,9 @@ namespace Greenshot.Editor.Drawing.Fields /// Represents a single field of a drawable element, i.e. /// line thickness of a rectangle. /// - [Serializable] public class Field : IField { - [field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged; + public event PropertyChangedEventHandler PropertyChanged; private object _myValue; diff --git a/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs b/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs index 96a19b7b9..690ed6bec 100644 --- a/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs +++ b/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs @@ -19,7 +19,6 @@ * along with this program. If not, see . */ -using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -41,7 +40,6 @@ namespace Greenshot.Editor.Drawing.Fields /// Properties that do not apply for ALL selected elements are null (or 0 respectively) /// If the property values of the selected elements differ, the value of the last bound element wins. /// - [Serializable] public sealed class FieldAggregator : AbstractFieldHolder, IFieldAggregator { private readonly IDrawableContainerList _boundContainers; diff --git a/src/Greenshot.Editor/Drawing/Fields/FieldType.cs b/src/Greenshot.Editor/Drawing/Fields/FieldType.cs index eead7452a..07996e700 100644 --- a/src/Greenshot.Editor/Drawing/Fields/FieldType.cs +++ b/src/Greenshot.Editor/Drawing/Fields/FieldType.cs @@ -19,7 +19,6 @@ * along with this program. If not, see . */ -using System; using Greenshot.Base.Interfaces.Drawing; namespace Greenshot.Editor.Drawing.Fields @@ -28,7 +27,6 @@ namespace Greenshot.Editor.Drawing.Fields /// Defines all FieldTypes + their default value. /// (The additional value is why this is not an enum) /// - [Serializable] public class FieldType : IFieldType { public static readonly IFieldType ARROWHEADS = new FieldType(nameof(ARROWHEADS)); diff --git a/src/Greenshot.Editor/Drawing/FilterContainer.cs b/src/Greenshot.Editor/Drawing/FilterContainer.cs index 4b846fe66..3d3ff1c05 100644 --- a/src/Greenshot.Editor/Drawing/FilterContainer.cs +++ b/src/Greenshot.Editor/Drawing/FilterContainer.cs @@ -22,7 +22,6 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; -using System.Runtime.Serialization; using Dapplo.Windows.Common.Extensions; using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces; @@ -35,7 +34,6 @@ namespace Greenshot.Editor.Drawing /// /// empty container for filter-only elements /// - [Serializable] public abstract class FilterContainer : DrawableContainer { public enum PreparedFilterMode @@ -56,18 +54,6 @@ namespace Greenshot.Editor.Drawing public FilterContainer(ISurface parent) : base(parent) { - Init(); - } - - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } - - private void Init() - { - CreateDefaultAdorners(); } protected override void InitializeFields() diff --git a/src/Greenshot.Editor/Drawing/Filters/AbstractFilter.cs b/src/Greenshot.Editor/Drawing/Filters/AbstractFilter.cs index c972ed77d..d391e9e67 100644 --- a/src/Greenshot.Editor/Drawing/Filters/AbstractFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/AbstractFilter.cs @@ -33,10 +33,9 @@ namespace Greenshot.Editor.Drawing.Filters /// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call /// OnPropertyChanged whenever a public property has been changed. /// - [Serializable] public abstract class AbstractFilter : AbstractFieldHolder, IFilter { - [NonSerialized] private PropertyChangedEventHandler propertyChanged; + private PropertyChangedEventHandler propertyChanged; public event PropertyChangedEventHandler PropertyChanged { diff --git a/src/Greenshot.Editor/Drawing/Filters/BlurFilter.cs b/src/Greenshot.Editor/Drawing/Filters/BlurFilter.cs index 010cfd2cb..649d86979 100644 --- a/src/Greenshot.Editor/Drawing/Filters/BlurFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/BlurFilter.cs @@ -19,7 +19,6 @@ * along with this program. If not, see . */ -using System; using System.Drawing; using System.Drawing.Drawing2D; using Dapplo.Windows.Common.Structs; @@ -30,7 +29,6 @@ using Greenshot.Editor.Drawing.Fields; namespace Greenshot.Editor.Drawing.Filters { - [Serializable] public class BlurFilter : AbstractFilter { public double previewQuality; diff --git a/src/Greenshot.Editor/Drawing/Filters/MagnifierFilter.cs b/src/Greenshot.Editor/Drawing/Filters/MagnifierFilter.cs index 70d829a2e..119e86c9c 100644 --- a/src/Greenshot.Editor/Drawing/Filters/MagnifierFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/MagnifierFilter.cs @@ -32,7 +32,6 @@ namespace Greenshot.Editor.Drawing.Filters /// /// Magnify an area /// - [Serializable] public class MagnifierFilter : AbstractFilter { public MagnifierFilter(DrawableContainer parent) : base(parent) diff --git a/src/Greenshot.Editor/Drawing/FreehandContainer.cs b/src/Greenshot.Editor/Drawing/FreehandContainer.cs index 9c66e1a8e..49db5d548 100644 --- a/src/Greenshot.Editor/Drawing/FreehandContainer.cs +++ b/src/Greenshot.Editor/Drawing/FreehandContainer.cs @@ -23,7 +23,6 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; -using System.Runtime.Serialization; using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; @@ -35,7 +34,6 @@ namespace Greenshot.Editor.Drawing /// /// Description of PathContainer. /// - [Serializable] public class FreehandContainer : DrawableContainer { private static readonly float[] PointOffset = @@ -43,7 +41,6 @@ namespace Greenshot.Editor.Drawing 0.5f, 0.25f, 0.75f }; - [NonSerialized] private GraphicsPath freehandPath = new GraphicsPath(); private Rectangle myBounds = NativeRect.Empty; @@ -56,12 +53,25 @@ namespace Greenshot.Editor.Drawing /// public FreehandContainer(ISurface parent) : base(parent) { - Width = parent.Image.Width; - Height = parent.Image.Height; + if (parent?.Image is not null) + { + Width = parent.Image.Width; + Height = parent.Image.Height; + } Top = 0; Left = 0; } + /// + /// + /// + /// And recalculates path from capturePoints + public override void OnDeserialized() + { + base.OnDeserialized(); + RecalculatePath(); + } + protected override void InitializeFields() { AddField(GetType(), FieldType.LINE_THICKNESS, 3); @@ -78,11 +88,6 @@ namespace Greenshot.Editor.Drawing RecalculatePath(); } - protected override void OnDeserialized(StreamingContext context) - { - RecalculatePath(); - } - /// /// This Dispose is called from the Dispose and the Destructor. /// @@ -98,6 +103,12 @@ namespace Greenshot.Editor.Drawing freehandPath = null; } + public List CapturePoints + { + get => capturePoints; + set => capturePoints = value; + } + /// /// Called from Surface (the parent) when the drawing begins (mouse-down) /// diff --git a/src/Greenshot.Editor/Drawing/HighlightContainer.cs b/src/Greenshot.Editor/Drawing/HighlightContainer.cs index 58cf792ba..d9193fcc3 100644 --- a/src/Greenshot.Editor/Drawing/HighlightContainer.cs +++ b/src/Greenshot.Editor/Drawing/HighlightContainer.cs @@ -19,8 +19,6 @@ * along with this program. If not, see . */ -using System; -using System.Runtime.Serialization; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; @@ -31,7 +29,6 @@ namespace Greenshot.Editor.Drawing /// /// Description of ObfuscateContainer. /// - [Serializable] public class HighlightContainer : FilterContainer { public HighlightContainer(ISurface parent) : base(parent) @@ -48,11 +45,6 @@ namespace Greenshot.Editor.Drawing AddField(GetType(), FieldType.PREPARED_FILTER_HIGHLIGHT, PreparedFilter.TEXT_HIGHTLIGHT); } - protected override void OnDeserialized(StreamingContext context) - { - Init(); - } - private void Init() { FieldChanged += HighlightContainer_OnFieldChanged; diff --git a/src/Greenshot.Editor/Drawing/IconContainer.cs b/src/Greenshot.Editor/Drawing/IconContainer.cs index 185c17cd2..15ecaa640 100644 --- a/src/Greenshot.Editor/Drawing/IconContainer.cs +++ b/src/Greenshot.Editor/Drawing/IconContainer.cs @@ -19,11 +19,9 @@ * along with this program. If not, see . */ -using System; using System.Drawing; using System.Drawing.Drawing2D; using System.IO; -using System.Runtime.Serialization; using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; @@ -34,7 +32,6 @@ namespace Greenshot.Editor.Drawing /// /// Description of IconContainer. /// - [Serializable] public class IconContainer : DrawableContainer, IIconContainer { private static readonly ILog Log = LogManager.GetLogger(typeof(IconContainer)); @@ -46,12 +43,6 @@ namespace Greenshot.Editor.Drawing Init(); } - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } - private void Init() { CreateDefaultAdorners(); diff --git a/src/Greenshot.Editor/Drawing/ImageContainer.cs b/src/Greenshot.Editor/Drawing/ImageContainer.cs index 21f8d2fa6..43a07f9ce 100644 --- a/src/Greenshot.Editor/Drawing/ImageContainer.cs +++ b/src/Greenshot.Editor/Drawing/ImageContainer.cs @@ -19,11 +19,9 @@ * along with this program. If not, see . */ -using System; using System.Drawing; using System.Drawing.Drawing2D; using System.IO; -using System.Runtime.Serialization; using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Effects; @@ -37,7 +35,6 @@ namespace Greenshot.Editor.Drawing /// /// Description of BitmapContainer. /// - [Serializable] public class ImageContainer : DrawableContainer, IImageContainer { private static readonly ILog Log = LogManager.GetLogger(typeof(ImageContainer)); @@ -48,13 +45,13 @@ namespace Greenshot.Editor.Drawing /// This is the shadow version of the bitmap, rendered once to save performance /// Do not serialize, as the shadow is recreated from the original bitmap if it's not available /// - [NonSerialized] private Image _shadowBitmap; + private Image _shadowBitmap; /// /// This is the offset for the shadow version of the bitmap /// Do not serialize, as the offset is recreated /// - [NonSerialized] private NativePoint _shadowOffset = new NativePoint(-1, -1); + private NativePoint _shadowOffset = new NativePoint(-1, -1); public ImageContainer(ISurface parent, string filename) : this(parent) { @@ -67,12 +64,6 @@ namespace Greenshot.Editor.Drawing Init(); } - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } - private void Init() { CreateDefaultAdorners(); diff --git a/src/Greenshot.Editor/Drawing/LineContainer.cs b/src/Greenshot.Editor/Drawing/LineContainer.cs index f48dddec7..ed7f1681c 100644 --- a/src/Greenshot.Editor/Drawing/LineContainer.cs +++ b/src/Greenshot.Editor/Drawing/LineContainer.cs @@ -19,10 +19,8 @@ * along with this program. If not, see . */ -using System; using System.Drawing; using System.Drawing.Drawing2D; -using System.Runtime.Serialization; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Adorners; @@ -34,7 +32,6 @@ namespace Greenshot.Editor.Drawing /// /// Description of LineContainer. /// - [Serializable()] public class LineContainer : DrawableContainer { public LineContainer(ISurface parent) : base(parent) @@ -49,11 +46,6 @@ namespace Greenshot.Editor.Drawing AddField(GetType(), FieldType.SHADOW, true); } - protected override void OnDeserialized(StreamingContext context) - { - Init(); - } - protected void Init() { Adorners.Add(new MoveAdorner(this, Positions.TopLeft)); diff --git a/src/Greenshot.Editor/Drawing/MetafileContainer.cs b/src/Greenshot.Editor/Drawing/MetafileContainer.cs index 3225c4700..0e115aac6 100644 --- a/src/Greenshot.Editor/Drawing/MetafileContainer.cs +++ b/src/Greenshot.Editor/Drawing/MetafileContainer.cs @@ -23,6 +23,7 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; +using System.IO; using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Interfaces; @@ -32,17 +33,44 @@ namespace Greenshot.Editor.Drawing /// /// This provides a resizable SVG container, redrawing the SVG in the size the container takes. /// - [Serializable] public class MetafileContainer : VectorGraphicsContainer { - private readonly Metafile _metafile; - - public MetafileContainer(Metafile metafile, ISurface parent) : base(parent) + private Metafile _metafile; + public Metafile Metafile { - _metafile = metafile; - Size = new NativeSize(metafile.Width/4, metafile.Height/4); + get => _metafile; } - + + /// + /// Original file content. Is used for serialization. + /// More Information: GDI+ does not support saving .wmf or .emf files, because there is no encoder. + /// So we need to save the original file content for deserialization. + /// + public MemoryStream MetafileContent = new MemoryStream(); + + public MetafileContainer(Stream stream, ISurface parent) : base(parent) + { + + stream.CopyTo(MetafileContent); + stream.Seek(0, SeekOrigin.Begin); + var image = Image.FromStream(stream, true, true); + if (image is Metafile metaFile) + { + _metafile = metaFile; + Size = new NativeSize(_metafile.Width / 4, _metafile.Height / 4); + } else if (image is Bitmap imageFile) + { + // Fallback to support old files version 1.03 + // if the stream is not a Metafile, we create a Metafile from the Bitmap. + _metafile = CreateMetafileFromImage(imageFile); + Size = new NativeSize(imageFile.Width, imageFile.Height); + } + else + { + throw new ArgumentException("Stream is not a valid Metafile"); + } + } + protected override Image ComputeBitmap() { var image = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent); @@ -74,6 +102,37 @@ namespace Greenshot.Editor.Drawing base.Dispose(disposing); } + /// + /// Creates a new from the specified . + /// + /// The source to be converted into a . Cannot be . + /// A object that contains the graphical content of the specified . + /// Thrown if is . + private static Metafile CreateMetafileFromImage(Image image) + { + if (image == null) throw new ArgumentNullException(nameof(image)); + + using (Bitmap tempBitmap = new Bitmap(1, 1)) + using (Graphics referenceGraphics = Graphics.FromImage(tempBitmap)) + { + IntPtr hdc = referenceGraphics.GetHdc(); + try + { + // Erstelle ein neues Metafile mit der Größe des Bildes + Metafile metafile = new Metafile(hdc, new Rectangle(0, 0, image.Width, image.Height), MetafileFrameUnit.Pixel, EmfType.EmfOnly); + using (Graphics gMetafile = Graphics.FromImage(metafile)) + { + gMetafile.DrawImage(image, 0, 0, image.Width, image.Height); + } + return metafile; + } + finally + { + referenceGraphics.ReleaseHdc(hdc); + } + } + } public override bool HasDefaultSize => true; public override NativeSize DefaultSize => new NativeSize(_metafile.Width, _metafile.Height); diff --git a/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs b/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs index b0c890ffc..3294a8ef3 100644 --- a/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs +++ b/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs @@ -19,8 +19,6 @@ * along with this program. If not, see . */ -using System; -using System.Runtime.Serialization; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Fields; @@ -31,7 +29,6 @@ namespace Greenshot.Editor.Drawing /// /// This is a FilterContainer for the obfuscator filters like blur and pixelate. /// - [Serializable] public class ObfuscateContainer : FilterContainer { public ObfuscateContainer(ISurface parent) : base(parent) @@ -45,11 +42,6 @@ namespace Greenshot.Editor.Drawing AddField(GetType(), FieldType.PREPARED_FILTER_OBFUSCATE, PreparedFilter.PIXELIZE); } - protected override void OnDeserialized(StreamingContext context) - { - Init(); - } - private void Init() { FieldChanged += ObfuscateContainer_OnFieldChanged; diff --git a/src/Greenshot.Editor/Drawing/RectangleContainer.cs b/src/Greenshot.Editor/Drawing/RectangleContainer.cs index 717e1a593..410a52117 100644 --- a/src/Greenshot.Editor/Drawing/RectangleContainer.cs +++ b/src/Greenshot.Editor/Drawing/RectangleContainer.cs @@ -35,7 +35,6 @@ namespace Greenshot.Editor.Drawing /// /// Represents a rectangular shape on the Surface /// - [Serializable] public class RectangleContainer : DrawableContainer { public RectangleContainer(ISurface parent) : base(parent) @@ -43,16 +42,6 @@ namespace Greenshot.Editor.Drawing Init(); } - /// - /// Do some logic to make sure all field are initiated correctly - /// - /// StreamingContext - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } - private void Init() { CreateDefaultAdorners(); diff --git a/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs b/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs index e1f37544b..b3065a000 100644 --- a/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs +++ b/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs @@ -23,7 +23,6 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; -using System.Runtime.Serialization; using Dapplo.Windows.Common.Extensions; using Dapplo.Windows.Common.Structs; using Greenshot.Base.Interfaces; @@ -36,41 +35,36 @@ namespace Greenshot.Editor.Drawing /// /// Description of SpeechbubbleContainer. /// - [Serializable] public class SpeechbubbleContainer : TextContainer { private Point _initialGripperPoint; - // Only used for serializing the TargetGripper location + /// + /// Location of target gripper, used to draw the tail of the speech bubble. + /// private Point _storedTargetGripperLocation; - /// - /// Store the current location of the target gripper - /// - /// - [OnSerializing] - private void SetValuesOnSerializing(StreamingContext context) + /// + public Point StoredTargetGripperLocation { - if (TargetAdorner != null) - { - _storedTargetGripperLocation = TargetAdorner.Location; - } - } - - /// - /// Restore the target gripper - /// - /// StreamingContext - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - InitTargetAdorner(_storedTargetGripperLocation); + get => _storedTargetGripperLocation; + set => _storedTargetGripperLocation = value; } public SpeechbubbleContainer(ISurface parent) : base(parent) { } + /// + /// + /// + /// And initialize target adorner using the stored gripper location. + public override void OnDeserialized() + { + base.OnDeserialized(); + InitTargetAdorner(_storedTargetGripperLocation); + } + /// /// We set our own field values /// diff --git a/src/Greenshot.Editor/Drawing/StepLabelContainer.cs b/src/Greenshot.Editor/Drawing/StepLabelContainer.cs index 7edecc4f8..a085c537b 100644 --- a/src/Greenshot.Editor/Drawing/StepLabelContainer.cs +++ b/src/Greenshot.Editor/Drawing/StepLabelContainer.cs @@ -23,7 +23,6 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; -using System.Runtime.Serialization; using System.Windows.Forms; using Dapplo.Windows.Common.Extensions; using Dapplo.Windows.Common.Structs; @@ -37,11 +36,11 @@ namespace Greenshot.Editor.Drawing /// This is an enumerated label, every single StepLabelContainer shows the number of the order it was created. /// To make sure that deleting recalculates, we check the location before every draw. /// - [Serializable] public sealed class StepLabelContainer : DrawableContainer { - [NonSerialized] private StringFormat _stringFormat = new StringFormat(); + private StringFormat _stringFormat = new StringFormat(); + // TODO: currently not in use, but implemented. Define a new Boolean-Field for this, so it can be changed in the UI private readonly bool _drawAsRectangle = false; public StepLabelContainer(ISurface parent) : base(parent) @@ -56,44 +55,23 @@ namespace Greenshot.Editor.Drawing CreateDefaultAdorners(); } - // Used to store the number of this label, so when deserializing it can be placed back to the StepLabels list in the right location - private int _number; - - // Used to store the counter start of the Surface, as the surface is NOT stored. + /// + /// Used to store the counter start of the Surface on serialization / deserialization. + /// + /// The Surface itself is not stored. All StepLabelContainer will store the same start value. It's a bit hacky. private int _counterStart = 1; - public int Number - { - get { return _number; } - set { _number = value; } - } - /// - /// Retrieve the counter before serializing + /// Used to store the number of this label on serialization / deserialization. /// - /// - [OnSerializing] - private void SetValuesOnSerializing(StreamingContext context) - { - if (InternalParent == null) return; + /// This is not the displayed number, but the internal number of the label. The displayed number is calculated based on position in container list in the Surface, while drawing . + public int Number { get;set; } - Number = InternalParent.CountStepLabels(this); - _counterStart = InternalParent.CounterStart; - } - - /// - /// Restore values that don't serialize - /// - /// - protected override void OnDeserialized(StreamingContext context) - { - Init(); - _stringFormat = new StringFormat - { - Alignment = StringAlignment.Center, - LineAlignment = StringAlignment.Center - }; - } + /// + public int CounterStart { + get { return _counterStart; } + set { _counterStart = value; } + } /// /// Add the StepLabel to the parent diff --git a/src/Greenshot.Editor/Drawing/Surface.cs b/src/Greenshot.Editor/Drawing/Surface.cs index 6d47a67a6..b77d13d64 100644 --- a/src/Greenshot.Editor/Drawing/Surface.cs +++ b/src/Greenshot.Editor/Drawing/Surface.cs @@ -25,10 +25,7 @@ using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; -using System.IO; using System.Linq; -using System.Runtime.Serialization.Formatters.Binary; -using System.ServiceModel.Security; using System.Windows.Forms; using Dapplo.Windows.Common.Extensions; using Dapplo.Windows.Common.Structs; @@ -41,7 +38,8 @@ using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Drawing.Adorners; using Greenshot.Editor.Configuration; using Greenshot.Editor.Drawing.Fields; -using Greenshot.Editor.Helpers; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; using Greenshot.Editor.Memento; using log4net; @@ -689,59 +687,18 @@ namespace Greenshot.Editor.Drawing _undoStack.Push(memento); } } - } + } - /// - /// This saves the elements of this surface to a stream. - /// Is used to save a template of the complete surface - /// - /// - /// - public long SaveElementsToStream(Stream streamWrite) + /// + public void LoadElements(IDrawableContainerList containerList) { - long bytesWritten = 0; - try - { - long lengtBefore = streamWrite.Length; - BinaryFormatter binaryWrite = new BinaryFormatter(); - binaryWrite.Serialize(streamWrite, _elements); - bytesWritten = streamWrite.Length - lengtBefore; - } - catch (Exception e) - { - LOG.Error("Error serializing elements to stream.", e); - } - - return bytesWritten; - } - - /// - /// This loads elements from a stream, among others this is used to load a surface. - /// - /// - public void LoadElementsFromStream(Stream streamRead) - { - try - { - BinaryFormatter binaryRead = new BinaryFormatter(); - binaryRead.Binder = new BinaryFormatterHelper(); - IDrawableContainerList loadedElements = (IDrawableContainerList) binaryRead.Deserialize(streamRead); - loadedElements.Parent = this; - // Make sure the steplabels are sorted according to their number - _stepLabels.Sort((p1, p2) => p1.Number.CompareTo(p2.Number)); - DeselectAllElements(); - AddElements(loadedElements); - SelectElements(loadedElements); - FieldAggregator.BindElements(loadedElements); - } - catch (SecurityAccessDeniedException) - { - throw; - } - catch (Exception e) - { - LOG.Error("Error serializing elements from stream.", e); - } + containerList.Parent = this; + // Make sure the steplabels are sorted according to their number + _stepLabels.Sort((p1, p2) => p1.Number.CompareTo(p2.Number)); + DeselectAllElements(); + AddElements(containerList); + SelectElements(containerList); + FieldAggregator.BindElements(containerList); } /// @@ -2082,8 +2039,11 @@ namespace Greenshot.Editor.Drawing /// public void CutSelectedElements() { - if (!HasSelectedElements) return; - ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); + if (!HasSelectedElements) return; + + var serializedData = DtoHelper.SerializeDrawableContainerList((DrawableContainerList)selectedElements); + ClipboardHelper.SetClipboardData(typeof(DrawableContainerListDto), serializedData); + RemoveSelectedElements(); } @@ -2092,8 +2052,10 @@ namespace Greenshot.Editor.Drawing /// public void CopySelectedElements() { - if (!HasSelectedElements) return; - ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); + if (!HasSelectedElements) return; + + var serializedData = DtoHelper.SerializeDrawableContainerList((DrawableContainerList)selectedElements); + ClipboardHelper.SetClipboardData(typeof(DrawableContainerListDto), serializedData); } /// @@ -2200,9 +2162,12 @@ namespace Greenshot.Editor.Drawing } } - if (formats.Contains(typeof(IDrawableContainerList).FullName)) - { - IDrawableContainerList dcs = (IDrawableContainerList) ClipboardHelper.GetFromDataObject(clipboard, typeof(IDrawableContainerList)); + if (formats.Contains(typeof(DrawableContainerListDto).FullName)) + { + var serializedData = ClipboardHelper.GetFromDataObject(clipboard, typeof(DrawableContainerListDto)) as byte[]; + + IDrawableContainerList dcs = DtoHelper.DeserializeDrawableContainerList(serializedData); + if (dcs != null) { // Make element(s) only move 10,10 if the surface is the same diff --git a/src/Greenshot.Editor/Drawing/SvgContainer.cs b/src/Greenshot.Editor/Drawing/SvgContainer.cs index 85b8cb43d..b1662f455 100644 --- a/src/Greenshot.Editor/Drawing/SvgContainer.cs +++ b/src/Greenshot.Editor/Drawing/SvgContainer.cs @@ -19,9 +19,8 @@ * along with this program. If not, see . */ -using System; using System.Drawing; -using System.IO; +using System.IO; using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Interfaces; @@ -32,35 +31,32 @@ namespace Greenshot.Editor.Drawing /// /// This provides a resizable SVG container, redrawing the SVG in the size the container takes. /// - [Serializable] public class SvgContainer : VectorGraphicsContainer { - private MemoryStream _svgContent; + public MemoryStream SvgContent; - [NonSerialized] private SvgDocument _svgDocument; public SvgContainer(Stream stream, ISurface parent) : base(parent) { - _svgContent = new MemoryStream(); - stream.CopyTo(_svgContent); + SvgContent = new MemoryStream(); + stream.CopyTo(SvgContent); Init(); Size = new Size((int)_svgDocument.Width, (int)_svgDocument.Height); } - protected override void Init() - { - base.Init(); - // Do nothing when there is no content - if (_svgContent == null) - { - return; - } - _svgContent.Position = 0; - - _svgDocument = SvgDocument.Open(_svgContent); - } - + private void Init() + { + // Do nothing when there is no content + if (SvgContent == null) + { + return; + } + SvgContent.Position = 0; + + _svgDocument = SvgDocument.Open(SvgContent); + } + protected override Image ComputeBitmap() { //var image = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent); diff --git a/src/Greenshot.Editor/Drawing/TextContainer.cs b/src/Greenshot.Editor/Drawing/TextContainer.cs index a49fd4bee..ca06c0cec 100644 --- a/src/Greenshot.Editor/Drawing/TextContainer.cs +++ b/src/Greenshot.Editor/Drawing/TextContainer.cs @@ -25,7 +25,6 @@ using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; -using System.Runtime.Serialization; using System.Windows.Forms; using Dapplo.Windows.Common.Extensions; using Dapplo.Windows.Common.Structs; @@ -40,7 +39,6 @@ namespace Greenshot.Editor.Drawing /// /// Represents a textbox (extends RectangleContainer for border/background support /// - [Serializable] public class TextContainer : RectangleContainer, ITextContainer { // If makeUndoable is true the next text-change will make the change undoable. @@ -48,15 +46,15 @@ namespace Greenshot.Editor.Drawing // Although the name is wrong, we can't change it due to file serialization // ReSharper disable once InconsistentNaming private bool makeUndoable; - [NonSerialized] private Font _font; + private Font _font; public Font Font => _font; - [NonSerialized] private TextBox _textBox; + private TextBox _textBox; /// /// The StringFormat object is not serializable!! /// - [NonSerialized] private StringFormat _stringFormat = new StringFormat(); + private StringFormat _stringFormat = new StringFormat(); public StringFormat StringFormat => _stringFormat; @@ -70,26 +68,27 @@ namespace Greenshot.Editor.Drawing get => text; set => ChangeText(value, true); } - - internal void ChangeText(string newText, bool allowUndoable) - { - if ((text != null || newText == null) && string.Equals(text, newText)) return; - - if (makeUndoable && allowUndoable && IsUndoable) - { - makeUndoable = false; - _parent.MakeUndoable(new TextChangeMemento(this), false); - } - - text = newText; - OnPropertyChanged("Text"); - } - public TextContainer(ISurface parent) : base(parent) { Init(); } + private void Init() + { + _stringFormat = new StringFormat + { + Trimming = StringTrimming.EllipsisWord + }; + + CreateTextBox(); + + UpdateFormat(); + UpdateTextBoxFormat(); + + PropertyChanged += TextContainer_PropertyChanged; + FieldChanged += TextContainer_FieldChanged; + } + protected override void InitializeFields() { AddField(GetType(), FieldType.LINE_THICKNESS, 2); @@ -104,16 +103,6 @@ namespace Greenshot.Editor.Drawing AddField(GetType(), FieldType.TEXT_VERTICAL_ALIGNMENT, StringAlignment.Center); } - /// - /// Do some logic to make sure all field are initiated correctly - /// - /// StreamingContext - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } - protected override void Dispose(bool disposing) { if (disposing) @@ -140,20 +129,18 @@ namespace Greenshot.Editor.Drawing base.Dispose(disposing); } - private void Init() + internal void ChangeText(string newText, bool allowUndoable) { - _stringFormat = new StringFormat + if ((text != null || newText == null) && string.Equals(text, newText)) return; + + if (makeUndoable && allowUndoable && IsUndoable) { - Trimming = StringTrimming.EllipsisWord - }; + makeUndoable = false; + _parent.MakeUndoable(new TextChangeMemento(this), false); + } - CreateTextBox(); - - UpdateFormat(); - UpdateTextBoxFormat(); - - PropertyChanged += TextContainer_PropertyChanged; - FieldChanged += TextContainer_FieldChanged; + text = newText; + OnPropertyChanged("Text"); } protected override void SwitchParent(ISurface newParent) diff --git a/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs b/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs index 43da94c7d..b18f31491 100644 --- a/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs +++ b/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs @@ -19,13 +19,12 @@ * along with this program. If not, see . */ -using System; using System.Drawing; using System.Drawing.Drawing2D; -using System.Runtime.Serialization; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Editor.Drawing.Adorners; +using log4net; namespace Greenshot.Editor.Drawing { @@ -33,11 +32,17 @@ namespace Greenshot.Editor.Drawing /// This is the base container for vector graphics, these ae graphics which can resize without loss of quality. /// Examples for this are SVG, WMF or EMF, but also graphics based on fonts (e.g. Emoji) /// - [Serializable] public abstract class VectorGraphicsContainer : DrawableContainer { + private static readonly ILog LOG = LogManager.GetLogger(typeof(VectorGraphicsContainer)); + + /// private int _rotationAngle; - protected int RotationAngle + + /// /// + /// This is the rotation angle of the vector graphics. It is used to rotate the graphics when rendering in . + /// + public int RotationAngle { get => _rotationAngle; set => _rotationAngle = value; @@ -47,37 +52,26 @@ namespace Greenshot.Editor.Drawing /// This is the cached version of the bitmap, pre-rendered to save performance /// Do not serialized, it can be rebuild with other information. /// - [NonSerialized] private Image _cachedImage; /// - /// Constructor takes care of calling Init + /// Constructor takes care of creating adorners /// /// ISurface - public VectorGraphicsContainer(ISurface parent) : base(parent) + protected VectorGraphicsContainer(ISurface parent) : base(parent) { - Init(); + InitAdorners(); } /// - /// Make sure Init is called after deserializing + /// For vector graphics the are not used. so we need to initialize the adorners here. /// - /// StreamingContext - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } - - /// - /// Init is called after creating the instance, and from OnDeserialized - /// This is the place to generate your adorners - /// - protected virtual void Init() + private void InitAdorners() { // Check if the adorners are already defined! if (Adorners.Count > 0) { + LOG.Warn("Adorners are already defined!"); return; } @@ -93,7 +87,6 @@ namespace Greenshot.Editor.Drawing /// When disposing==true all non-managed resources should be freed too! /// /// - protected override void Dispose(bool disposing) { if (disposing) diff --git a/src/Greenshot.Editor/EditorInitialize.cs b/src/Greenshot.Editor/EditorInitialize.cs index c1ee27db8..87164a04e 100644 --- a/src/Greenshot.Editor/EditorInitialize.cs +++ b/src/Greenshot.Editor/EditorInitialize.cs @@ -32,8 +32,10 @@ namespace Greenshot.Editor SimpleServiceProvider.Current.AddService( // All generic things, like gif, png, jpg etc. new DefaultFileFormatHandler(), - // Greenshot format + // Greenshot file format new GreenshotFileFormatHandler(), + // Greenshot template format + new GreenshotTemplateFormatHandler(), // For .svg support new SvgFileFormatHandler(), // For clipboard support diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/ArrowContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/ArrowContainerDto.cs new file mode 100644 index 000000000..25021822c --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/ArrowContainerDto.cs @@ -0,0 +1,32 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// +[MessagePackObject] +public sealed class ArrowContainerDto : DrawableContainerDto +{ +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/CursorContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/CursorContainerDto.cs new file mode 100644 index 000000000..e95c19899 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/CursorContainerDto.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// The is not really in use. For a capture with mouse cursor the IconContainer is used. See: cctor Surface(ICapture capture) in . +/// +[MessagePackObject] +public sealed class CursorContainerDto : DrawableContainerDto +{ + // Because the CursorContainer is not used, the effort to serialize deserialize the System.Drawing.Cursor object is not justified - YAGNI + //[Key(101)] + //public byte[] Cursor { get; set; } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/DrawableContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/DrawableContainerDto.cs new file mode 100644 index 000000000..309433f43 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/DrawableContainerDto.cs @@ -0,0 +1,66 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Collections.Generic; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto.Fields; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// Simplified Version that supports Properties from and as well. +/// Ignore because it is only used for filters at the moment and all field values from the filters are already in . +/// +[Union(0, typeof(ImageContainerDto))] +[Union(1, typeof(LineContainerDto))] +[Union(2, typeof(RectangleContainerDto))] +[Union(3, typeof(IconContainerDto))] +[Union(4, typeof(TextContainerDto))] +[Union(5, typeof(SpeechbubbleContainerDto))] +[Union(6, typeof(ArrowContainerDto))] +[Union(7, typeof(CursorContainerDto))] +[Union(8, typeof(EllipseContainerDto))] +[Union(9, typeof(FreehandContainerDto))] +[Union(10, typeof(HighlightContainerDto))] +[Union(11, typeof(MetafileContainerDto))] +[Union(12, typeof(ObfuscateContainerDto))] +[Union(13, typeof(StepLabelContainerDto))] +[Union(14, typeof(SvgContainerDto))] +[MessagePackObject] +public abstract class DrawableContainerDto +{ + + [Key(10)] + public int Left { get; set; } + [Key(11)] + public int Top { get; set; } + [Key(12)] + public int Width { get; set; } + [Key(13)] + public int Height { get; set; } + + [Key(14)] + public List Fields { get; set; } = []; +} + + diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/DrawableContainerListDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/DrawableContainerListDto.cs new file mode 100644 index 000000000..276dcd7be --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/DrawableContainerListDto.cs @@ -0,0 +1,35 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Collections.Generic; +using Greenshot.Editor.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// +[MessagePackObject] +public sealed class DrawableContainerListDto +{ + [Key(10)] + public List ContainerList { get; set; } = []; +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/EllipseContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/EllipseContainerDto.cs new file mode 100644 index 000000000..244f903c8 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/EllipseContainerDto.cs @@ -0,0 +1,31 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// +[MessagePackObject] +public sealed class EllipseContainerDto : DrawableContainerDto +{ +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/FreehandContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/FreehandContainerDto.cs new file mode 100644 index 000000000..721541465 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/FreehandContainerDto.cs @@ -0,0 +1,35 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Collections.Generic; +using Greenshot.Editor.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// +[MessagePackObject] +public sealed class FreehandContainerDto : DrawableContainerDto +{ + [Key(100)] + public List CapturePoints { get; set; } = []; +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/HighlightContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/HighlightContainerDto.cs new file mode 100644 index 000000000..94f95da8e --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/HighlightContainerDto.cs @@ -0,0 +1,34 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// Ignore because they would be recreated on deserialization based on field values of . +/// +[MessagePackObject] +public sealed class HighlightContainerDto : DrawableContainerDto +{ +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/IconContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/IconContainerDto.cs new file mode 100644 index 000000000..599623dc5 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/IconContainerDto.cs @@ -0,0 +1,33 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// +[MessagePackObject] +public sealed class IconContainerDto : DrawableContainerDto +{ + [Key(100)] + public byte[] Icon { get; set; } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/ImageContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/ImageContainerDto.cs new file mode 100644 index 000000000..adc1a4db8 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/ImageContainerDto.cs @@ -0,0 +1,34 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// +[MessagePackObject] +public sealed class ImageContainerDto : DrawableContainerDto +{ + [Key(100)] + public byte[] Image { get; set; } // Store image as byte array +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/LineContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/LineContainerDto.cs new file mode 100644 index 000000000..b3c5fab92 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/LineContainerDto.cs @@ -0,0 +1,32 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// +[MessagePackObject] +public sealed class LineContainerDto : DrawableContainerDto +{ +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/MetafileContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/MetafileContainerDto.cs new file mode 100644 index 000000000..6c2d59a39 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/MetafileContainerDto.cs @@ -0,0 +1,39 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// Simplified version that supports properties from as well. +/// +[MessagePackObject] +public sealed class MetafileContainerDto : DrawableContainerDto +{ + [Key(100)] + public int RotationAngle { get; set; } + + [Key(101)] + public byte[] MetafileData { get; set; } // Store metafile as byte array +} + diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/ObfuscateContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/ObfuscateContainerDto.cs new file mode 100644 index 000000000..5694110c4 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/ObfuscateContainerDto.cs @@ -0,0 +1,34 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// Ignore because they would be recreated on deserialization based on field values of . +/// +[MessagePackObject] +public sealed class ObfuscateContainerDto : DrawableContainerDto +{ +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/PointDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/PointDto.cs new file mode 100644 index 000000000..a50de3647 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/PointDto.cs @@ -0,0 +1,33 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +[MessagePackObject] +public sealed class PointDto +{ + [Key(10)] + public int X { get; set; } + + [Key(11)] + public int Y { get; set; } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/RectangleContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/RectangleContainerDto.cs new file mode 100644 index 000000000..b136802e8 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/RectangleContainerDto.cs @@ -0,0 +1,32 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// +[MessagePackObject] +public sealed class RectangleContainerDto : DrawableContainerDto +{ +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/SpeechbubbleContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/SpeechbubbleContainerDto.cs new file mode 100644 index 000000000..e223721ae --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/SpeechbubbleContainerDto.cs @@ -0,0 +1,37 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// +[MessagePackObject] +public sealed class SpeechbubbleContainerDto : DrawableContainerDto +{ + [Key(100)] + public string Text { get; set; } = string.Empty; + + [Key(101)] + public PointDto StoredTargetGripperLocation { get; set; } = new() { X = 0, Y = 0 }; +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/StepLabelContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/StepLabelContainerDto.cs new file mode 100644 index 000000000..4d7d74e8d --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/StepLabelContainerDto.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// +[MessagePackObject] +public sealed class StepLabelContainerDto : DrawableContainerDto +{ + [Key(100)] + public int Number { get; set; } = 1; + + [Key(101)] + public int CounterStart { get; set; } = 1; +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/SvgContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/SvgContainerDto.cs new file mode 100644 index 000000000..742635293 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/SvgContainerDto.cs @@ -0,0 +1,38 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +/// +/// Data transfer object to serialize objects. +/// Simplified version that supports properties from as well. +/// +[MessagePackObject] +public sealed class SvgContainerDto : DrawableContainerDto +{ + [Key(100)] + public int RotationAngle { get; set; } + + [Key(101)] + public byte[] SvgData { get; set; } // Store SVG as byte array +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Container/TextContainerDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Container/TextContainerDto.cs new file mode 100644 index 000000000..57bdaef17 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Container/TextContainerDto.cs @@ -0,0 +1,30 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Container; + +[MessagePackObject] +public sealed class TextContainerDto : DrawableContainerDto +{ + [Key(100)] + public string Text { get; set; } = string.Empty; +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/ConvertDomainToDto.cs b/src/Greenshot.Editor/FileFormat/Dto/ConvertDomainToDto.cs new file mode 100644 index 000000000..f357d9123 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/ConvertDomainToDto.cs @@ -0,0 +1,406 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; +using static Greenshot.Editor.Drawing.ArrowContainer; +using static Greenshot.Editor.Drawing.FilterContainer; + +namespace Greenshot.Editor.FileFormat.Dto; + +/// +/// Provides methods to convert domain objects, mainly drawable container and fields, into their corresponding Data Transfer Object (DTO) representations. +/// +/// This class contains a collection of static ToDto() methods that handle the transformation +public static class ConvertDomainToDto +{ + + public static GreenshotFileDto ToDto(GreenshotFile domain) + { + if (domain == null) return null; + + return new GreenshotFileDto + { + ContainerList = ToDto(domain.ContainerList), + Image = ImageToByteArray(domain.Image), + RenderedImage = ImageToByteArray(domain.RenderedImage), + FormatVersion = domain.FormatVersion, + SchemaVersion = domain.SchemaVersion + }; + } + + public static GreenshotTemplateDto ToDto(GreenshotTemplate domain) + { + if (domain == null) return null; + + return new GreenshotTemplateDto + { + ContainerList = ToDto(domain.ContainerList), + FormatVersion = domain.FormatVersion, + SchemaVersion = domain.SchemaVersion + }; + } + + public static DrawableContainerListDto ToDto(DrawableContainerList domain) + { + if (domain == null) return null; + + var dtoList = new DrawableContainerListDto(); + foreach (var item in domain) + { + dtoList.ContainerList.Add(ToDto(item)); + } + return dtoList; + } + + public static DrawableContainerDto ToDto(IDrawableContainer domain) => + domain switch + { + null => null, + ImageContainer imageContainer => ToDto(imageContainer), + ArrowContainer arrowContainer => ToDto(arrowContainer), + LineContainer lineContainer => ToDto(lineContainer), + SpeechbubbleContainer speechbubbleContainer => ToDto(speechbubbleContainer), + TextContainer textContainer => ToDto(textContainer), + RectangleContainer rectangleContainer => ToDto(rectangleContainer), + IconContainer iconContainer => ToDto(iconContainer), + StepLabelContainer stepLabelContainer => ToDto(stepLabelContainer), + EllipseContainer ellipseContainer => ToDto(ellipseContainer), + HighlightContainer highlightContainer => ToDto(highlightContainer), + ObfuscateContainer obfuscateContainer => ToDto(obfuscateContainer), + CursorContainer cursorContainer => ToDto(cursorContainer), + FreehandContainer freehandContainer => ToDto(freehandContainer), + MetafileContainer metafileContainer => ToDto(metafileContainer), + SvgContainer svgContainer => ToDto(svgContainer), + _ => throw new ArgumentException($"Unsupported IDrawableContainer type: {domain.GetType()}"), + }; + + public static ImageContainerDto ToDto(ImageContainer domain) + { + if (domain == null) return null; + + var dto = new ImageContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList(), + Image = ImageToByteArray(domain.Image) + }; + return dto; + } + + public static MetafileContainerDto ToDto(MetafileContainer domain) + { + if (domain == null) return null; + + var dto = new MetafileContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList(), + MetafileData = domain.MetafileContent.ToArray(), + RotationAngle = domain.RotationAngle + }; + return dto; + } + + public static SvgContainerDto ToDto(SvgContainer domain) + { + if (domain == null) return null; + + var dto = new SvgContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList(), + SvgData = domain.SvgContent.ToArray(), + RotationAngle = domain.RotationAngle + }; + return dto; + } + + public static LineContainerDto ToDto(LineContainer domain) + { + if (domain == null) return null; + + var dto = new LineContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList() + }; + return dto; + } + + public static RectangleContainerDto ToDto(RectangleContainer domain) + { + if (domain == null) return null; + + var dto = new RectangleContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList() + }; + return dto; + } + + public static TextContainerDto ToDto(TextContainer domain) + { + if (domain == null) return null; + + var dto = new TextContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Text = domain.Text, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList() + }; + return dto; + } + + public static SpeechbubbleContainerDto ToDto(SpeechbubbleContainer domain) + { + if (domain == null) return null; + + var dto = new SpeechbubbleContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Text = domain.Text, + StoredTargetGripperLocation = new PointDto { X = domain.StoredTargetGripperLocation.X, Y = domain.StoredTargetGripperLocation.Y }, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList() + }; + return dto; + } + + public static ArrowContainerDto ToDto(ArrowContainer domain) + { + if (domain == null) return null; + + var dto = new ArrowContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList() + }; + return dto; + } + + public static IconContainerDto ToDto(IconContainer domain) + { + if (domain == null) return null; + + var dto = new IconContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList(), + Icon = IconToByteArray(domain.Icon) + }; + return dto; + } + + public static StepLabelContainerDto ToDto(StepLabelContainer domain) + { + if (domain == null) return null; + + // recalculate the StepLabel number from parent Surface if it exists + if (domain.Parent is Surface parentSurface) + { + domain.Number = parentSurface.CountStepLabels(domain); + domain.CounterStart = parentSurface.CounterStart; + } + + var dto = new StepLabelContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList(), + Number = domain.Number, + CounterStart = domain.CounterStart + }; + return dto; + } + + public static EllipseContainerDto ToDto(EllipseContainer domain) + { + if (domain == null) return null; + + var dto = new EllipseContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList() + }; + return dto; + } + + public static HighlightContainerDto ToDto(HighlightContainer domain) + { + if (domain == null) return null; + + var dto = new HighlightContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList() + }; + return dto; + } + + public static ObfuscateContainerDto ToDto(ObfuscateContainer domain) + { + if (domain == null) return null; + + var dto = new ObfuscateContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList() + }; + return dto; + } + + public static CursorContainerDto ToDto(CursorContainer domain) + { + if (domain == null) return null; + + var dto = new CursorContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList() + }; + return dto; + } + + public static FreehandContainerDto ToDto(FreehandContainer domain) + { + if (domain == null) return null; + + var dto = new FreehandContainerDto + { + Left = domain.Left, + Top = domain.Top, + Width = domain.Width, + Height = domain.Height, + Fields = domain.GetFields() == null ? [] : domain.GetFields().Select(ToDto).ToList(), + CapturePoints = domain.CapturePoints.Select(p => new PointDto { X = p.X, Y = p.Y }).ToList() + }; + return dto; + } + + public static FieldDto ToDto(IField domain) + { + if (domain == null) return null; + + return new FieldDto + { + FieldTypeName = domain.FieldType.Name, + Scope = domain.Scope, + Value = ConvertValueToDto(domain.Value) + }; + } + + /// + /// Converts a given value to its corresponding representation. + /// + /// The method is public mainly because of testing. + /// The value to convert. + /// A specific subclass of instance representing the provided value. + /// Thrown if the type of is not supported. + public static FieldValueDto ConvertValueToDto(object value) => + value switch + { + null => new NullFieldValueDto(), + int intValue => new IntFieldValueDto { Value = intValue }, + string stringValue => new StringFieldValueDto { Value = stringValue }, + bool boolValue => new BoolFieldValueDto { Value = boolValue }, + float singleValue => new SingleFieldValueDto { Value = singleValue }, + double doubleValue => new DoubleFieldValueDto { Value = doubleValue }, + decimal decimalValue => new DecimalFieldValueDto { Value = decimalValue }, + Color colorValue => new ColorFieldValueDto { Value = colorValue }, + ArrowHeadCombination arrowHeadCombinationValue => new ArrowHeadCombinationFieldValueDto { Value = arrowHeadCombinationValue }, + FieldFlag fieldFlagValue => new FieldFlagFieldValueDto { Value = fieldFlagValue }, + PreparedFilter preparedFilterValue => new PreparedFilterFieldValueDto { Value = preparedFilterValue }, + StringAlignment stringAlignmentValue => new StringAlignmentFieldValueDto { Value = stringAlignmentValue }, + _ => throw new ArgumentException($"Unsupported type: {value.GetType()}"), + }; + + /// + /// Converts the specified to a byte array in PNG format. + /// + private static byte[] ImageToByteArray(Image image) + { + if (image == null) return null; + + using var memoryStream = new MemoryStream(); + image.Save(memoryStream, ImageFormat.Png); + return memoryStream.ToArray(); + } + + /// + /// Converts the specified to a byte array representation. + /// + private static byte[] IconToByteArray(Icon icon) + { + if (icon == null) return null; + + using var memoryStream = new MemoryStream(); + icon.Save(memoryStream); + return memoryStream.ToArray(); + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/Dto/ConvertDtoToDomain.cs b/src/Greenshot.Editor/FileFormat/Dto/ConvertDtoToDomain.cs new file mode 100644 index 000000000..03f2f61f1 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/ConvertDtoToDomain.cs @@ -0,0 +1,399 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; +using log4net; + +namespace Greenshot.Editor.FileFormat.Dto; + + +/// +/// Provides methods to convert various Data Transfer Object (DTO) types into their corresponding domain types, mainly drawable container and fields. +/// +/// This class contains a collection of static ToDomain() methods that handle the transformation +public static class ConvertDtoToDomain +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(ConvertDtoToDomain)); + + /// + /// Checks if the provided parent surface is null and creates a new one if it is. + /// + private static ISurface CheckOrCreateParentSurface( ISurface parentSurface) => parentSurface ?? SimpleServiceProvider.Current.GetInstance>().Invoke(); + + public static GreenshotFile ToDomain(GreenshotFileDto dto) + { + if (dto == null) return null; + + return new GreenshotFile + { + ContainerList = ToDomain(dto.ContainerList), + Image = ByteArrayToImage(dto.Image), + RenderedImage = ByteArrayToImage(dto.RenderedImage), + SchemaVersion = dto.SchemaVersion, + FormatVersion = dto.FormatVersion + }; + } + + public static GreenshotTemplate ToDomain(GreenshotTemplateDto dto) + { + if (dto == null) return null; + + return new GreenshotTemplate + { + ContainerList = ToDomain(dto.ContainerList), + SchemaVersion = dto.SchemaVersion, + FormatVersion = dto.FormatVersion + }; + } + + public static DrawableContainerList ToDomain(DrawableContainerListDto dto) + { + if (dto == null) return null; + + // new Surface for all Container + ISurface parentSurface = CheckOrCreateParentSurface(null); + + var domainList = new DrawableContainerList(); + foreach (var item in dto.ContainerList) + { + domainList.Add(ToDomain(item, parentSurface)); + } + return domainList; + } + + public static IDrawableContainer ToDomain(DrawableContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + return dto switch + { + ImageContainerDto imageContainerDto => ToDomain(imageContainerDto, parentSurface), + ArrowContainerDto arrowContainerDto => ToDomain(arrowContainerDto, parentSurface), + LineContainerDto lineContainerDto => ToDomain(lineContainerDto, parentSurface), + RectangleContainerDto rectangleContainerDto => ToDomain(rectangleContainerDto, parentSurface), + IconContainerDto iconContainerDto => ToDomain(iconContainerDto, parentSurface), + StepLabelContainerDto stepLabelContainerDto => ToDomain(stepLabelContainerDto, parentSurface), + EllipseContainerDto ellipseContainerDto => ToDomain(ellipseContainerDto, parentSurface), + HighlightContainerDto highlightContainerDto => ToDomain(highlightContainerDto, parentSurface), + ObfuscateContainerDto obfuscateContainerDto => ToDomain(obfuscateContainerDto, parentSurface), + CursorContainerDto cursorContainerDto => ToDomain(cursorContainerDto, parentSurface), + FreehandContainerDto freehandContainerDto => ToDomain(freehandContainerDto, parentSurface), + TextContainerDto textContainerDto => ToDomain(textContainerDto, parentSurface), + SpeechbubbleContainerDto speechbubbleContainerDto => ToDomain(speechbubbleContainerDto, parentSurface), + MetafileContainerDto metafileContainerDto => ToDomain(metafileContainerDto, parentSurface), + SvgContainerDto svgContainerDto => ToDomain(svgContainerDto, parentSurface), + _ => throw new ArgumentException($"Unsupported IDrawableContainerDto type: {dto.GetType()}") + }; + } + + public static ImageContainer ToDomain(ImageContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new ImageContainer(parentSurface); + if (dto.Image !=null) + { + // The image setter recalculates the position and dimensions + // this is no problem, because we correct them later in InitDrawableContainer() + domain.Image = ByteArrayToImage(dto.Image); + }else + { + Log.Warn("ImageContainerDto contains no image. Creating an empty image with tranparent background as a replacement."); + // If no image is provided, we create an empty image with the specified dimensions + domain.Image = ImageHelper.CreateEmpty(Math.Max(dto.Width,50), Math.Max(dto.Height,50), PixelFormat.Format32bppArgb, Color.Transparent); + } + + return InitDrawableContainer(domain, dto); + } + + public static MetafileContainer ToDomain(MetafileContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new MetafileContainer(new MemoryStream(dto.MetafileData), parentSurface); + domain.RotationAngle = dto.RotationAngle; + + return InitDrawableContainer(domain, dto); + } + + public static SvgContainer ToDomain(SvgContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new SvgContainer(new MemoryStream(dto.SvgData), parentSurface); + domain.RotationAngle = dto.RotationAngle; + + return InitDrawableContainer(domain, dto); + } + + public static LineContainer ToDomain(LineContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new LineContainer(parentSurface); + return InitDrawableContainer(domain, dto); + } + + public static RectangleContainer ToDomain(RectangleContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new RectangleContainer(parentSurface); + return InitDrawableContainer(domain, dto); + } + + public static TextContainer ToDomain(TextContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new TextContainer(parentSurface) + { + Text = dto.Text + }; + return InitDrawableContainer(domain, dto); + } + + public static SpeechbubbleContainer ToDomain(SpeechbubbleContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new SpeechbubbleContainer(parentSurface) + { + Text = dto.Text, + StoredTargetGripperLocation = new Point(dto.StoredTargetGripperLocation.X, dto.StoredTargetGripperLocation.Y) + }; + return InitDrawableContainer(domain, dto); + } + + public static ArrowContainer ToDomain(ArrowContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new ArrowContainer(parentSurface); + return InitDrawableContainer(domain, dto); + } + + public static IconContainer ToDomain(IconContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new IconContainer(parentSurface); + if (dto.Icon !=null) + { + domain.Icon = ByteArrayToIcon(dto.Icon); + } + else + { + Log.Warn("IconContainerDto contains no Icon. Cannot create a replacement"); + } + return InitDrawableContainer(domain, dto); + } + + public static StepLabelContainer ToDomain(StepLabelContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new StepLabelContainer(parentSurface) + { + Number = dto.Number, + CounterStart = dto.CounterStart + }; + return InitDrawableContainer(domain, dto); + } + + public static EllipseContainer ToDomain(EllipseContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new EllipseContainer(parentSurface); + return InitDrawableContainer(domain, dto); + } + + public static HighlightContainer ToDomain(HighlightContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new HighlightContainer(parentSurface); + return InitDrawableContainer(domain, dto); + } + + public static ObfuscateContainer ToDomain(ObfuscateContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new ObfuscateContainer(parentSurface); + return InitDrawableContainer(domain, dto); + } + + public static CursorContainer ToDomain(CursorContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new CursorContainer(parentSurface); + return InitDrawableContainer(domain, dto); + } + + public static FreehandContainer ToDomain(FreehandContainerDto dto, ISurface parentSurface) + { + if (dto == null) return null; + + parentSurface = CheckOrCreateParentSurface(parentSurface); + + var domain = new FreehandContainer(parentSurface) + { + CapturePoints = dto.CapturePoints.Select(p => new Point(p.X, p.Y)).ToList() + }; + return InitDrawableContainer(domain, dto); + } + + public static IField ToDomain(FieldDto dto) + { + IFieldType FieldTypeNameToFieldTyp(string name) + { + foreach (var fieldType in FieldType.Values) + { + if (fieldType.Name.Equals(name)) + { + return fieldType; + } + } + + throw new ArgumentException($"Unknown field type name: {name}"); + } + + if (dto == null) return null; + + return new Field(FieldTypeNameToFieldTyp(dto.FieldTypeName), dto.Scope) + { + Value = ConvertDtoToValue(dto.Value) + }; + } + + /// + /// Converts a instance to its corresponding value. + /// + /// The method is public mainly because of testing. + /// The value extracted from the using its GetValue method, or + /// if is . + public static object ConvertDtoToValue(FieldValueDto dto) + { + return dto?.GetValue(); + } + + /// + /// Initializes a drawable container. + /// + /// This method sets the position, dimensions, and field values of the container based on the + /// provided DTO. It also invokes the method to finalize the + /// initialization process. + /// The type of the drawable container, which must inherit from . + /// The drawable container instance to initialize. Must not be . + /// The data transfer object containing the properties and field values to apply. Must not be . + /// The initialized drawable container of type . + private static T InitDrawableContainer(T container, DrawableContainerDto dto) where T : DrawableContainer + { + container.Left = dto.Left; + container.Top = dto.Top; + container.Width = dto.Width; + container.Height = dto.Height; + + TranferFieldValues(dto.Fields, container); + + container.OnDeserialized(); + return container; + } + + /// + /// Transfers field values to a . + /// + /// This method uses to add or update the field value. + private static void TranferFieldValues(List dtoFields, DrawableContainer domain) + { + foreach (var field in dtoFields.Select(ToDomain)) + { + domain.SetFieldValue(field.FieldType, field.Value); + } + } + + /// + /// Converts a byte array into an object. + /// + private static Image ByteArrayToImage(byte[] byteArrayIn) + { + if (byteArrayIn == null) return null; + using var ms = new MemoryStream(byteArrayIn); + return Image.FromStream(ms); + } + + /// + /// Converts a byte array into an object. + /// + private static Icon ByteArrayToIcon(byte[] byteArrayIn) + { + if (byteArrayIn == null) return null; + using var ms = new MemoryStream(byteArrayIn); + return new Icon(ms); + } + +} + diff --git a/src/Greenshot.Editor/FileFormat/Dto/DtoHelper.cs b/src/Greenshot.Editor/FileFormat/Dto/DtoHelper.cs new file mode 100644 index 000000000..c6d30db28 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/DtoHelper.cs @@ -0,0 +1,96 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.Linq; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.Forms; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto; + +/// +/// All DTO classes should not contain any business logic. This applies to helper methods as well +/// So this is the place for them. +/// +public static class DtoHelper +{ + /// + /// Get the value of a field from a object. Null if not found. + /// + /// + /// + /// + public static object GetFieldValue(DrawableContainerDto drawableContainer, IFieldType fieldType) + { + return drawableContainer.Fields? + .FirstOrDefault(x => x.FieldTypeName == fieldType.Name)? + .Value.GetValue(); + } + + /// + /// We store Color as an ARGB integer, so we have to compare two colors by their ARGB values + /// + /// + /// + /// + public static bool CompareColorValue(Color leftColor, Color rightColor) + { + return leftColor.ToArgb() == rightColor.ToArgb(); + } + + /// + /// Helper method. + /// hides the ARGB value for KnownColor, so we have to use this method to print ARGB value every time. + /// + /// + /// + public static string ArgbString(Color color) => $"ARGB({color.A}, {color.R}, {color.G}, {color.B})"; + + /// + /// Serializes a into a byte array using MessagePack serialization. + /// + /// This method converts the DrawableContainerList into a DTO before serializing it. + ///
It is mainly used for copying the DrawableContainerList to Clipboard with "Greenshot.Editor.FileFormat.Dto.Container.DrawableContainerListDto" as identifier. + /// See also:
+ /// The instance to serialize. Must not be . + /// A byte array containing the serialized representation of the . + public static byte[] SerializeDrawableContainerList(DrawableContainerList drawableContainerList) + { + var dto = ConvertDomainToDto.ToDto(drawableContainerList); + return MessagePackSerializer.Serialize(dto); + } + + /// + /// Deserializes a byte array into a using MessagePack serialization. + /// + /// It deserializes the byte array into a and then converts it to a . + ///
It is mainly used for reading the DrawableContainerList from Clipboard with "Greenshot.Editor.FileFormat.Dto.Container.DrawableContainerListDto" as identifier. + /// See also: + ///
+ /// A byte array containing the serialized representation of the . + public static DrawableContainerList DeserializeDrawableContainerList(byte[] data) + { + var dto = MessagePackSerializer.Deserialize(data); + return ConvertDtoToDomain.ToDomain(dto); + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/ArrowHeadCombinationFieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/ArrowHeadCombinationFieldValueDto.cs new file mode 100644 index 000000000..16bcbdb8e --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/ArrowHeadCombinationFieldValueDto.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; +using static Greenshot.Editor.Drawing.ArrowContainer; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +[MessagePackObject] +public sealed class ArrowHeadCombinationFieldValueDto : FieldValueDto +{ + [Key(100)] + public ArrowHeadCombination Value { get; set; } = ArrowHeadCombination.NONE; + + public override object GetValue() + { + return Value; + } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/BoolFieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/BoolFieldValueDto.cs new file mode 100644 index 000000000..32ece7212 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/BoolFieldValueDto.cs @@ -0,0 +1,35 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +[MessagePackObject] +public sealed class BoolFieldValueDto : FieldValueDto +{ + [Key(100)] + public bool Value { get; set; } + + public override object GetValue() + { + return Value; + } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/ColorFieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/ColorFieldValueDto.cs new file mode 100644 index 000000000..04f17e171 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/ColorFieldValueDto.cs @@ -0,0 +1,53 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +/// +/// Represents a field value that stores color information. +/// +[MessagePackObject(AllowPrivate = true)] +// This needs to be a partial class to support private properties with MessagePack serialization +public sealed partial class ColorFieldValueDto : FieldValueDto +{ + [Key(100)] + private int Argb { get; set; } // Store Color as an ARGB integer + + [IgnoreMember] + public Color Value + { + get + { + return Color.FromArgb(Argb); + } + set + { + Argb = value.ToArgb(); + } + } + + public override object GetValue() + { + return Value; + } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/DecimalFieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/DecimalFieldValueDto.cs new file mode 100644 index 000000000..0787c5e47 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/DecimalFieldValueDto.cs @@ -0,0 +1,35 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +[MessagePackObject] +public sealed class DecimalFieldValueDto : FieldValueDto +{ + [Key(100)] + public decimal Value { get; set; } + + public override object GetValue() + { + return Value; + } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/DoubleFieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/DoubleFieldValueDto.cs new file mode 100644 index 000000000..2bf3cda9c --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/DoubleFieldValueDto.cs @@ -0,0 +1,35 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +[MessagePackObject] +public sealed class DoubleFieldValueDto : FieldValueDto +{ + [Key(100)] + public double Value { get; set; } + + public override object GetValue() + { + return Value; + } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/FieldDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/FieldDto.cs new file mode 100644 index 000000000..cd0bf23ff --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/FieldDto.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Diagnostics; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +[MessagePackObject] +[DebuggerDisplay("Scope = {Scope}, FieldTypeName = {FieldTypeName}, Value = {Value.GetValue()}")] +public sealed class FieldDto +{ + [Key(10)] + public FieldValueDto Value { get; set; } + [Key(11)] + public string FieldTypeName { get; set; } = string.Empty; + [Key(12)] + public string Scope { get; set; } = string.Empty; +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/FieldFlagFieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/FieldFlagFieldValueDto.cs new file mode 100644 index 000000000..789ea6b5f --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/FieldFlagFieldValueDto.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Interfaces.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +[MessagePackObject] +public sealed class FieldFlagFieldValueDto : FieldValueDto +{ + [Key(100)] + public FieldFlag Value { get; set; } = FieldFlag.NONE; + + public override object GetValue() + { + return Value; + } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/FieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/FieldValueDto.cs new file mode 100644 index 000000000..9d800fd47 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/FieldValueDto.cs @@ -0,0 +1,45 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.Drawing.Fields; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +/// +/// This is a specific Dto to support serialization for the possible types in +/// +[MessagePackObject] +[Union(0, typeof(NullFieldValueDto))] +[Union(1, typeof(IntFieldValueDto))] +[Union(2, typeof(StringFieldValueDto))] +[Union(3, typeof(BoolFieldValueDto))] +[Union(4, typeof(SingleFieldValueDto))] +[Union(5, typeof(DoubleFieldValueDto))] +[Union(6, typeof(DecimalFieldValueDto))] +[Union(7, typeof(ColorFieldValueDto))] +[Union(8, typeof(ArrowHeadCombinationFieldValueDto))] +[Union(9, typeof(FieldFlagFieldValueDto))] +[Union(10, typeof(PreparedFilterFieldValueDto))] +[Union(11, typeof(StringAlignmentFieldValueDto))] +public abstract class FieldValueDto +{ + public abstract object GetValue(); +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/IntFieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/IntFieldValueDto.cs new file mode 100644 index 000000000..f589853ce --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/IntFieldValueDto.cs @@ -0,0 +1,35 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +[MessagePackObject] +public sealed class IntFieldValueDto : FieldValueDto +{ + [Key(100)] + public int Value { get; set; } + + public override object GetValue() + { + return Value; + } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/NullFieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/NullFieldValueDto.cs new file mode 100644 index 000000000..a8f90a250 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/NullFieldValueDto.cs @@ -0,0 +1,35 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +[MessagePackObject] +public sealed class NullFieldValueDto : FieldValueDto +{ + [Key(100)] + public bool IsNull { get; set; } = true; // Use a boolean to represent null + + public override object GetValue() + { + return null; + } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/PreparedFilterFieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/PreparedFilterFieldValueDto.cs new file mode 100644 index 000000000..7e2ba6997 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/PreparedFilterFieldValueDto.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; +using static Greenshot.Editor.Drawing.FilterContainer; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +[MessagePackObject] +public sealed class PreparedFilterFieldValueDto : FieldValueDto +{ + [Key(100)] + public PreparedFilter Value { get; set; } = PreparedFilter.BLUR; + + public override object GetValue() + { + return Value; + } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/SingleFieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/SingleFieldValueDto.cs new file mode 100644 index 000000000..d5d68f712 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/SingleFieldValueDto.cs @@ -0,0 +1,35 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +[MessagePackObject] +public sealed class SingleFieldValueDto : FieldValueDto +{ + [Key(100)] + public float Value { get; set; } + + public override object GetValue() + { + return Value; + } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/StringAlignmentFieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/StringAlignmentFieldValueDto.cs new file mode 100644 index 000000000..619bf7d4b --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/StringAlignmentFieldValueDto.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +[MessagePackObject] +public sealed class StringAlignmentFieldValueDto : FieldValueDto +{ + [Key(100)] + public StringAlignment Value { get; set; } = StringAlignment.Near; + + public override object GetValue() + { + return Value; + } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/Fields/StringFieldValueDto.cs b/src/Greenshot.Editor/FileFormat/Dto/Fields/StringFieldValueDto.cs new file mode 100644 index 000000000..775b5839b --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/Fields/StringFieldValueDto.cs @@ -0,0 +1,35 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto.Fields; + +[MessagePackObject] +public sealed class StringFieldValueDto : FieldValueDto +{ + [Key(100)] + public string Value { get; set; } = string.Empty; + + public override object GetValue() + { + return Value; + } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/GreenshotFileDto.cs b/src/Greenshot.Editor/FileFormat/Dto/GreenshotFileDto.cs new file mode 100644 index 000000000..2156b4910 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/GreenshotFileDto.cs @@ -0,0 +1,62 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto; + +/// +/// Is Data Transfer Object (DTO) for +/// This represents the main class for a .greenshot file. +/// +[MessagePackObject] +public sealed class GreenshotFileDto +{ + /// + /// + /// + [Key(0)] + public int SchemaVersion { get; set; } = GreenshotFileVersionHandler.CurrentSchemaVersion; + + /// + /// + /// + [Key(1)] + public GreenshotFileVersionHandler.GreenshotFileFormatVersion FormatVersion { get; set; } = GreenshotFileVersionHandler.GreenshotFileFormatVersion.Unknown; + + /// + /// + /// + [Key(11)] + public byte[] Image { get; set; } + + /// + /// + /// + [Key(12)] + public DrawableContainerListDto ContainerList { get; set; } = new(); + + /// + /// + /// + [Key(13)] + public byte[] RenderedImage { get; set; } +} diff --git a/src/Greenshot.Editor/FileFormat/Dto/GreenshotTemplateDto.cs b/src/Greenshot.Editor/FileFormat/Dto/GreenshotTemplateDto.cs new file mode 100644 index 000000000..736d48739 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/Dto/GreenshotTemplateDto.cs @@ -0,0 +1,50 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.Dto; + +/// +/// Is Data Transfer Object (DTO) for +/// This represents the main class for a .gst file. +/// +[MessagePackObject] +public sealed class GreenshotTemplateDto +{ + /// + /// + /// + [Key(0)] + public int SchemaVersion { get; set; } = GreenshotFileVersionHandler.CurrentSchemaVersion; + + /// + /// + /// + [Key(1)] + public GreenshotFileVersionHandler.GreenshotFileFormatVersion FormatVersion { get; set; } = GreenshotFileVersionHandler.GreenshotFileFormatVersion.Unknown; + + /// + /// + /// + [Key(11)] + public DrawableContainerListDto ContainerList { get; set; } = new(); +} diff --git a/src/Greenshot.Editor/FileFormat/GreenshotFile.cs b/src/Greenshot.Editor/FileFormat/GreenshotFile.cs new file mode 100644 index 000000000..820c2b3ee --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/GreenshotFile.cs @@ -0,0 +1,59 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Editor.Drawing; + +namespace Greenshot.Editor.FileFormat; + +/// +/// Represents a .greenshot file as domain object. +/// +public sealed class GreenshotFile +{ + /// + /// List of drawable containers that are positioned on the surface. These are graphical elements like text, shapes, and images that can be drawn on the surface. + /// + public DrawableContainerList ContainerList { get; set; } + + /// + /// The main image of the surface. It is captured from the screen or opend from a file. + /// + public Image Image { get; set; } + + /// + /// The image rendered from the surface, which includes all drawable containers. + /// + public Image RenderedImage { get; set; } + + /// + /// Indicates the version of the file format, which is used to determine the serializer and deserializer for the file. + /// For now this is not really needed within the Dto, because you need to know the serializer before deserialzing the Dto. + /// The format version is part of the complete file version, so we include it here for completeness. + /// May be in the future used to handle backward compatibility issues. + /// + public GreenshotFileVersionHandler.GreenshotFileFormatVersion FormatVersion { get; set; } = GreenshotFileVersionHandler.GreenshotFileFormatVersion.Unknown; + + /// + /// Version of the file schema + /// + public int SchemaVersion { get; set; } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/GreenshotFileVersionHandler.cs b/src/Greenshot.Editor/FileFormat/GreenshotFileVersionHandler.cs new file mode 100644 index 000000000..b47aeec2e --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/GreenshotFileVersionHandler.cs @@ -0,0 +1,221 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.IO; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.V1; +using Greenshot.Editor.FileFormat.V2; +using log4net; + +namespace Greenshot.Editor.FileFormat; + +/// +/// Provides functionality for handling all supported Greenshot file format versions. +/// It also provides methods to create a from a surface and to create a from a Greenshot file. +/// +public sealed class GreenshotFileVersionHandler +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileVersionHandler)); + + /// + /// Represents the file format version for greenshot files. This includes greenshot templates as well. + /// + /// The file versions are now independent of the app version.
+ /// The version numbers of greenshot 1.2 matched old greenshot file version 01.02 and greenshot 1.3 matched old file version 01.03. + /// Now the definition changed a bit and it is composed of two parts. + /// {serializer version}.{schema version} + /// The first part is . This decides wich serializer/ binary data structure is used.
+ /// The second part is . This schema version only needs to be changed if certain actions are necessary for backward compatibility.
+ /// + /// The old versions still fit this pattern. + ///
+ public enum GreenshotFileFormatVersion + { + Unknown = 0, + /// + /// This format uses BinaryFormat serialization, supporting Greenshot file versions 01.02 and 01.03 + /// + V1 = 1, + /// + /// This format uses MessagePack serialization + /// + V2 = 2 + } + + /// + /// Version of the current file schema. More precisely, the version of the Dto structure. This includes greenshot templates as well. + /// + /// + /// Increase this version if you change the Dto structure in a way that breaks backward compatibility. + /// After incrementing this version, you need to extend . + /// + /// ./FileFormat/readme.md for more information about the file format and versioning. + /// + public const int CurrentSchemaVersion = 1; + + /// + /// Loads a from the stream. + /// + /// This method detects the file format version of the Greenshot file and loads the GreenshotFile accordingly. + /// A containing the Greenshot file data. + /// The loaded . + /// Thrown if the stream does not contain a valid Greenshot file. + public static GreenshotFile LoadFromStream(Stream greenshotFileStream) + { + GreenshotFileFormatVersion fileFormatVersion = DetectFileFormatVersion(greenshotFileStream); + + return fileFormatVersion switch + { + GreenshotFileFormatVersion.V1 => GreenshotFileV1.LoadFromStream(greenshotFileStream), + GreenshotFileFormatVersion.V2 => GreenshotFileV2.LoadFromStream(greenshotFileStream), + _ => throw new ArgumentException("Stream is not a Greenshot file!") + }; + + } + + /// + /// + /// + /// + public static bool SaveToStreamInCurrentVersion( ISurface surface, Stream stream) => + SaveToStream(CreateGreenshotFile(surface), stream); + + /// + /// + /// + private static bool SaveToStream(GreenshotFile greenshotFile, Stream stream) => GreenshotFileV2.SaveToStream(greenshotFile, stream); + + /// + /// Detects the Greenshot file version from the provided stream. + /// + /// A containing the Greenshot file data. + /// + /// Thrown if the file format version cannot be determined. + private static GreenshotFileFormatVersion DetectFileFormatVersion(Stream greenshotFileStream) + { + // check for newest version first + + if (GreenshotFileV2.DoesFileFormatMatch(greenshotFileStream)) + { + return GreenshotFileFormatVersion.V2; + } + + if (GreenshotFileV1.DoesFileFormatMatch(greenshotFileStream)) + { + return GreenshotFileFormatVersion.V1; + } + + Log.Error("Stream does not contain a known Greenshot file format!"); + throw new ArgumentException("Stream does not contain a known Greenshot file format!"); + } + + /// + /// Creates a from surface. + /// + /// + /// Cannot be . + /// A with surface's image, rendered image, and elements. + /// Thrown if is or if its is . + public static GreenshotFile CreateGreenshotFile(ISurface surface) + { + if (surface == null) + { + Log.Error("Surface cannot be null"); + throw new ArgumentNullException(nameof(surface), "Surface cannot be null"); + } + + if (surface.Image == null) + { + Log.Error("Surface image cannot be null"); + throw new ArgumentNullException(nameof(surface.Image), "Surface image cannot be null"); + } + + return CreateGreenshotFileInCurrentVersion( + (Bitmap)surface.Image, + (Bitmap)surface.GetImageForExport(), + new DrawableContainerList(surface.Elements)); + } + + /// + /// Creates a new with the specified image and elements and also + /// configured with the current format and schema versions. + /// + /// Background image of the surface. + /// rendered image of the surface, which includes all drawable containers. + /// Elements that are positioned on the surface, like text, shapes, and images. + /// + private static GreenshotFile CreateGreenshotFileInCurrentVersion(Bitmap image, Bitmap renderedImage, DrawableContainerList elements) + { + return new GreenshotFile + { + Image = image, + RenderedImage = renderedImage, + ContainerList = elements, + FormatVersion = GreenshotFileFormatVersion.V2, + SchemaVersion = CurrentSchemaVersion, + }; + } + + /// + /// Creates a new instance and initializes it with the data from the specified . It validates the data and creates always a new surface, or throw an exception. + /// + /// Thrown if is + /// or if its is . + public static ISurface CreateSurface(GreenshotFile greenshotFile) + { + if (greenshotFile == null) + { + Log.Error("Greenshot file cannot be null"); + throw new ArgumentNullException(nameof(greenshotFile), "Greenshot file cannot be null"); + } + + if (greenshotFile.Image == null) + { + // a new surface should always have an image, so this is a critical error + Log.Error("Greenshot file image cannot be null"); + throw new ArgumentNullException(nameof(greenshotFile.Image), "Greenshot file image cannot be null"); + } + + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + + surface.Image = greenshotFile.Image; + + if (greenshotFile.ContainerList != null) + { + // An empty container list is allowed, it's a normal case when no elements are present. + surface.LoadElements(greenshotFile.ContainerList); + } + + return surface; + } + + /// + /// + /// + /// + public static ISurface CreateSurfaceFromStream(Stream stream) => + CreateSurface(LoadFromStream(stream)); +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/GreenshotTemplate.cs b/src/Greenshot.Editor/FileFormat/GreenshotTemplate.cs new file mode 100644 index 000000000..4a1bf174f --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/GreenshotTemplate.cs @@ -0,0 +1,47 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.Drawing; + +namespace Greenshot.Editor.FileFormat; + +/// +/// Represents a .gst file as domain object. +/// +public sealed class GreenshotTemplate +{ + /// + /// List of drawable containers that are positioned on the surface. These are graphical elements like text, shapes, and images that can be drawn on the surface. + /// + public DrawableContainerList ContainerList { get; set; } + + /// + /// Indicates the version of the file format, which is used to determine the serializer and deserializer for the file. + /// For now this is not really needed within the Dto, because you need to know the serializer before deserialzing the Dto. + /// The format version is part of the complete file version, so we include it here for completeness. + /// May be in the future used to handle backward compatibility issues. + /// + public GreenshotFileVersionHandler.GreenshotFileFormatVersion FormatVersion { get; set; } = GreenshotFileVersionHandler.GreenshotFileFormatVersion.Unknown; + + /// + /// Version of the file schema + /// + public int SchemaVersion { get; set; } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/GreenshotTemplateVersionHandler.cs b/src/Greenshot.Editor/FileFormat/GreenshotTemplateVersionHandler.cs new file mode 100644 index 000000000..19c7419a0 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/GreenshotTemplateVersionHandler.cs @@ -0,0 +1,104 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.IO; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.V1; +using Greenshot.Editor.FileFormat.V2; +using log4net; +using static Greenshot.Editor.FileFormat.GreenshotFileVersionHandler; + +namespace Greenshot.Editor.FileFormat; + +/// +/// Provides functionality for handling different Greenshot template file format versions. +/// +public class GreenshotTemplateVersionHandler +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileVersionHandler)); + + /// + /// Loads a from the stream. + /// + /// This method detects the file format version of the Greenshot template and loads the GreenshotTemplate accordingly. + /// A containing the Greenshot template data. + /// The loaded . + /// Thrown if the stream does not contain a valid Greenshot template. + public static GreenshotTemplate LoadFromStream(Stream greenshotTemplateStream) + { + GreenshotFileFormatVersion fileFormatVersion = DetectFileFormatVersion(greenshotTemplateStream); + + return fileFormatVersion switch + { + GreenshotFileFormatVersion.V1 => GreenshotTemplateV1.LoadFromStream(greenshotTemplateStream), + GreenshotFileFormatVersion.V2 => GreenshotTemplateV2.LoadFromStream(greenshotTemplateStream), + _ => throw new ArgumentException("Stream is not a Greenshot template file!") + }; + } + + /// + /// + /// + /// + public static bool SaveToStreamInCurrentVersion(DrawableContainerList elements, Stream stream) => + SaveToStream(CreateGreenshotTemplateInCurrentVersion(elements), stream); + + /// + /// + /// + private static bool SaveToStream(GreenshotTemplate greenshotTemplate, Stream stream) => GreenshotTemplateV2.SaveToStream(greenshotTemplate, stream); + + + private static GreenshotFileFormatVersion DetectFileFormatVersion(Stream drawableContainerListFileStream) + { + // check for newest version first + + if (GreenshotTemplateV2.DoesFileFormatMatch(drawableContainerListFileStream)) + { + return GreenshotFileFormatVersion.V2; + } + + if (GreenshotTemplateV1.DoesFileFormatMatch(drawableContainerListFileStream)) + { + return GreenshotFileFormatVersion.V1; + } + + Log.Error("Stream does not contain a known Greenshot template file format!"); + throw new ArgumentException("Stream does not contain a known Greenshot template file format!"); + } + + /// + /// Creates a new with the specified elements and also + /// configured with the current format and schema versions. + /// + /// + /// + private static GreenshotTemplate CreateGreenshotTemplateInCurrentVersion(DrawableContainerList elements) + { + return new GreenshotTemplate + { + ContainerList = elements, + FormatVersion = GreenshotFileFormatVersion.V2, + SchemaVersion = CurrentSchemaVersion, + }; + } + +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/V1/GreenshotFileV1.cs b/src/Greenshot.Editor/FileFormat/V1/GreenshotFileV1.cs new file mode 100644 index 000000000..c6d1d0f88 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/V1/GreenshotFileV1.cs @@ -0,0 +1,163 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.IO; +using System.ServiceModel.Security; +using System.Text; +using Greenshot.Base.Core; +using Greenshot.Editor.FileFormat.V1.Legacy; +using log4net; + +namespace Greenshot.Editor.FileFormat.V1; + +/// +/// Provides methods for loading Greenshot file format version V1. +/// +/// Greenshot file format version V1 supports Greenshot file versions 01.02 and 01.03. +/// Saving to this format is not supported anymore. +internal static class GreenshotFileV1 +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileV1)); + + /// + /// Determines whether the stream matches the Greenshot file format version V1. + /// + /// It checks for specific markers ("Greenshot01.02" or + /// "Greenshot01.03") and validates the size of surface / container list data. The stream's position will be + /// modified during the operation but will remain open after the method completes. + /// The stream containing the file to check. The stream must support seeking. + /// if the file format matches the Greenshot version V1 and the file contains surface + /// data; otherwise, . + internal static bool DoesFileFormatMatch(Stream greenshotFileStream) + { + // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) + const int markerSize = 14; + greenshotFileStream.Seek(-markerSize, SeekOrigin.End); + // set leaveOpen to prevent the automatic closing of the file stream + using var streamReader = new StreamReader(greenshotFileStream, Encoding.ASCII, false, 1024, true); + var greenshotMarker = streamReader.ReadToEnd(); + + // only these two known marker are allowed + var foundMarkerV0102 = greenshotMarker.Equals("Greenshot01.02"); + var foundMarkerV0103 = greenshotMarker.Equals("Greenshot01.03"); + + // Load 8 byte in front of marker for file size + const int fileSizeLocation = 8 + markerSize; + greenshotFileStream.Seek(-fileSizeLocation, SeekOrigin.End); + // set leaveOpen to prevent the automatic closing of the file stream + using BinaryReader reader = new BinaryReader(greenshotFileStream, Encoding.ASCII, true); + var bytesWritten = reader.ReadInt64(); + + if (foundMarkerV0102 && bytesWritten > 0) + { + Log.InfoFormat("Greenshot file format: {0}", greenshotMarker); + return true; + } + + if (foundMarkerV0103 && bytesWritten > 0) + { + Log.InfoFormat("Greenshot file format: {0}", greenshotMarker); + return true; + } + + return false; + } + + /// + /// This load function support Greenshot file version 01.02 and 01.03. + /// + /// A containing the Greenshot file data. + /// The loaded Greenshot file. + /// If schema version is not found in the stream. + /// If legacy container list cannot be read due to security restrictions. + internal static GreenshotFile LoadFromStream(Stream greenshotFileStream) + { + GreenshotFile returnGreenshotFile = new GreenshotFile + { + FormatVersion = GreenshotFileVersionHandler.GreenshotFileFormatVersion.V1, + }; + + const int markerSize = 14; + greenshotFileStream.Seek(-markerSize, SeekOrigin.End); + // set leaveOpen to prevent the automatic closing of the file stream + using var streamReader = new StreamReader(greenshotFileStream, Encoding.ASCII, false, 1024, true); + var greenshotMarker = streamReader.ReadToEnd(); + + // only these two known marker are allowed + var foundMarkerV0102 = greenshotMarker.Equals("Greenshot01.02"); + var foundMarkerV0103 = greenshotMarker.Equals("Greenshot01.03"); + + if (foundMarkerV0102) + { + returnGreenshotFile.SchemaVersion = 2; + } + else if (foundMarkerV0103) + { + returnGreenshotFile.SchemaVersion = 3; + }else + { + throw new ArgumentException("No schema version found in Greenshot file stream!"); + } + + + // 14 byte for marker and 8 byte for filesize + const int fileSizeLocation = 8 + markerSize; + greenshotFileStream.Seek(-fileSizeLocation, SeekOrigin.End); + // set leaveOpen to prevent the automatic closing of the file stream + using BinaryReader reader = new BinaryReader(greenshotFileStream, Encoding.ASCII, true); + long bytesWritten = reader.ReadInt64(); + greenshotFileStream.Seek(-(bytesWritten + fileSizeLocation), SeekOrigin.End); + + try + { + returnGreenshotFile.ContainerList = LegacyFileHelper.GetContainerListFromLegacyContainerListStream(greenshotFileStream); + } + catch (SecurityAccessDeniedException) + { + throw; + } + catch (Exception e) + { + Log.Error("Error serializing elements from stream.", e); + } + + Bitmap captureBitmap; + + // reset stream to load image + greenshotFileStream.Seek(0, SeekOrigin.Begin); + // Fixed problem that the bitmap stream is disposed... by Cloning the image + // This also ensures the bitmap is correctly created + using (Image tmpImage = Image.FromStream(greenshotFileStream, true, true)) + { + Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); + captureBitmap = ImageHelper.Clone(tmpImage) as Bitmap; + } + + if (captureBitmap != null) + { + returnGreenshotFile.Image = captureBitmap; + Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", captureBitmap.Width, captureBitmap.Height, captureBitmap.PixelFormat, captureBitmap.HorizontalResolution, captureBitmap.VerticalResolution); + } + + return returnGreenshotFile; + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/V1/GreenshotTemplateV1.cs b/src/Greenshot.Editor/FileFormat/V1/GreenshotTemplateV1.cs new file mode 100644 index 000000000..bf0132137 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/V1/GreenshotTemplateV1.cs @@ -0,0 +1,93 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.IO; +using System.ServiceModel.Security; +using Greenshot.Editor.FileFormat.V1.Legacy; +using log4net; + +namespace Greenshot.Editor.FileFormat.V1; + +/// +/// Provides methods for loading Greenshot template in file format version V1. +/// +/// Greenshot template file format version V1 supports Greenshot template files from app version 1.2 and 1.3. +/// Saving to this format is not supported anymore. +internal static class GreenshotTemplateV1 +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotTemplateV1)); + + /// Determines whether the stream matches the Greenshot template format version V1. + /// + /// In this file format is no specific marker or version information, + /// so it is not possible to detect the file format version without deserializing the file. + /// The stream's position will be modified during the operation but will remain open after the method completes. + internal static bool DoesFileFormatMatch(Stream greenshotTemplateFileStream) + { + // This file format has no specific marker, so we just check if we can deserialize it and could find at least one container. + var greenshotTemplate = LoadFromStream(greenshotTemplateFileStream); + if (greenshotTemplate is { ContainerList.Count: > 0 }) + { + Log.Info("Greenshot template file format: V1"); + return true; + } + + return false; + } + + /// + /// This load function support Greenshot template from app version 1.2 and 1.3. + /// + /// In this file format is no specific version information, so schema version is always 3. + /// The stream's position will be modified during the operation but will remain open after the method completes. + /// A containing the Greenshot template data. + /// The loaded Greenshot template. + /// + internal static GreenshotTemplate LoadFromStream(Stream greenshotTemplateFileStream) + { + // reset position + greenshotTemplateFileStream.Seek(0, SeekOrigin.Begin); + + GreenshotTemplate greenshotTemplate = new GreenshotTemplate + { + FormatVersion = GreenshotFileVersionHandler.GreenshotFileFormatVersion.V1, + SchemaVersion = -1 + }; + + try + { + greenshotTemplate.ContainerList = LegacyFileHelper.GetContainerListFromLegacyContainerListStream(greenshotTemplateFileStream); + } + catch (SecurityAccessDeniedException) + { + throw; + } + catch (Exception e) + { + Log.Error("Error serializing elements from stream.", e); + } + + greenshotTemplate.SchemaVersion = 3; // The schema version for V1 is always 3, as this is the last version that was used in the old Greenshot file format. + + return greenshotTemplate; + } + +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/V1/Legacy/ConvertLegacyToDto.cs b/src/Greenshot.Editor/FileFormat/V1/Legacy/ConvertLegacyToDto.cs new file mode 100644 index 000000000..da2839b27 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/V1/Legacy/ConvertLegacyToDto.cs @@ -0,0 +1,303 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.IO; +using System.Linq; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; + +namespace Greenshot.Editor.FileFormat.V1.Legacy; + +/// +/// Provides methods for converting legacy drawable container objects into their corresponding Data Transfer Object +/// (DTO) representations. +/// +/// This class contains a collection of static ToDto() methods that handle the transformation +internal static class ConvertLegacyToDto +{ + public static DrawableContainerDto ToDto(LegacyDrawableContainer container) + { + return container switch + { + LegacyArrowContainer arrow => ToDto(arrow), + LegacyLineContainer line => ToDto(line), + LegacyRectangleContainer rect => ToDto(rect), + LegacyEllipseContainer ellipse => ToDto(ellipse), + LegacyHighlightContainer highlight => ToDto(highlight), + LegacyObfuscateContainer obfuscate => ToDto(obfuscate), + LegacyTextContainer text => ToDto(text), + LegacyImageContainer image => ToDto(image), + LegacyIconContainer icon => ToDto(icon), + LegacySpeechbubbleContainer speech => ToDto(speech), + LegacyFreehandContainer freehand => ToDto(freehand), + LegacyMetafileContainer meta => ToDto(meta), + LegacySvgContainer svg => ToDto(svg), + LegacyStepLabelContainer step => ToDto(step), + _ => throw new NotSupportedException($"Cannot convert unknown container type {container.GetType()} to a concrete ContainerDto."), + }; + } + + public static DrawableContainerListDto ToDto(LegacyDrawableContainerList containerList) + { + var dto = new DrawableContainerListDto { ContainerList = new List() }; + foreach (var w in containerList) + { + dto.ContainerList.Add(ToDto(w)); + } + return dto; + } + + private static List ToFieldDtos(IList fields) + { + var list = new List(); + if (fields == null) return list; + list.AddRange(fields.Select(f => new FieldDto { FieldTypeName = f.FieldType?.Name, Scope = f.Scope, Value = ConvertValueToDto(f.Value) })); + return list; + } + + public static ArrowContainerDto ToDto(LegacyArrowContainer container) + { + return new ArrowContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields) + }; + } + + public static LineContainerDto ToDto(LegacyLineContainer container) + { + return new LineContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields) + }; + } + + public static RectangleContainerDto ToDto(LegacyRectangleContainer container) + { + return new RectangleContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields) + }; + } + + public static EllipseContainerDto ToDto(LegacyEllipseContainer container) + { + return new EllipseContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields) + }; + } + + public static HighlightContainerDto ToDto(LegacyHighlightContainer container) + { + return new HighlightContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields) + }; + } + + public static ObfuscateContainerDto ToDto(LegacyObfuscateContainer container) + { + return new ObfuscateContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields) + }; + } + + public static TextContainerDto ToDto(LegacyTextContainer container) + { + return new TextContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields), + Text = container.Text + }; + } + + public static ImageContainerDto ToDto(LegacyImageContainer container) + { + return new ImageContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields), + Image = container.Image != null ? ImageToByteArray(container.Image) : null + }; + } + + public static IconContainerDto ToDto(LegacyIconContainer container) + { + return new IconContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields), + Icon = container.Icon != null ? IconToByteArray(container.Icon) : null + }; + } + + public static SpeechbubbleContainerDto ToDto(LegacySpeechbubbleContainer container) + { + return new SpeechbubbleContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields), + Text = container.Text, + StoredTargetGripperLocation = new PointDto { X = container.StoredTargetGripperLocation.X, Y = container.StoredTargetGripperLocation.Y } + }; + } + + public static FreehandContainerDto ToDto(LegacyFreehandContainer container) + { + var points = new List(); + if (container.CapturePoints != null) + { + foreach (var p in container.CapturePoints) + { + points.Add(new PointDto { X = p.X, Y = p.Y }); + } + } + return new FreehandContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields), + CapturePoints = points + }; + } + + public static MetafileContainerDto ToDto(LegacyMetafileContainer container) + { + return new MetafileContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields), + RotationAngle = container.RotationAngle, + MetafileData = container.MetafileContent?.ToArray() + }; + } + + public static SvgContainerDto ToDto(LegacySvgContainer container) + { + return new SvgContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields), + RotationAngle = container.RotationAngle, + SvgData = container.SvgContent?.ToArray() + }; + } + + public static StepLabelContainerDto ToDto(LegacyStepLabelContainer container) + { + return new StepLabelContainerDto + { + Left = container.Left, + Top = container.Top, + Width = container.Width, + Height = container.Height, + Fields = ToFieldDtos(container.Fields), + Number = container.Number, + CounterStart = container.CounterStart + }; + } + + private static byte[] ImageToByteArray(Image image) + { + if (image == null) return null; + using var ms = new MemoryStream(); + image.Save(ms, System.Drawing.Imaging.ImageFormat.Png); + return ms.ToArray(); + } + private static byte[] IconToByteArray(Icon icon) + { + if (icon == null) return null; + using var ms = new MemoryStream(); + icon.Save(ms); + return ms.ToArray(); + } + + private static FieldValueDto ConvertValueToDto(object value) + { + if (value == null) return new NullFieldValueDto(); + return value switch + { + int i => new IntFieldValueDto { Value = i }, + string s => new StringFieldValueDto { Value = s }, + bool b => new BoolFieldValueDto { Value = b }, + float f => new SingleFieldValueDto { Value = f }, + double d => new DoubleFieldValueDto { Value = d }, + decimal m => new DecimalFieldValueDto { Value = m }, + Color c => new ColorFieldValueDto { Value = c }, + FilterContainer.PreparedFilter filter => new PreparedFilterFieldValueDto { Value = filter }, + ArrowContainer.ArrowHeadCombination arrowHead => new ArrowHeadCombinationFieldValueDto { Value = arrowHead }, + FieldFlag fieldFlag => new FieldFlagFieldValueDto { Value = fieldFlag }, + StringAlignment alignment => new StringAlignmentFieldValueDto { Value = alignment }, + _ => throw new ArgumentException($"Unsupported type: {value.GetType()}") + }; + } +} + diff --git a/src/Greenshot.Editor/FileFormat/V1/Legacy/LegacyClasses.cs b/src/Greenshot.Editor/FileFormat/V1/Legacy/LegacyClasses.cs new file mode 100644 index 000000000..ea404f2c2 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/V1/Legacy/LegacyClasses.cs @@ -0,0 +1,358 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.Serialization; + +namespace Greenshot.Editor.FileFormat.V1.Legacy; + +/* + * This File includes all the legacy classes that are used to deserialize the old .greenshot files in file format V1. + * Each class is a copy of the old drawable container classes. + * Each class only contains the properties that are needed for deserialization. + * In some classes we have to handle backward compatibility. + */ + +#region Field and FieldHolder + +[Serializable] +internal class LegacyField +{ +#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value. The field will be set during deserialization. + private object _myValue; +#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value. The field will be set during deserialization. + public LegacyFieldType FieldType { get; set; } + public string Scope { get; set; } + + public object Value => _myValue; +} + +[Serializable] +internal class LegacyFieldType +{ + public string Name { get; set; } +} + +[Serializable] +internal class LegacyFieldHolder : ISerializable +{ + public IList Fields; + + protected LegacyFieldHolder(SerializationInfo info, StreamingContext context) + { + Fields = (IList)info.GetValue("AbstractFieldHolder+fields", typeof(IList)); + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + // is only called when the object is serialized + // we never want to serialize this object + throw new NotImplementedException(); + } +} + +[Serializable] +internal class LegacyFieldHolderWithChildren : LegacyFieldHolder +{ + public IList Children; + + protected LegacyFieldHolderWithChildren(SerializationInfo info, StreamingContext context) : base(info, context) + { + Children = (IList)info.GetValue("Children", typeof(IList)); + } +} +#endregion + +#region DrawableContainer + +[Serializable] +internal class LegacyDrawableContainer : LegacyFieldHolderWithChildren +{ + public int Left; + public int Top; + public int Width; + public int Height; + + protected LegacyDrawableContainer(SerializationInfo info, StreamingContext context) : base(info, context) + { + Left = info.GetInt32("DrawableContainer+left"); + Top = info.GetInt32("DrawableContainer+top"); + Width = info.GetInt32("DrawableContainer+width"); + Height = info.GetInt32("DrawableContainer+height"); + } +} + +[Serializable] +internal class LegacyDrawableContainerList : List +{ +} + +[Serializable] +internal class LegacyLineContainer : LegacyDrawableContainer +{ + protected LegacyLineContainer(SerializationInfo info, StreamingContext context) : base(info, context) { } +} + +[Serializable] +internal class LegacyArrowContainer : LegacyDrawableContainer +{ + protected LegacyArrowContainer(SerializationInfo info, StreamingContext context) : base(info, context) { } +} + +[Serializable] +internal class LegacyRectangleContainer : LegacyDrawableContainer +{ + protected LegacyRectangleContainer(SerializationInfo info, StreamingContext context) : base(info, context) { } +} + +[Serializable] +internal class LegacyEllipseContainer : LegacyDrawableContainer +{ + protected LegacyEllipseContainer(SerializationInfo info, StreamingContext context) : base(info, context) { } +} + +[Serializable] +internal class LegacyHighlightContainer : LegacyDrawableContainer +{ + protected LegacyHighlightContainer(SerializationInfo info, StreamingContext context) : base(info, context) { } +} + +[Serializable] +internal class LegacyObfuscateContainer : LegacyDrawableContainer +{ + protected LegacyObfuscateContainer(SerializationInfo info, StreamingContext context) : base(info, context) { } +} + +[Serializable] +internal class LegacyTextContainer : LegacyDrawableContainer +{ + public string Text; + + protected LegacyTextContainer(SerializationInfo info, StreamingContext context) : base(info, context) + { + Text = info.GetString("text"); + } +} + +[Serializable] +internal class LegacyImageContainer : LegacyDrawableContainer +{ + public Image Image; + + protected LegacyImageContainer(SerializationInfo info, StreamingContext context) : base(info, context) + { + try + { + Image = (Image)info.GetValue("_image", typeof(Image)); + } + catch (SerializationException) + { + // backword compatibility + // try to serialize file version 01.02 + Image = (Image)info.GetValue("image", typeof(Image)); + } + + } +} + +[Serializable] +internal class LegacyIconContainer : LegacyDrawableContainer +{ + public Icon Icon; + + protected LegacyIconContainer(SerializationInfo info, StreamingContext context) : base(info, context) + { + Icon = (Icon)info.GetValue("icon", typeof(Icon)); + } +} + +[Serializable] +internal class LegacySpeechbubbleContainer : LegacyDrawableContainer +{ + public string Text; + public Point StoredTargetGripperLocation; + + protected LegacySpeechbubbleContainer(SerializationInfo info, StreamingContext context) : base(info, context) + { + Text = info.GetString("TextContainer+text"); + StoredTargetGripperLocation = (Point)info.GetValue("_storedTargetGripperLocation", typeof(Point)); + } +} + +[Serializable] +internal class LegacyFreehandContainer : LegacyDrawableContainer +{ + public List CapturePoints; + + protected LegacyFreehandContainer(SerializationInfo info, StreamingContext context) : base(info, context) + { + CapturePoints = (List)info.GetValue("capturePoints", typeof(List)); + } +} + +[Serializable] +internal class LegacyMetafileContainer : LegacyDrawableContainer +{ + public int RotationAngle; + + public Metafile Metafile; + + public MemoryStream MetafileContent; + + protected LegacyMetafileContainer(SerializationInfo info, StreamingContext context) : base(info, context) + { + RotationAngle = info.GetInt32("VectorGraphicsContainer+_rotationAngle"); + + Metafile = (Metafile)info.GetValue("_metafile", typeof(Metafile)); + + if (Metafile != null) + { + ConvertWmfToPngAndReplaceMetafileAndStream(ref Metafile, ref MetafileContent); + } + } + + /// + /// Workaround for serialization for file version > 1.03. + /// For old files we need to recreate MetafileContent. We have to convert it to png, because there is no encoder for .wmf or .emf files in GDI+. + /// + /// + private void ConvertWmfToPngAndReplaceMetafileAndStream( + ref Metafile metafile, + ref MemoryStream pngStream) + { + if (metafile == null) + throw new ArgumentNullException(nameof(metafile)); + + int width = metafile.Width; + int height = metafile.Height; + + // Render original metafile to bitmap + using Bitmap bitmap = new Bitmap(width, height); + using (Graphics g = Graphics.FromImage(bitmap)) + { + g.SmoothingMode = SmoothingMode.AntiAlias; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; + g.Clear(Color.Transparent); + + g.DrawImage(metafile, 0, 0, width, height); + } + + // Dispose old MemoryStream if exists + pngStream?.Dispose(); + + // Save bitmap as PNG into new MemoryStream + pngStream = new MemoryStream(); + bitmap.Save(pngStream, ImageFormat.Png); + pngStream.Position = 0; + + // Create new Metafile + using Bitmap tempBitmap = new Bitmap(1, 1); + using Graphics referenceGraphics = Graphics.FromImage(tempBitmap); + IntPtr hdc = referenceGraphics.GetHdc(); + + Metafile newMetafile = new Metafile(hdc, EmfType.EmfOnly); + + referenceGraphics.ReleaseHdc(hdc); + + using (Graphics gMetafile = Graphics.FromImage(newMetafile)) + { + gMetafile.DrawImage(bitmap, 0, 0, width, height); + } + + // Dispose old metafile and replace + metafile?.Dispose(); + metafile = newMetafile; + } +} + +[Serializable] +internal class LegacySvgContainer : LegacyDrawableContainer +{ + public int RotationAngle; + public MemoryStream SvgContent; + + protected LegacySvgContainer(SerializationInfo info, StreamingContext context) : base(info, context) + { + RotationAngle = info.GetInt32("VectorGraphicsContainer+_rotationAngle"); + SvgContent = (MemoryStream)info.GetValue("_svgContent", typeof(MemoryStream)); + } +} + +[Serializable] +internal class LegacyStepLabelContainer : LegacyDrawableContainer +{ + public int Number; + public int CounterStart; + + protected LegacyStepLabelContainer(SerializationInfo info, StreamingContext context) : base(info, context) + { + Number = info.GetInt32("_number"); + CounterStart = info.GetInt32("_counterStart"); + } +} + +#endregion + +#region FilterContainer + +[Serializable] +internal class LegacyHighlightFilter : LegacyFieldHolder +{ + protected LegacyHighlightFilter(SerializationInfo info, StreamingContext context) : base(info, context) { } +} + +[Serializable] +internal class LegacyBlurFilter : LegacyFieldHolder +{ + protected LegacyBlurFilter(SerializationInfo info, StreamingContext context) : base(info, context) { } +} + +[Serializable] +internal class LegacyBrightnessFilter : LegacyFieldHolder +{ + protected LegacyBrightnessFilter(SerializationInfo info, StreamingContext context) : base(info, context) { } +} + +[Serializable] +internal class LegacyGrayscaleFilter : LegacyFieldHolder +{ + protected LegacyGrayscaleFilter(SerializationInfo info, StreamingContext context) : base(info, context) { } +} + +[Serializable] +internal class LegacyMagnifierFilter : LegacyFieldHolder +{ + protected LegacyMagnifierFilter(SerializationInfo info, StreamingContext context) : base(info, context) { } +} + +[Serializable] +internal class LegacyPixelizationFilter : LegacyFieldHolder +{ + protected LegacyPixelizationFilter(SerializationInfo info, StreamingContext context) : base(info, context) { } +} +#endregion + + diff --git a/src/Greenshot.Editor/FileFormat/V1/Legacy/LegacyFileHelper.cs b/src/Greenshot.Editor/FileFormat/V1/Legacy/LegacyFileHelper.cs new file mode 100644 index 000000000..031eca510 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/V1/Legacy/LegacyFileHelper.cs @@ -0,0 +1,88 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.IO; +using System.Runtime.Serialization.Formatters.Binary; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; + +namespace Greenshot.Editor.FileFormat.V1.Legacy; + +public static class LegacyFileHelper +{ + /// + /// Deserializes a legacy Greenshot file stream and converts it into a object. + /// + /// This method processes a legacy Greenshot file by deserializing its contents to , + /// convert this to and then to . The input stream must contain data in the expected legacy format. + /// The input stream containing the serialized legacy Greenshot file data. Must not be . + /// A representing the deserialized and converted data from the legacy file. + /// Thrown if the stream does not contain a valid legacy Greenshot file. + public static DrawableContainerList GetContainerListFromLegacyContainerListStream(Stream stream) + { + // load file in legacy container classes + BinaryFormatter binaryRead = new BinaryFormatter + { + Binder = new LegacySerializationBinder() + }; + var loadedElements = binaryRead.Deserialize(stream); + + if (loadedElements is not LegacyDrawableContainerList legacyDrawableContainerList) + { + throw new ArgumentException("Stream is not a Greenshot file!"); + } + + // Convert the legacy data to DTO + var dto = ConvertLegacyToDto.ToDto(legacyDrawableContainerList); + + // and then to domain object + var containerList = ConvertDtoToDomain.ToDomain(dto); + + return containerList; + } + + /// + /// This is more or less a helper method for develop. It a shorter way to load a ContainerList from a legacy Greenshot file instead of using the . + /// It extracts the container list sub stream from the Greenshot file stream and call . + /// + public static DrawableContainerList GetContainerListFromGreenshotfile(Stream stream) + { + // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) + const int markerSize = 14; + stream.Seek(-markerSize, SeekOrigin.End); + using StreamReader streamReader = new StreamReader(stream); + + var greenshotMarker = streamReader.ReadToEnd(); + if (!greenshotMarker.StartsWith("Greenshot")) + { + throw new ArgumentException("Stream is not a Greenshot file!"); + } + + const int filesizeLocation = 8 + markerSize; + stream.Seek(-filesizeLocation, SeekOrigin.End); + using BinaryReader reader = new BinaryReader(stream); + long bytesWritten = reader.ReadInt64(); + stream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End); + + return GetContainerListFromLegacyContainerListStream(stream); + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/V1/Legacy/LegacySerializationBinder.cs b/src/Greenshot.Editor/FileFormat/V1/Legacy/LegacySerializationBinder.cs new file mode 100644 index 000000000..c7300f962 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/V1/Legacy/LegacySerializationBinder.cs @@ -0,0 +1,123 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Runtime.Serialization; +using System.ServiceModel.Security; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; +using log4net; + +namespace Greenshot.Editor.FileFormat.V1.Legacy; + +/// +/// This helps to map the serialization of the old .greenshot file to the legacy container. +/// It also prevents misuse like ysoserial attacks, by throwing an exception if a type is not mapped. +/// +internal sealed class LegacySerializationBinder : SerializationBinder +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(LegacySerializationBinder)); + private static readonly IDictionary TypeMapper = new Dictionary + { + // system types + {"System.Guid",typeof(Guid) }, + {"System.Drawing.Rectangle",typeof(System.Drawing.Rectangle) }, + {"System.Drawing.Point",typeof(System.Drawing.Point) }, + {"System.Drawing.Color",typeof(System.Drawing.Color) }, + {"System.Drawing.Bitmap",typeof(System.Drawing.Bitmap) }, + {"System.Drawing.Imaging.Metafile",typeof(System.Drawing.Imaging.Metafile) }, + {"System.Drawing.Icon",typeof(System.Drawing.Icon) }, + {"System.Drawing.Size",typeof(System.Drawing.Size) }, + {"System.IO.MemoryStream",typeof(System.IO.MemoryStream) }, + {"System.Drawing.StringAlignment",typeof(System.Drawing.StringAlignment) }, + {"System.Collections.Generic.List`1[[System.Drawing.Point", typeof(List)}, + + // legacy types + {"System.Collections.Generic.List`1[[Greenshot.Base.Interfaces.Drawing.IFieldHolder", typeof(List)}, + {"System.Collections.Generic.List`1[[Greenshot.Base.Interfaces.Drawing.IField", typeof(List)}, + {"Greenshot.Editor.Drawing.ArrowContainer", typeof(LegacyArrowContainer) }, + {"Greenshot.Editor.Drawing.LineContainer", typeof(LegacyLineContainer) }, + {"Greenshot.Editor.Drawing.TextContainer", typeof(LegacyTextContainer) }, + {"Greenshot.Editor.Drawing.SpeechbubbleContainer", typeof(LegacySpeechbubbleContainer) }, + {"Greenshot.Editor.Drawing.RectangleContainer", typeof(LegacyRectangleContainer) }, + {"Greenshot.Editor.Drawing.EllipseContainer", typeof(LegacyEllipseContainer) }, + {"Greenshot.Editor.Drawing.FreehandContainer", typeof(LegacyFreehandContainer) }, + {"Greenshot.Editor.Drawing.HighlightContainer", typeof(LegacyHighlightContainer) }, + {"Greenshot.Editor.Drawing.IconContainer", typeof(LegacyIconContainer) }, + {"Greenshot.Editor.Drawing.ObfuscateContainer", typeof(LegacyObfuscateContainer) }, + {"Greenshot.Editor.Drawing.StepLabelContainer", typeof(LegacyStepLabelContainer) }, + {"Greenshot.Editor.Drawing.SvgContainer", typeof(LegacySvgContainer) }, + {"Greenshot.Editor.Drawing.MetafileContainer", typeof(LegacyMetafileContainer) }, + {"Greenshot.Editor.Drawing.ImageContainer", typeof(LegacyImageContainer) }, + {"Greenshot.Editor.Drawing.DrawableContainer", typeof(LegacyDrawableContainer) }, + {"Greenshot.Editor.Drawing.DrawableContainerList", typeof(LegacyDrawableContainerList) }, + {"Greenshot.Editor.Drawing.Filters.HighlightFilter", typeof(LegacyHighlightFilter) }, + {"Greenshot.Editor.Drawing.Filters.GrayscaleFilter", typeof(LegacyGrayscaleFilter) }, + {"Greenshot.Editor.Drawing.Filters.MagnifierFilter", typeof(LegacyMagnifierFilter) }, + {"Greenshot.Editor.Drawing.Filters.BrightnessFilter", typeof(LegacyBrightnessFilter) }, + {"Greenshot.Editor.Drawing.Filters.BlurFilter", typeof(LegacyBlurFilter) }, + {"Greenshot.Editor.Drawing.Filters.PixelizationFilter", typeof(LegacyPixelizationFilter) }, + {"Greenshot.Base.Interfaces.Drawing.IDrawableContainer", typeof(LegacyDrawableContainer) }, + {"Greenshot.Base.Interfaces.Drawing.IFieldHolder", typeof(LegacyFieldHolder) }, + {"Greenshot.Base.Interfaces.Drawing.IField", typeof(LegacyField) }, + {"Greenshot.Editor.Drawing.Fields.Field", typeof(LegacyField) }, + {"Greenshot.Editor.Drawing.Fields.FieldType", typeof(LegacyFieldType) }, + {"Greenshot.Editor.Drawing.Fields.AbstractFieldHolder", typeof(LegacyFieldHolder) }, + + // oiginal types, no wapper needed + {"Greenshot.Base.Interfaces.Drawing.FieldFlag", typeof(FieldFlag) }, + {"Greenshot.Base.Interfaces.Drawing.EditStatus", typeof(EditStatus) }, + {"Greenshot.Editor.Drawing.FilterContainer+PreparedFilter", typeof(FilterContainer.PreparedFilter) }, + {"Greenshot.Editor.Drawing.ArrowContainer+ArrowHeadCombination", typeof(ArrowContainer.ArrowHeadCombination) }, + }; + + /// + /// Do the type mapping + /// + /// Assembly for the type that was serialized + /// Type that was serialized + /// Type which was mapped + /// If something smells fishy + public override Type BindToType(string assemblyName, string typeName) + { + if (string.IsNullOrEmpty(typeName)) + { + return null; + } + var typeNameCommaLocation = typeName.IndexOf(","); + var comparingTypeName = typeName.Substring(0, typeNameCommaLocation > 0 ? typeNameCommaLocation : typeName.Length); + + // Correct wrong types + comparingTypeName = comparingTypeName.Replace("Greenshot.Drawing", "Greenshot.Editor.Drawing"); + comparingTypeName = comparingTypeName.Replace("Greenshot.Plugin.Drawing", "Greenshot.Base.Interfaces.Drawing"); + comparingTypeName = comparingTypeName.Replace("GreenshotPlugin.Interfaces.Drawing", "Greenshot.Base.Interfaces.Drawing"); + comparingTypeName = comparingTypeName.Replace("Greenshot.Drawing.Fields", "Greenshot.Editor.Drawing.Fields"); + comparingTypeName = comparingTypeName.Replace("Greenshot.Drawing.Filters", "Greenshot.Editor.Drawing.Filters"); + + if (TypeMapper.TryGetValue(comparingTypeName, out var returnType)) + { + Log.Info($"Mapped {assemblyName} - {typeName} to {returnType.FullName}"); + return returnType; + } + Log.Warn($"Unexpected Greenshot type in .greenshot file detected, maybe vulnerability attack created with ysoserial? Suspicious type: {assemblyName} - {typeName}"); + throw new SecurityAccessDeniedException($"Suspicious type in .greenshot file: {assemblyName} - {typeName}"); + } +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/V2/GreenshotFileV2.cs b/src/Greenshot.Editor/FileFormat/V2/GreenshotFileV2.cs new file mode 100644 index 000000000..43e157f6d --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/V2/GreenshotFileV2.cs @@ -0,0 +1,266 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing.Imaging; +using System.IO; +using System.Text; +using Greenshot.Editor.FileFormat.Dto; +using log4net; +using MessagePack; + +namespace Greenshot.Editor.FileFormat.V2; + +/// +/// Provides methods for loading and saving Greenshot file format version V2. +/// +internal static class GreenshotFileV2 +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileV2)); + + /// + /// Represents the marker string used to identify the file format as specific to Greenshot file. + /// + private const string FileFormatMarker = "Greenshot"; + + /// + /// Represents the file format version used for Greenshot files, formatted as a two-digit string. + /// + /// Thie ist fixed to ("02") . + private static readonly string FileFormatVersion = ((int)GreenshotFileVersionHandler.GreenshotFileFormatVersion.V2).ToString("D2"); + + /// + /// Represents the current schema version of the Greenshot file in a two-digit string format. + /// + /// It is derived from the property. + private static readonly string SchemaVersion = GreenshotFileVersionHandler.CurrentSchemaVersion.ToString("D2"); + + /// + /// Represents the complete version string, combining the file format version and schema version. + /// + /// This value is a concatenation of and , separated by a period ("."), e.g. "02.01" for version 2.1. + private static readonly string CompleteVersion = FileFormatVersion + Dot + SchemaVersion; + + private const string Dot = "."; + + /// + /// Determines whether the stream matches the Greenshot file format version V2. + /// + /// It checks for specific markers + at the end of the file and validates the size of surface / container list data. + /// The stream's position will be modified during the operation but will remain open after the method completes. + /// The stream containing the file to check. The stream must support seeking. + /// if the file format matches the Greenshot version V2; otherwise, . + internal static bool DoesFileFormatMatch(Stream greenshotFileStream) + { + // file should end with the marker text and format version. + var expectedfileFormatMarker = FileFormatMarker + FileFormatVersion; + + // amount of bytes to ignore at the end of the file + var schemaVersionSuffixLength = Dot.Length + SchemaVersion.Length; + + var markerPosition = expectedfileFormatMarker.Length + schemaVersionSuffixLength; + greenshotFileStream.Seek(-markerPosition, SeekOrigin.End); + + // set leaveOpen to prevent the automatic closing of the file stream + using var streamReader = new StreamReader(greenshotFileStream, Encoding.ASCII, false, 1024, true); + var greenshotMarker = streamReader.ReadToEnd(); + + // Schema version is not relevant for the file format check. + greenshotMarker = greenshotMarker.Substring(0, expectedfileFormatMarker.Length); + + // only these two known marker are allowed + var foundMarkerV02 = expectedfileFormatMarker.Equals(greenshotMarker); + + // Load 8 byte in front of marker for file size + var fileSizeLocation = 8 + markerPosition; + greenshotFileStream.Seek(-fileSizeLocation, SeekOrigin.End); + + // set leaveOpen to prevent the automatic closing of the file stream + using var reader = new BinaryReader(greenshotFileStream, Encoding.ASCII, true); + var bytesWritten = reader.ReadInt64(); + + if (foundMarkerV02 && bytesWritten > 0) + { + Log.InfoFormat("Greenshot file format: {0}", greenshotMarker); + return true; + } + + return false; + + } + + /// + /// Saves the specified to the provided stream in the Greenshot file format version V2. + /// + /// This ignores the file format version and schema version in the GreenshotFile instance. + /// File format version is always V2, schema version is always the current schema version.
+ /// The file parts are:
+ /// 1. The PNG image data
+ /// 2. The binary data of the . (serialized with MessagePack).
+ /// 3. 8 bytes for the binary data size(Int64).
+ /// 4. 11 bytes for the Greenshot marker string + (i.e. `"Greenshot02"`).
+ /// 5. 3 bytes for the Greenshot file schema version (i.e. `".01"`).
+ ///
+ internal static bool SaveToStream(GreenshotFile greenshotFile, Stream stream) + { + // 1. file part - PNG + // Use the rendered image for the PNG part if available, otherwise use the surface image. + var mainImage = greenshotFile.RenderedImage ?? greenshotFile.Image; + if (mainImage == null) + { + Log.Error("Cannot save Greenshot file, no image found!"); + return false; + } + mainImage.Save(stream, ImageFormat.Png); + + greenshotFile.RenderedImage = null; // clear the rendered image it is not nessary to serialize it + + // 2. file part - Greenshot file data + byte[] appFileBytes = Serialize(greenshotFile); + stream.Write(appFileBytes, 0, appFileBytes.Length); + + // 3. file part - Greenshot file data length + var lengthBytes = BitConverter.GetBytes((long)appFileBytes.Length); + stream.Write(lengthBytes, 0, lengthBytes.Length); + + // 4./5. file part - Greenshot file Marker and version information + // writes constant marker and complete version information to the stream + var headerBytes = Encoding.ASCII.GetBytes(FileFormatMarker); + stream.Write(headerBytes, 0, headerBytes.Length); + + var versionBytes = Encoding.ASCII.GetBytes(CompleteVersion); + stream.Write(versionBytes, 0, versionBytes.Length); + + return true; + } + + /// + /// This load function supports file version 02 + /// + /// A containing the Greenshot file data. + /// The loaded Greenshot file. + internal static GreenshotFile LoadFromStream(Stream greenshotFileStream) + { + GreenshotFile returnGreenshotFile; + + try + { + // constant length of file parts at the end of the file + var greenshotMarkerAndVersionLength = FileFormatMarker.Length + CompleteVersion.Length; + var lengthOfTypeLong = 8; + + var fileSizeLocation = lengthOfTypeLong + greenshotMarkerAndVersionLength; + greenshotFileStream.Seek(-fileSizeLocation, SeekOrigin.End); + + // set leaveOpen to prevent the automatic closing of the file stream + using var reader = new BinaryReader(greenshotFileStream, Encoding.ASCII, true); + var bytesWritten = reader.ReadInt64(); + + // go to start of the greenshot file data + var dataStartPosition = fileSizeLocation + bytesWritten; + greenshotFileStream.Seek(-dataStartPosition, SeekOrigin.End); + + // extract the Greenshot file data + var allBytes = new byte[bytesWritten]; + var bytesRead = greenshotFileStream.Read(allBytes, 0, (int)bytesWritten); + + // double check if we read all bytes + if (bytesRead < bytesWritten) + { + throw new EndOfStreamException("Unexpected end of the Greenshot file data!"); + } + + returnGreenshotFile = Deserialize(allBytes); + } + catch (Exception e) + { + Log.Error("Error deserializing Greenshot file from stream.", e); + throw; + } + + return returnGreenshotFile; + } + + /// + /// Serializes the specified instance into a byte array by using MessagePackSerializer. + /// + /// The instance to serialize. Cannot be . + /// A byte array representing the serialized form of the instance. + private static byte[] Serialize(GreenshotFile data) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data), "Cannot serialize a null GreenshotFile."); + } + + var dto = ConvertDomainToDto.ToDto(data); + return MessagePackSerializer.Serialize(dto); + } + + /// + /// Deserializes a byte array into an object by using MessagePackSerializer. + /// + /// This also migrates the loaded to the current version if necessary. + /// + /// The byte array containing the serialized data of an . + /// An object deserialized from the provided byte array. + private static GreenshotFile Deserialize(byte[] bytes) + { + var dto = MessagePackSerializer.Deserialize(bytes); + var currentVersionDto = MigrateToCurrentVersion(dto); + return ConvertDtoToDomain.ToDomain(currentVersionDto); + } + + /// + /// Main method for migrating an to the current version. + /// + /// Does nothing if the version is already current. + /// + private static GreenshotFileDto MigrateToCurrentVersion(GreenshotFileDto dto) + { + switch (dto.SchemaVersion) + { + case GreenshotFileVersionHandler.CurrentSchemaVersion: + return dto; // is already at the current version + case > GreenshotFileVersionHandler.CurrentSchemaVersion: + Log.Warn($"Greenshot file schema version {dto.SchemaVersion} is newer than the current version {GreenshotFileVersionHandler.CurrentSchemaVersion}. No migration will be performed."); + return dto; // no migration possible, just return the dto as is + //case 1: + // Uncomment the next line if the first migration is needed + // return MigrateFromV1ToV2(dto); + default: + return dto; // no migration needed, just return the dto as is + } + } + + /* + // uncomment and implement if the first migration is needed + + private GreenshotFileDto MigrateFromV1ToV2(GreenshotFileDto dto) + { + // Chenge properties as needed for migration + + dto.SchemaVersion = 2; + return dto; + } + */ + +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/V2/GreenshotTemplateV2.cs b/src/Greenshot.Editor/FileFormat/V2/GreenshotTemplateV2.cs new file mode 100644 index 000000000..f07443c59 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/V2/GreenshotTemplateV2.cs @@ -0,0 +1,212 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.IO; +using System.Text; +using Greenshot.Editor.FileFormat.Dto; +using log4net; +using MessagePack; +using static Greenshot.Editor.FileFormat.GreenshotFileVersionHandler; + +namespace Greenshot.Editor.FileFormat.V2; + +/// +/// Provides methods for loading and saving Greenshot template file format version V2. +/// +internal static class GreenshotTemplateV2 +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotTemplateV2)); + + /// + /// Represents the marker string used to identify the file format as specific to Greenshot templates. + /// + private const string FileFormatMarker = "GreenshotTemplate"; + + /// + /// Represents the file format version used for Greenshot files, formatted as a two-digit string. + /// + /// Thie ist fixed to ("02") . + private static readonly string FileFormatVersion = ((int)GreenshotFileFormatVersion.V2).ToString("D2"); + + /// + /// Represents the current schema version of the Greenshot file in a two-digit string format. + /// + /// It is derived from the property. + private static readonly string SchemaVersion = CurrentSchemaVersion.ToString("D2"); + + /// + /// Represents the complete version string, combining the file format version and schema version. + /// + /// This value is a concatenation of and , separated by a period ("."), e.g. "02.01" for version 2.1. + private static readonly string CompleteVersion = FileFormatVersion + "." + SchemaVersion; + + /// + /// Determines whether the stream matches the Greenshot file format version V2. + /// + /// The stream's position will be modified during the operation but will remain open after the method completes. + /// The stream containing the file to check. The stream must support seeking. + /// if the file format matches the Greenshot version V2; otherwise, . + internal static bool DoesFileFormatMatch(Stream greenshotTemplateFileStream) + { + //reset position + greenshotTemplateFileStream.Seek(0, SeekOrigin.Begin); + + // set leaveOpen to prevent the automatic closing of the file stream + using var streamReader = new StreamReader(greenshotTemplateFileStream, Encoding.ASCII, false, 1024, true); + + // file should start with the marker text and format version. Schema version is not relevant for the file format check. + string expectedfileFormatMarker = FileFormatMarker + FileFormatVersion; + + var markerInFile = new char[expectedfileFormatMarker.Length]; + streamReader.Read(markerInFile, 0, expectedfileFormatMarker.Length); + var markerText = new string(markerInFile); + + var foundMarkerV02 = expectedfileFormatMarker.Equals(markerText); + + if (foundMarkerV02) + { + Log.InfoFormat("Greenshot file format: {0}", markerText); + return true; + } + + return false; + } + + /// + /// This load function supports file format version V2. + /// + /// A containing the Greenshot template data. + /// The loaded Greenshot template. + internal static GreenshotTemplate LoadFromStream(Stream greenshotTemplateFileStream) + { + GreenshotTemplate returnGreenshotTemplate; + try + { + // ignore marker and use offset + var completeMarker = FileFormatMarker + CompleteVersion; + + greenshotTemplateFileStream.Seek(completeMarker.Length, SeekOrigin.Begin); + using var ms = new MemoryStream(); + greenshotTemplateFileStream.CopyTo(ms); + var allBytes = ms.ToArray(); + + returnGreenshotTemplate = Deserialize(allBytes); + } + catch (Exception e) + { + Log.Error("Error deserializing Greenshot template from stream.", e); + throw; + } + + return returnGreenshotTemplate; + } + + /// + /// Saves the to the provided stream in the Greenshot template format. + /// + /// This ignores the file format version and schema version in the GreenshotTemplate instance. + /// File format version is always V2, schema version is always the current schema version.
+ /// The file parts are:
+ /// 1. 19 bytes for the Greenshot marker string + (i.e. `"GreenshotTemplate02"`).
+ /// 2. 3 bytes for the Greenshot file schema version (i.e. `".01"`).
+ /// 3. The binary data of the . (serialized with MessagePack).
+ ///
+ internal static bool SaveToStream(GreenshotTemplate greenshotTemplate, Stream stream) + { + // 1./2. file part - Greenshot template Marker and version information + // writes constant marker and complete version information to the stream + var headerBytes = Encoding.ASCII.GetBytes(FileFormatMarker); + stream.Write(headerBytes, 0, headerBytes.Length); + + var versionBytes = Encoding.ASCII.GetBytes(CompleteVersion); + stream.Write(versionBytes, 0, versionBytes.Length); + + //3. file part - Greenshot template data + // writes the serialized GreenshotTemplate to the stream + byte[] templateFileBytes = Serialize(greenshotTemplate); + stream.Write(templateFileBytes, 0, templateFileBytes.Length); + + return true; + } + + /// + /// Serializes the specified instance into a byte array by using MessagePackSerializer. + /// + /// The instance to serialize. Cannot be . + /// A byte array representing the serialized form of the instance. + private static byte[] Serialize(GreenshotTemplate data) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data), "Cannot serialize a null GreenshotTemplate."); + } + + var dto = ConvertDomainToDto.ToDto(data); + return MessagePackSerializer.Serialize(dto); + } + + /// + /// Deserializes a byte array into an object by using MessagePackSerializer. + /// + /// The byte array containing the serialized data of an . + /// An object deserialized from the provided byte array. + private static GreenshotTemplate Deserialize(byte[] bytes) + { + var dto = MessagePackSerializer.Deserialize(bytes); + var currentVersionDto = MigrateToCurrentVersion(dto); + return ConvertDtoToDomain.ToDomain(currentVersionDto); + } + + /// + /// Main method for migrating an to the current version. + /// + /// Does nothing if the version is already current. + /// + private static GreenshotTemplateDto MigrateToCurrentVersion(GreenshotTemplateDto dto) + { + switch (dto.SchemaVersion) + { + case CurrentSchemaVersion: + return dto; // is already at the current version + case > CurrentSchemaVersion: + Log.Warn($"Greenshot template schema version {dto.SchemaVersion} is newer than the current version {CurrentSchemaVersion}. No migration will be performed."); + return dto; // no migration possible, just return the dto as is + //case 1: + // Uncomment the next line if the first migration is needed + // return MigrateFromV1ToV2(dto); + default: + return dto; // no migration needed, just return the dto as is + } + } + + /* + // uncomment and implement if the first migration is needed + + private GreenshotTemplateDto MigrateFromV1ToV2(GreenshotTemplateDto dto) + { + // Chenge properties as needed for migration + + dto.SchemaVersion = 2; + return dto; + } + */ +} \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormat/readme.md b/src/Greenshot.Editor/FileFormat/readme.md new file mode 100644 index 000000000..226f3e769 --- /dev/null +++ b/src/Greenshot.Editor/FileFormat/readme.md @@ -0,0 +1,216 @@ +# Greenshot File Format + +This document describes the file formats used by Greenshot Editor for saving editor files and templates. +- Greenshot editor files contains an image and graphical elements (such as text, shapes, and highlights) layered on top of the image. +- Greenshot templates files contains only graphical elements. + +--- + +# Greenshot Editor File + +- Greenshot editor files are saved with the `.greenshot` file extension. +- Class `GreenshotFile` represents a .greenshot file as domain object. +- It contains the PNG image data that came from `Surface` (the editor canvas). More precisely from `Surface.Image`. +- It also contains a `DrawableContainerList` that came from `Surface.Elements`. A `DrawableContainer` object represents a graphical element (such as text, shapes, and highlights) that are placed on the editor canvas. +- For serialization/deserialization, the `GreenshotFile` is converted to a Data Transfer Object (DTO) `GreenshotFileDto`, which contains the data to be serialized. + +## Supported File Formats + +Greenshot editor files supports two file formats: +- Legacy file format (V1) +- Current file format (V2) + +### 1. Legacy File Format (V1) + +- **Serialization:** Uses .NET BinaryFormatter. +- **Markers:** Files are identified by the marker strings `"Greenshot01.02"` or `"Greenshot01.03"` at the end of the file. +- **Security:** BinaryFormatter is deprecated and considered insecure. Support for this format will be removed in the future. +- **Implementation:** See `Greenshot.Editor.FileFormat.V1.GreenshotFileV1`. +- **Implementation Detail:** + The binary data for the drawable containers is not deserialized directly into a `DrawableContainerList`. Instead, there is an intermediate layer of legacy classes. The deserialization steps are as follows: First, the data is deserialized into a `LegacyDrawableContainerList`, then it is converted to a `DrawableContainerListDto`, and finally, it is converted to a `DrawableContainerList`. + Because the legacy classes are used during deserialization, it will be easier to remove support for BinaryFormatter from the application in the future. Only the `LegacyDrawableContainerList` and `LegacyDrawableContainer` classes uses the BinaryFormatter serialization attributes. +- **Binary Data Structure:** + The file consists of: + 1. The PNG image data. + 2. The binary data of the `DrawableContainerList` (serialized with BinaryFormatter). + 3. 8 bytes for the binary data size (Int64). + 4. 14 bytes for the Greenshot marker string (`"Greenshot01.02"` or `"Greenshot01.03"`). +- **Note:** + Because the file starts with PNG data, it was possible to open `.greenshot` files with an image viewer and at least display the image. + +### 2. Current File Format (V2) + +- **Serialization:** Uses [MessagePack](https://github.com/MessagePack-CSharp/MessagePack-CSharp/) for serialization. +- **Marker:** Files are identified by the marker string `"Greenshot02.01"` at the end of the file. The schema version `".01"` would be ignored for determines the file format. +- **Implementation:** See `Greenshot.Editor.FileFormat.V2.GreenshotFileV2`. +- **Binary Data Structure:** + The file consists of: + 1. The PNG image data of the rendered image for export + 2. The binary data of the `GreenshotFileDto`. (serialized with MessagePack). + 3. 8 bytes for the binary data size (Int64). + 4. 11 bytes for the Greenshot marker string `"Greenshot02"`. + 5. 3 bytes for the Greenshot file schema version (i.e. `".01"`). +- **Note:** + In contrast to the legacy file format, the current file format contains the rendered PNG image. This means that all graphical elements are rendered into the PNG image, and the file still can be opened with any image viewer. The image from the editors canvas is stored in the serialized `GreenshotFileDto`. +--- + +# Greenshot Template Files + +- Greenshot template files are saved with the `.gst` file extension. +- Class `GreenshotTemplate` represents a .gst file as domain object. +- It contains a `DrawableContainerList` that came from `Surface.Elements`. This is exactly the same as the `DrawableContainerList` in a Greenshot editor file. +- For serialization/deserialization, the `GreenshotTemplate` is converted to a DTO `GreenshotTemplateDto`, which contains the data to be serialized. + +## Supported File Formats + +Greenshot template files supports two file formats: +- Legacy file format (V1) +- Current file format (V2) + +### 1. Legacy File Format (V1) + +- **Serialization:** Uses .NET BinaryFormatter. +- **Markers:** Files don't have a marker string. +- **Security:** BinaryFormatter is deprecated and considered insecure. Support for this format will be removed in the future. +- **Implementation:** See `Greenshot.Editor.FileFormat.V1.GreenshotTemplateV1`. +- **Implementation Detail:** + The binary data for the drawable containers is not deserialized directly into a `DrawableContainerList`. Instead, there is an intermediate layer of legacy classes. The deserialization steps are as follows: First, the data is deserialized into a `LegacyDrawableContainerList`, then it is converted to a `DrawableContainerListDto`, and finally, it is converted to a `DrawableContainerList`. + Because the legacy classes are used during deserialization, it will be easier to remove support for BinaryFormatter from the application in the future. Only the `LegacyDrawableContainerList` and `LegacyDrawableContainer` classes contain the BinaryFormatter serialization attributes. +- **Binary Data Structure:** + The file consists of: + 1. The binary data of the `DrawableContainerList` (serialized with BinaryFormatter). + +### 2. Current File Format (V2) + +- **Serialization:** Uses [MessagePack](https://github.com/MessagePack-CSharp/MessagePack-CSharp/) for serialization. +- **Marker:** Files are identified by the marker string `"GreenshotTemplate02"` at the beginning of the file. _(Note: Schema version (i.e. `".01"` ) is not part of the marker)_. +- **Implementation:** See `Greenshot.Editor.FileFormat.V2.GreenshotTemplateV2`. +- **Binary Data Structure:** + The file consists of: + 1. 22 bytes for the Greenshot marker string with complete version string (i.e. `"GreenshotTemplate02.01"` ). + 2. The binary data of `GreenshotTemplateDto`. (serialized with MessagePack). + + +## Summary Table + +|File Type | Format | Marker | Serializer | Security | Implementation | Support Status | +|----------|--------|---------------------|-----------------|------------------|---------------------|---------------------| +| Editor | V1 | Greenshot01.02/03 | BinaryFormatter | Deprecated/Unsafe| GreenshotFileV1 | To be removed | +| Template | V1 | -no marker- | BinaryFormatter | Deprecated/Unsafe| GreenshotTemplateV1 | To be removed | +| Editor | V2 | Greenshot02 | MessagePack | Modern/Safe | GreenshotFileV2 | Current | +| Template | V2 | GreenshotTemplate02 | MessagePack | Modern/Safe | GreenshotTemplateV2 | Current | + +--- + +# Versioning + +Greenshot files and Greenshot templates using the same versioning concept. So the Greenshot templates reuses some global constants from the Greenshot file format. + +Since File Format V2 Greenshot file versions are independent of the application version. + +The version is composed of two parts: + +- **Format Version:** Indicates the serializer is used (i.e. BinaryFormatter or MessagePack) and the binary structure of the file. +- **Schema Version:** Indicates the version of the DTO structure. + +The version is represented as `{format version}.{schema version}` (i.e. as string, `02.01`, this pattern still matches markers in V1 and V2). + +- **Format Version:** See `GreenshotFileVersionHandler.GreenshotFileFormatVersion`. +- **Schema Version:** See `GreenshotFileVersionHandler.CurrentSchemaVersion`. + +--- + +# MessagePack + +Since File Format V2, Greenshot uses [MessagePack](https://github.com/MessagePack-CSharp/MessagePack-CSharp/) for serialization. + + TL;DR: For using MessagePack, the DTO classes are annotated with attributes `[MessagePackObject]`. + Every property that should be serialized is annotated with `[Key]` and has a unique key value. + + Serialization/Deserialization: + var bytes = MessagePackSerializer.Serialize(dto); + var dto = MessagePackSerializer.Deserialize(bytes); + + +## MessagePack Rules + +- **Always** use `int` as `[Key]` value. +- **Do not change** the `[Key]` value of existing properties. +- **Do not remove** properties unless they are marked with `[IgnoreMember]`. +- **Add new properties** with new, unique `[Key]` values. +- **Obsolete properties** have to be marked with `[IgnoreMember]` instead of being removed. +- `[Key]` values `0 - 9` are reserved for operational functions like versioning and have no relation to domain data. +- Use `[Key]` values `10 - 99` for base class properties and `100 - 199` for inherited class properties and so on. +- If possible, avoid inheritance and keep DTO classes as simple as possible. +- If possible, include no business logic in DTO classes. Use the `DtoHelper` class for that. + + +## Rules for Schema and Format Versioning + +- **Non-breaking changes** (do **not** require incrementing `SchemaVersion`): + - Adding required properties with default values. + - Adding new optional properties. + - Changing property types that are compatible (i.e., `int` → `long`). + - Changing default values. + - Marking properties as obsolete with `[IgnoreMember]`. + +- **Breaking changes** (require incrementing `SchemaVersion`): + - Reordering values in enums used in DTO properties. + - Adding required properties without default values. + - Changing property types that are not compatible (i.e., `int` → `string`). + - Changing the meaning of a property content, that requires migration logic. + - Moving properties between DTOs. + +- **Format Version** changes if: + - the serialization method changes (an other serializer than MessagePack and BinaryFormatter is used) + - the binary data structure changes (i.e. the marker string changes). + +--- + +## DTO Migration + +If the DTO structure changes in the future (i.e. a breaking change), you must: + +- Increment the `SchemaVersion`. +- Implement migration logic to convert older DTO versions to the current version (see `GreenshotFileV2.MigrateToCurrentVersion`). +- Ensure backward compatibility for loading older files. + +--- + +# How to handle files + +Main entry point for file handling is the `FileFormatHandler` class. + +It provides methods to save and load files. There is a `GreenshotFileFormatHandler` for handling Greenshot editor files and a `GreenshotTemplateFormatHandler` for handling Greenshot template files. + +Every `FileFormatHandler` calls the associated `VersionHandler` wich determines the file format version and calls the appropriate class that implements the serialization/deserialization logic. + +From `FileFormatHandler` to the implementation, only first-class domain objects (`GreenshotFile`, `GreenshotTemplate`) or file streams are passed as parameters. _(In particular, no `Surface` or `DrawableContainer`.)_ + +--- + +# Serialization + +The serialization is done by these steps: + 1. Convert the domain object (`GreenshotFile` or `GreenshotTemplate`) to a DTO (`GreenshotFileDto` or `GreenshotTemplateDto`) by using `Greenshot.Editor.FileFormat.Dto.ConvertDomainToDto.ToDto()` + 1. Convert the DTO to binary data using the MessagePack serializer. _(Serialization with BinaryFormatter is not implemented anymore.)_ + +# Deserialization + +The deserialization is done by these steps: +1. determine the file format version. + - A: If the file format version is V1: + - Extract the PNG image data from the file. + - convert the binary data to `LegacyDrawableContainerList` by using the BinaryFormatter deserializer. _(In this step there is some backward compatibility logic to handle older versions of the legacy classes.)_ + - convert this to `DrawableContainerListDto`. + - use the image data and `DrawableContainerListDto` to create a `GreenshotFileDto` or `GreenshotTemplateDto`. + - B: If the file format version is V2: + - convert the binary data to `GreenshotFileDto` or `GreenshotTemplateDto` using the MessagePack deserializer directly. +1. Convert the DTO to a domain object (`GreenshotFile` or `GreenshotTemplate`) by using `Greenshot.Editor.FileFormat.Dto.ConvertDtoToDomain.ToDomain()`. +1. Call `OnDeserialize()` on every `DrawableContainer` domain object to perform additional initialization. + + +# Clipboard + +In the Greenshot Editor it is possible to copy and paste selected graphical elements (`DrawableContainerList`) to and from the clipboard. For the clipboard data we uses the same mechanism as for the file format. +The `DrawableContainerList` is converted to a `DrawableContainerListDto` and serialized with MessagePack. The clipboard data is stored as a byte array in the clipboard. \ No newline at end of file diff --git a/src/Greenshot.Editor/FileFormatHandlers/AbstractFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/AbstractFileFormatHandler.cs index fff576267..e82b9fa09 100644 --- a/src/Greenshot.Editor/FileFormatHandlers/AbstractFileFormatHandler.cs +++ b/src/Greenshot.Editor/FileFormatHandlers/AbstractFileFormatHandler.cs @@ -40,17 +40,20 @@ namespace Greenshot.Editor.FileFormatHandlers return 0; } + /// public abstract bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null); + /// public abstract bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap); /// - /// Default implementation taking the TryLoadFromStream image and placing it in an ImageContainer + /// /// + /// Default implementation taking the TryLoadFromStream image and placing it in an /// Stream /// string /// ISurface - /// IEnumerable{IDrawableContainer} + /// One if it was possible to load an image from the stream public virtual IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface parent = null) { if (TryLoadFromStream(stream, extension, out var bitmap)) diff --git a/src/Greenshot.Editor/FileFormatHandlers/DefaultFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/DefaultFileFormatHandler.cs index 115b5328f..c363b2e99 100644 --- a/src/Greenshot.Editor/FileFormatHandlers/DefaultFileFormatHandler.cs +++ b/src/Greenshot.Editor/FileFormatHandlers/DefaultFileFormatHandler.cs @@ -115,7 +115,11 @@ namespace Greenshot.Editor.FileFormatHandlers return true; } + /// /// + /// + /// This default implementation uses + /// if the image was successfully loaded into a ; otherwise, . public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) { try diff --git a/src/Greenshot.Editor/FileFormatHandlers/GreenshotFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/GreenshotFileFormatHandler.cs index 0b956c001..6e7687c2b 100644 --- a/src/Greenshot.Editor/FileFormatHandlers/GreenshotFileFormatHandler.cs +++ b/src/Greenshot.Editor/FileFormatHandlers/GreenshotFileFormatHandler.cs @@ -22,112 +22,96 @@ using System; using System.Collections.Generic; using System.Drawing; -using System.Drawing.Imaging; using System.IO; -using System.Reflection; -using System.Text; -using Greenshot.Base.Core; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat; using log4net; -namespace Greenshot.Editor.FileFormatHandlers +namespace Greenshot.Editor.FileFormatHandlers; + +public sealed class GreenshotFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler { - public class GreenshotFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler + private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileFormatHandler)); + private readonly IReadOnlyCollection _ourExtensions = new[] { ".greenshot" }; + public GreenshotFileFormatHandler() { - private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileFormatHandler)); - private readonly IReadOnlyCollection _ourExtensions = new [] { ".greenshot" }; - public GreenshotFileFormatHandler() + SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions; + } + + /// + /// Save the surface to the specified stream in the current .greenshot file format. + /// + /// Ignores the given bitmap, as the .greenshot file always uses the original surface image. + /// if the surface was successfully saved to the stream; otherwise, . + public override bool TrySaveToStream(Bitmap bitmap, Stream stream, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) + { + if (surface == null) { - SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions; - } - - public override bool TrySaveToStream(Bitmap bitmap, Stream stream, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) - { - if (surface == null) - { - return false; - } - - try - { - bitmap.Save(stream, ImageFormat.Png); - using MemoryStream tmpStream = new MemoryStream(); - long bytesWritten = surface.SaveElementsToStream(tmpStream); - using BinaryWriter writer = new BinaryWriter(tmpStream); - writer.Write(bytesWritten); - Version v = Assembly.GetExecutingAssembly().GetName().Version; - byte[] marker = Encoding.ASCII.GetBytes($"Greenshot{v.Major:00}.{v.Minor:00}"); - writer.Write(marker); - tmpStream.WriteTo(stream); - return true; - } - catch (Exception ex) - { - Log.Error("Couldn't save surface as .greenshot: ", ex); - } - return false; } - public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + try { - try - { - var surface = LoadSurface(stream); - bitmap = (Bitmap)surface.GetImageForExport(); - return true; - } - catch (Exception ex) - { - Log.Error("Couldn't load .greenshot: ", ex); - } - bitmap = null; - return false; + //ignore the given bitmap, in .greenshot file we always use the original surface image + return GreenshotFileVersionHandler.SaveToStreamInCurrentVersion(surface, stream); + } + catch (Exception ex) + { + Log.Error("Couldn't save surface as .greenshot: ", ex); } - private ISurface LoadSurface(Stream surfaceFileStream) + return false; + } + + /// + /// + /// + /// This implementation loads the from stream. Use this to creates a and uses wich renders all contained elements into the image. + /// if the bitmap was successfully loaded from the stream; otherwise, . + public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + { + try { - var returnSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); - Bitmap captureBitmap; + var surface = GreenshotFileVersionHandler.CreateSurfaceFromStream(stream); - // Fixed problem that the bitmap stream is disposed... by Cloning the image - // This also ensures the bitmap is correctly created - using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true)) - { - Log.DebugFormat("Loaded capture from .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); - captureBitmap = ImageHelper.Clone(tmpImage) as Bitmap; - } - - // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) - const int markerSize = 14; - surfaceFileStream.Seek(-markerSize, SeekOrigin.End); - using (var streamReader = new StreamReader(surfaceFileStream)) - { - var greenshotMarker = streamReader.ReadToEnd(); - if (!greenshotMarker.StartsWith("Greenshot")) - { - throw new ArgumentException("Stream is not a Greenshot file!"); - } - - Log.InfoFormat("Greenshot file format: {0}", greenshotMarker); - const int fileSizeLocation = 8 + markerSize; - surfaceFileStream.Seek(-fileSizeLocation, SeekOrigin.End); - using BinaryReader reader = new BinaryReader(surfaceFileStream); - long bytesWritten = reader.ReadInt64(); - surfaceFileStream.Seek(-(bytesWritten + fileSizeLocation), SeekOrigin.End); - returnSurface.LoadElementsFromStream(surfaceFileStream); - } - - if (captureBitmap != null) - { - returnSurface.Image = captureBitmap; - Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", captureBitmap.Width, captureBitmap.Height, captureBitmap.PixelFormat, captureBitmap.HorizontalResolution, captureBitmap.VerticalResolution); - } - - return returnSurface; + bitmap = (Bitmap)surface.GetImageForExport(); + return true; } + catch (Exception ex) + { + Log.Error("Couldn't load .greenshot: ", ex); + } + bitmap = null; + return false; + } + + /// + /// Load a from file path + /// + /// This implementation loads the from file. Use this to creates a . + /// + /// + public ISurface LoadGreenshotSurface(string fullPath) + { + if (string.IsNullOrEmpty(fullPath)) + { + Log.Warn("No file path provided for loading Greenshot surface."); + return null; + } + Log.InfoFormat("Loading surface data from file {0}", fullPath); + + using Stream greenshotFileStream = File.OpenRead(fullPath); + ISurface returnSurface = GreenshotFileVersionHandler.CreateSurfaceFromStream(greenshotFileStream); + + Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", fullPath, returnSurface.Image.Width, returnSurface.Image.Height, + returnSurface.Image.PixelFormat, returnSurface.Image.HorizontalResolution, returnSurface.Image.VerticalResolution); + + return returnSurface; } } + diff --git a/src/Greenshot.Editor/FileFormatHandlers/GreenshotTemplateFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/GreenshotTemplateFormatHandler.cs new file mode 100644 index 000000000..74b404087 --- /dev/null +++ b/src/Greenshot.Editor/FileFormatHandlers/GreenshotTemplateFormatHandler.cs @@ -0,0 +1,124 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.IO; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Plugin; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat; +using log4net; + +namespace Greenshot.Editor.FileFormatHandlers; + +/// +/// Handles the .gst (Greenshot Template) file format, similar to GreenshotFileFormatHandler but for templates. +/// +public sealed class GreenshotTemplateFormatHandler : AbstractFileFormatHandler, IFileFormatHandler +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotTemplateFormatHandler)); + private readonly IReadOnlyCollection _ourExtensions = [".gst"]; + public GreenshotTemplateFormatHandler() + { + SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = []; + SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions; + } + + /// + public override bool TrySaveToStream(Bitmap bitmap, Stream stream, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) + { + if (surface == null) + { + return false; + } + + try + { + return GreenshotTemplateVersionHandler.SaveToStreamInCurrentVersion(new DrawableContainerList(surface.Elements), stream); + } + catch (Exception ex) + { + Log.Error("Couldn't save surface as .gst: ", ex); + } + + return false; + } + + /// + /// Saves the surface data to a file in the Greenshot template format (.gst). + /// + /// The full file path where the template will be saved. Cannot be null or empty. + /// The surface to save as a template. Cannot be null. + /// Thrown if is null or empty, or if is null. + /// Thrown if the template fails to save to the specified file. + public void SaveTemplateToFile(string fullPath, ISurface surface) + { + if (string.IsNullOrEmpty(fullPath)) + { + throw new ArgumentNullException(nameof(fullPath), "Cannot save Greenshot template to stream, fullPath is null or empty."); + } + if (surface == null) + { + throw new ArgumentNullException(nameof(surface), "Cannot save Greenshot template, surface is null."); + } + Log.InfoFormat("Saving template surface data to file {0}", fullPath); + + using Stream fileStreamWrite = File.OpenWrite(fullPath); + + if (!TrySaveToStream(null, fileStreamWrite, ".gst", surface, null)) + { + throw new Exception("Failed to save Greenshot template to file."); + } + } + + /// + /// Currently not supported + public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + { + throw new NotImplementedException("Greenshot template (.gst) does not support save as image."); + } + + /// + /// Loads a Greenshot template from the specified file and applies its elements to the surface. + /// + /// The full path to the file containing the Greenshot template. Cannot be null or empty. + /// The surface to which the template elements will be applied. Cannot be null. + /// Thrown if is null or empty, or if is null. + public void LoadTemplateFromFile(string fullPath, ISurface surface) + { + if (string.IsNullOrEmpty(fullPath)) + { + throw new ArgumentNullException(nameof(fullPath), "Cannot load Greenshot template, fullPath is null or empty."); + } + if (surface == null) + { + throw new ArgumentNullException(nameof(surface), "Cannot load Greenshot template, surface is null."); + } + Log.InfoFormat("Loading template surface data from file {0}", fullPath); + + using Stream fileStreamRead = File.OpenRead(fullPath); + var greenshotTemplate = GreenshotTemplateVersionHandler.LoadFromStream(fileStreamRead); + surface.LoadElements(greenshotTemplate.ContainerList); + } +} diff --git a/src/Greenshot.Editor/FileFormatHandlers/MetaFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/MetaFileFormatHandler.cs index 04c3825b3..a4cf297f3 100644 --- a/src/Greenshot.Editor/FileFormatHandlers/MetaFileFormatHandler.cs +++ b/src/Greenshot.Editor/FileFormatHandlers/MetaFileFormatHandler.cs @@ -19,6 +19,7 @@ * along with this program. If not, see . */ +using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; @@ -28,6 +29,7 @@ using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Plugin; using Greenshot.Editor.Drawing; +using log4net; namespace Greenshot.Editor.FileFormatHandlers { @@ -36,6 +38,7 @@ namespace Greenshot.Editor.FileFormatHandlers /// public class MetaFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler { + private static readonly ILog Log = LogManager.GetLogger(typeof(MetaFileFormatHandler)); private readonly IReadOnlyCollection _ourExtensions = new[] { ".wmf", ".emf" }; public MetaFileFormatHandler() @@ -72,9 +75,18 @@ namespace Greenshot.Editor.FileFormatHandlers /// public override IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface surface = null) { - if (Image.FromStream(stream, true, true) is Metafile metaFile) + MetafileContainer metafileContainer = null; + try { - yield return new MetafileContainer(metaFile, surface); + metafileContainer = new MetafileContainer(stream, surface); + } + catch (Exception ex) + { + Log.Error("Can't load Metafile", ex); + } + if (metafileContainer != null) + { + yield return metafileContainer; } } } diff --git a/src/Greenshot.Editor/Forms/ImageEditorForm.cs b/src/Greenshot.Editor/Forms/ImageEditorForm.cs index 97263d8c2..e4bcd5491 100644 --- a/src/Greenshot.Editor/Forms/ImageEditorForm.cs +++ b/src/Greenshot.Editor/Forms/ImageEditorForm.cs @@ -48,6 +48,8 @@ using Greenshot.Editor.Destinations; using Greenshot.Editor.Drawing; using Greenshot.Editor.Drawing.Fields; using Greenshot.Editor.Drawing.Fields.Binding; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormatHandlers; using Greenshot.Editor.Helpers; using log4net; @@ -74,7 +76,7 @@ namespace Greenshot.Editor.Forms private static readonly string[] SupportedClipboardFormats = { - typeof(string).FullName, "Text", typeof(IDrawableContainerList).FullName + typeof(string).FullName, "Text", typeof(DrawableContainerListDto).FullName }; private bool _originalBoldCheckState; @@ -1513,34 +1515,48 @@ namespace Greenshot.Editor.Forms private void SaveElementsToolStripMenuItemClick(object sender, EventArgs e) { + var greenshotTemplateFormatHandler = SimpleServiceProvider.Current.GetAllInstances().OfType().FirstOrDefault(); + if (greenshotTemplateFormatHandler is null) + { + throw new Exception($"No instance of {nameof(GreenshotFileFormatHandler)} found in service provider."); + } + SaveFileDialog saveFileDialog = new SaveFileDialog { Filter = "Greenshot templates (*.gst)|*.gst", FileName = FilenameHelper.GetFilenameWithoutExtensionFromPattern(coreConfiguration.OutputFileFilenamePattern, _surface.CaptureDetails) }; + DialogResult dialogResult = saveFileDialog.ShowDialog(); - if (dialogResult.Equals(DialogResult.OK)) + + if (!dialogResult.Equals(DialogResult.OK)) { - using Stream streamWrite = File.OpenWrite(saveFileDialog.FileName); - _surface.SaveElementsToStream(streamWrite); + return; } + + greenshotTemplateFormatHandler.SaveTemplateToFile(saveFileDialog.FileName, _surface); + } private void LoadElementsToolStripMenuItemClick(object sender, EventArgs e) { + var greenshotTemplateFormatHandler = SimpleServiceProvider.Current.GetAllInstances().OfType().FirstOrDefault(); + if (greenshotTemplateFormatHandler is null) + { + throw new Exception($"No instance of {nameof(GreenshotFileFormatHandler)} found in service provider."); + } + OpenFileDialog openFileDialog = new OpenFileDialog { Filter = "Greenshot templates (*.gst)|*.gst" }; - if (openFileDialog.ShowDialog() == DialogResult.OK) + if (openFileDialog.ShowDialog() != DialogResult.OK) { - using (Stream streamRead = File.OpenRead(openFileDialog.FileName)) - { - _surface.LoadElementsFromStream(streamRead); - } - - _surface.Refresh(); + return; } + + greenshotTemplateFormatHandler.LoadTemplateFromFile(openFileDialog.FileName, _surface); + _surface.Refresh(); } private void DestinationToolStripMenuItemClick(object sender, EventArgs e) diff --git a/src/Greenshot.Editor/Greenshot.Editor.csproj b/src/Greenshot.Editor/Greenshot.Editor.csproj index 5dcd99bc1..093736bf8 100644 --- a/src/Greenshot.Editor/Greenshot.Editor.csproj +++ b/src/Greenshot.Editor/Greenshot.Editor.csproj @@ -88,4 +88,8 @@ ImageEditorForm.cs + + + + \ No newline at end of file diff --git a/src/Greenshot.Editor/Helpers/BinaryFormatterHelper.cs b/src/Greenshot.Editor/Helpers/BinaryFormatterHelper.cs deleted file mode 100644 index 11c7dbae6..000000000 --- a/src/Greenshot.Editor/Helpers/BinaryFormatterHelper.cs +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * 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 1 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.Runtime.Serialization; -using System.ServiceModel.Security; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Editor.Drawing; -using Greenshot.Editor.Drawing.Fields; -using Greenshot.Editor.Drawing.Filters; -using log4net; -using static Greenshot.Editor.Drawing.FilterContainer; - -namespace Greenshot.Editor.Helpers -{ - /// - /// This helps to map the serialization of the old .greenshot file to the newer. - /// It also prevents misuse. - /// - internal class BinaryFormatterHelper : SerializationBinder - { - private static readonly ILog LOG = LogManager.GetLogger(typeof(BinaryFormatterHelper)); - private static readonly IDictionary TypeMapper = new Dictionary - { - {"System.Guid",typeof(Guid) }, - {"System.Drawing.Rectangle",typeof(System.Drawing.Rectangle) }, - {"System.Drawing.Point",typeof(System.Drawing.Point) }, - {"System.Drawing.Color",typeof(System.Drawing.Color) }, - {"System.Drawing.Bitmap",typeof(System.Drawing.Bitmap) }, - {"System.Drawing.Icon",typeof(System.Drawing.Icon) }, - {"System.Drawing.Size",typeof(System.Drawing.Size) }, - {"System.IO.MemoryStream",typeof(System.IO.MemoryStream) }, - {"System.Drawing.StringAlignment",typeof(System.Drawing.StringAlignment) }, - {"System.Collections.Generic.List`1[[Greenshot.Base.Interfaces.Drawing.IFieldHolder", typeof(List)}, - {"System.Collections.Generic.List`1[[Greenshot.Base.Interfaces.Drawing.IField", typeof(List)}, - {"System.Collections.Generic.List`1[[System.Drawing.Point", typeof(List)}, - {"Greenshot.Editor.Drawing.ArrowContainer", typeof(ArrowContainer) }, - {"Greenshot.Editor.Drawing.ArrowContainer+ArrowHeadCombination", typeof(ArrowContainer.ArrowHeadCombination) }, - {"Greenshot.Editor.Drawing.LineContainer", typeof(LineContainer) }, - {"Greenshot.Editor.Drawing.TextContainer", typeof(TextContainer) }, - {"Greenshot.Editor.Drawing.SpeechbubbleContainer", typeof(SpeechbubbleContainer) }, - {"Greenshot.Editor.Drawing.RectangleContainer", typeof(RectangleContainer) }, - {"Greenshot.Editor.Drawing.EllipseContainer", typeof(EllipseContainer) }, - {"Greenshot.Editor.Drawing.FreehandContainer", typeof(FreehandContainer) }, - {"Greenshot.Editor.Drawing.HighlightContainer", typeof(HighlightContainer) }, - {"Greenshot.Editor.Drawing.IconContainer", typeof(IconContainer) }, - {"Greenshot.Editor.Drawing.ObfuscateContainer", typeof(ObfuscateContainer) }, - {"Greenshot.Editor.Drawing.StepLabelContainer", typeof(StepLabelContainer) }, - {"Greenshot.Editor.Drawing.SvgContainer", typeof(SvgContainer) }, - {"Greenshot.Editor.Drawing.VectorGraphicsContainer", typeof(VectorGraphicsContainer) }, - {"Greenshot.Editor.Drawing.MetafileContainer", typeof(MetafileContainer) }, - {"Greenshot.Editor.Drawing.ImageContainer", typeof(ImageContainer) }, - {"Greenshot.Editor.Drawing.FilterContainer", typeof(FilterContainer) }, - {"Greenshot.Editor.Drawing.DrawableContainer", typeof(DrawableContainer) }, - {"Greenshot.Editor.Drawing.DrawableContainerList", typeof(DrawableContainerList) }, - {"Greenshot.Editor.Drawing.CursorContainer", typeof(CursorContainer) }, - {"Greenshot.Editor.Drawing.Filters.HighlightFilter", typeof(HighlightFilter) }, - {"Greenshot.Editor.Drawing.Filters.GrayscaleFilter", typeof(GrayscaleFilter) }, - {"Greenshot.Editor.Drawing.Filters.MagnifierFilter", typeof(MagnifierFilter) }, - {"Greenshot.Editor.Drawing.Filters.BrightnessFilter", typeof(BrightnessFilter) }, - {"Greenshot.Editor.Drawing.Filters.BlurFilter", typeof(BlurFilter) }, - {"Greenshot.Editor.Drawing.Filters.PixelizationFilter", typeof(PixelizationFilter) }, - {"Greenshot.Base.Interfaces.Drawing.IDrawableContainer", typeof(IDrawableContainer) }, - {"Greenshot.Base.Interfaces.Drawing.EditStatus", typeof(EditStatus) }, - {"Greenshot.Base.Interfaces.Drawing.IFieldHolder", typeof(IFieldHolder) }, - {"Greenshot.Base.Interfaces.Drawing.IField", typeof(IField) }, - {"Greenshot.Base.Interfaces.Drawing.FieldFlag", typeof(FieldFlag) }, - {"Greenshot.Editor.Drawing.Fields.Field", typeof(Field) }, - {"Greenshot.Editor.Drawing.Fields.FieldType", typeof(FieldType) }, - {"Greenshot.Editor.Drawing.FilterContainer+PreparedFilter", typeof(PreparedFilter) }, - }; - - /// - /// Do the type mapping - /// - /// Assembly for the type that was serialized - /// Type that was serialized - /// Type which was mapped - /// If something smells fishy - public override Type BindToType(string assemblyName, string typeName) - { - if (string.IsNullOrEmpty(typeName)) - { - return null; - } - var typeNameCommaLocation = typeName.IndexOf(","); - var comparingTypeName = typeName.Substring(0, typeNameCommaLocation > 0 ? typeNameCommaLocation : typeName.Length); - - // Correct wrong types - comparingTypeName = comparingTypeName.Replace("Greenshot.Drawing", "Greenshot.Editor.Drawing"); - comparingTypeName = comparingTypeName.Replace("Greenshot.Plugin.Drawing", "Greenshot.Base.Interfaces.Drawing"); - comparingTypeName = comparingTypeName.Replace("GreenshotPlugin.Interfaces.Drawing", "Greenshot.Base.Interfaces.Drawing"); - comparingTypeName = comparingTypeName.Replace("Greenshot.Drawing.Fields", "Greenshot.Editor.Drawing.Fields"); - comparingTypeName = comparingTypeName.Replace("Greenshot.Drawing.Filters", "Greenshot.Editor.Drawing.Filters"); - - if (TypeMapper.TryGetValue(comparingTypeName, out var returnType)) - { - LOG.Info($"Mapped {assemblyName} - {typeName} to {returnType.FullName}"); - return returnType; - } - LOG.Warn($"Unexpected Greenshot type in .greenshot file detected, maybe vulnerability attack created with ysoserial? Suspicious type: {assemblyName} - {typeName}"); - throw new SecurityAccessDeniedException($"Suspicious type in .greenshot file: {assemblyName} - {typeName}"); - } - } -} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertApplicationToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertApplicationToDtoTests.cs new file mode 100644 index 000000000..86c0075b5 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertApplicationToDtoTests.cs @@ -0,0 +1,104 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertApplicationToDtoTests +{ + [Fact] + public void ConvertDomainToDto_ApplicationFile_Returns_ApplicationFileDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var lineContainer = new LineContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + var rectangleContainer = new RectangleContainer(surface) + { + Left = 30, + Top = 40, + Width = 200, + Height = 80 + }; + var domainList = new DrawableContainerList { lineContainer, rectangleContainer }; + var image = new Bitmap(10, 10); + var domain = new GreenshotFile + { + ContainerList = domainList, + Image = image, + SchemaVersion = GreenshotFileVersionHandler.CurrentSchemaVersion + }; + + // Act + var dto = ConvertDomainToDto.ToDto(domain); + + // Assert + Assert.NotNull(dto); + Assert.Equal(domain.SchemaVersion, dto.SchemaVersion); + Assert.NotNull(dto.Image); + Assert.NotNull(dto.ContainerList); + Assert.Equal(2, dto.ContainerList.ContainerList.Count); + Assert.IsType(dto.ContainerList.ContainerList[0]); + Assert.IsType(dto.ContainerList.ContainerList[1]); + + Assert.Equal(lineContainer.Top, dto.ContainerList.ContainerList[0].Top); + Assert.Equal(lineContainer.Left, dto.ContainerList.ContainerList[0].Left); + Assert.Equal(lineContainer.Width, dto.ContainerList.ContainerList[0].Width); + Assert.Equal(lineContainer.Height, dto.ContainerList.ContainerList[0].Height); + + Assert.Equal(rectangleContainer.Top, dto.ContainerList.ContainerList[1].Top); + Assert.Equal(rectangleContainer.Left, dto.ContainerList.ContainerList[1].Left); + Assert.Equal(rectangleContainer.Width, dto.ContainerList.ContainerList[1].Width); + Assert.Equal(rectangleContainer.Height, dto.ContainerList.ContainerList[1].Height); + } + + /// + /// Trivial test to ensure that null ApplicationFile returns null DTO. + /// + [Fact] + public void ToDto_NullApplicationFile_ReturnsNull() + { + // Arrange + GreenshotFile domain = null; + + // Act + // ReSharper disable once ExpressionIsAlwaysNull + var result = ConvertDomainToDto.ToDto(domain); + + // Assert + Assert.Null(result); + } + +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertArrowContainerToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertArrowContainerToDtoTests.cs new file mode 100644 index 000000000..2683591fd --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertArrowContainerToDtoTests.cs @@ -0,0 +1,148 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; +using static Greenshot.Editor.Drawing.ArrowContainer; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertArrowContainerToDtoTests +{ + [Fact] + public void ConvertDomainToDto_ArrowContainer_Returns_ArrowContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var arrowContainer = new ArrowContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + // see ArrowContainer.InitializeFields() for defaults + var defaultLineThickness = 2; + var defaultLineColor = Color.Red; + var defaultFillColor = Color.Transparent; + var defaultShadow = true; + var defaultArrowHeads = ArrowHeadCombination.END_POINT; + + // Act + var result = ConvertDomainToDto.ToDto(arrowContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultFillColor = DtoHelper.GetFieldValue(result, FieldType.FILL_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + var resultArrowHeads = DtoHelper.GetFieldValue(result, FieldType.ARROWHEADS); + + Assert.NotNull(result); + Assert.Equal(arrowContainer.Left, result.Left); + Assert.Equal(arrowContainer.Top, result.Top); + Assert.Equal(arrowContainer.Width, result.Width); + Assert.Equal(arrowContainer.Height, result.Height); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(defaultShadow, (bool)resultShadow); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(defaultLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(defaultLineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(defaultFillColor, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultArrowHeads); + Assert.IsType(resultArrowHeads); + Assert.Equal(defaultArrowHeads, resultArrowHeads); + } + + [Fact] + public void ConvertDomainToDto_ArrowContainer_with_Field_Values_Returns_ArrowContainerDto_with_same_Values() + { + // Arrange + var colorBlue = Color.Blue; + var colorGreen = Color.Green; + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var arrowContainer = new ArrowContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + arrowContainer.SetFieldValue(FieldType.LINE_THICKNESS, 3); + arrowContainer.SetFieldValue(FieldType.LINE_COLOR, colorBlue); + arrowContainer.SetFieldValue(FieldType.FILL_COLOR, colorGreen); + arrowContainer.SetFieldValue(FieldType.SHADOW, false); + arrowContainer.SetFieldValue(FieldType.ARROWHEADS, ArrowHeadCombination.BOTH); + + // Act + var result = ConvertDomainToDto.ToDto(arrowContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultFillColor = DtoHelper.GetFieldValue(result, FieldType.FILL_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + var resultArrowHeads = DtoHelper.GetFieldValue(result, FieldType.ARROWHEADS); + + Assert.NotNull(result); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(colorGreen, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorGreen)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); + + Assert.NotNull(resultArrowHeads); + Assert.IsType(resultArrowHeads); + Assert.Equal(ArrowHeadCombination.BOTH, resultArrowHeads); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertCursorContainerToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertCursorContainerToDtoTests.cs new file mode 100644 index 000000000..59391d843 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertCursorContainerToDtoTests.cs @@ -0,0 +1,58 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; +using static Greenshot.Editor.Drawing.ArrowContainer; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertCursorContainerToDtoTests +{ + [Fact] + public void ConvertDomainToDto_CursorContainer_Returns_CursorContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var cursorContainer = new CursorContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + + }; + + // Act + var result = ConvertDomainToDto.ToDto(cursorContainer); + + // Assert + Assert.NotNull(result); + Assert.Equal(cursorContainer.Left, result.Left); + Assert.Equal(cursorContainer.Top, result.Top); + Assert.Equal(cursorContainer.Width, result.Width); + Assert.Equal(cursorContainer.Height, result.Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertDrawableContainerListToDtoTest.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertDrawableContainerListToDtoTest.cs new file mode 100644 index 000000000..65739529f --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertDrawableContainerListToDtoTest.cs @@ -0,0 +1,74 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertDrawableContainerListToDtoTest +{ + [Fact] + public void ConvertDomainToDto_DrawableContainerList_Returns_DrawableContainerListDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var lineContainer = new LineContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + var rectangleContainer = new RectangleContainer(surface) + { + Left = 30, + Top = 40, + Width = 200, + Height = 80 + }; + var domainList = new DrawableContainerList { lineContainer, rectangleContainer }; + + // Act + var dto = ConvertDomainToDto.ToDto(domainList); + + // Assert + Assert.NotNull(dto); + Assert.Equal(2, dto.ContainerList.Count); + Assert.IsType(dto.ContainerList[0]); + Assert.IsType(dto.ContainerList[1]); + + Assert.Equal(lineContainer.Top, dto.ContainerList[0].Top); + Assert.Equal(lineContainer.Left, dto.ContainerList[0].Left); + Assert.Equal(lineContainer.Width, dto.ContainerList[0].Width); + Assert.Equal(lineContainer.Height, dto.ContainerList[0].Height); + + Assert.Equal(rectangleContainer.Top, dto.ContainerList[1].Top); + Assert.Equal(rectangleContainer.Left, dto.ContainerList[1].Left); + Assert.Equal(rectangleContainer.Width, dto.ContainerList[1].Width); + Assert.Equal(rectangleContainer.Height, dto.ContainerList[1].Height); + } +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertEllipseContainerToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertEllipseContainerToDtoTests.cs new file mode 100644 index 000000000..22915c3e3 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertEllipseContainerToDtoTests.cs @@ -0,0 +1,135 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertEllipseContainerToDtoTests +{ + [Fact] + public void ConvertDomainToDto_EllipseContainer_Returns_EllipseContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var ellipseContainer = new EllipseContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + // see EllipseContainer.InitializeFields() for defaults + var defaultLineThickness = 2; + var defaultLineColor = Color.Red; + var defaultFillColor = Color.Transparent; + var defaultShadow = true; + + // Act + var result = ConvertDomainToDto.ToDto(ellipseContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultFillColor = DtoHelper.GetFieldValue(result, FieldType.FILL_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + + Assert.NotNull(result); + Assert.Equal(ellipseContainer.Left, result.Left); + Assert.Equal(ellipseContainer.Top, result.Top); + Assert.Equal(ellipseContainer.Width, result.Width); + Assert.Equal(ellipseContainer.Height, result.Height); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(defaultShadow,(bool)resultShadow); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(defaultLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(defaultLineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(defaultFillColor, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + } + + [Fact] + public void ConvertDomainToDto_EllipseContainer_with_Field_Values_Returns_EllipseContainerDto_with_same_Values() + { + // Arrange + var colorBlue = Color.Blue; + var colorGreen = Color.Green; + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var ellipseContainer = new EllipseContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + ellipseContainer.SetFieldValue(FieldType.LINE_THICKNESS, 3); + ellipseContainer.SetFieldValue(FieldType.LINE_COLOR, colorBlue); + ellipseContainer.SetFieldValue(FieldType.FILL_COLOR, colorGreen); + ellipseContainer.SetFieldValue(FieldType.SHADOW, false); + + // Act + var result = ConvertDomainToDto.ToDto(ellipseContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultFillColor = DtoHelper.GetFieldValue(result, FieldType.FILL_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + + Assert.NotNull(result); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3,resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(colorGreen, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorGreen)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertFieldToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertFieldToDtoTests.cs new file mode 100644 index 000000000..04d4faec6 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertFieldToDtoTests.cs @@ -0,0 +1,86 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; +using static Greenshot.Editor.Drawing.ArrowContainer; +using static Greenshot.Editor.Drawing.FilterContainer; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertFieldToDtoTests +{ + /// + /// A collectioan of all supported value types for . + /// + public static IEnumerable GetFieldTestData() + { + yield return [FieldType.FONT_BOLD, typeof(bool), true]; + yield return [FieldType.FONT_FAMILY, typeof(string), "Arial"]; + yield return [FieldType.LINE_THICKNESS, typeof(int), 42]; + yield return [FieldType.PREVIEW_QUALITY, typeof(float), 3.14f]; + yield return [FieldType.PREVIEW_QUALITY, typeof(double), 3.14d]; + yield return [FieldType.PREVIEW_QUALITY, typeof(decimal), 3.14m]; + yield return [FieldType.ARROWHEADS, typeof(ArrowHeadCombination), ArrowHeadCombination.END_POINT]; + yield return [FieldType.TEXT_HORIZONTAL_ALIGNMENT, typeof(StringAlignment), StringAlignment.Center]; + yield return [FieldType.ARROWHEADS, typeof(PreparedFilter), PreparedFilter.TEXT_HIGHTLIGHT]; + yield return [FieldType.FLAGS, typeof(FieldFlag), FieldFlag.COUNTER]; + } + + [Theory] + [MemberData(nameof(GetFieldTestData))] + public void ConvertDomainToDto_Field_Returns_FieldDto(IFieldType field, Type valueType, object value) + { + // Arrange + var original = new Field(field, "TestScope") { Value = value }; + + // Act + var result = ConvertDomainToDto.ToDto(original); + + // Assert + Assert.NotNull(result); + Assert.Equal(field.Name, result.FieldTypeName); + Assert.Equal("TestScope", result.Scope); + Assert.IsType(valueType, result.Value.GetValue()); + Assert.Equal(value, result.Value.GetValue()); + } + + /// + /// trivial test to ensure that null Field returns null DTO. + /// + [Fact] + public void ToDto_NullField_ReturnsNull() + { + // Arrange + Field domain = null; + // Act + // ReSharper disable once ExpressionIsAlwaysNull + var result = ConvertDomainToDto.ToDto(domain); + // Assert + Assert.Null(result); + } + +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertFreehandContainerToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertFreehandContainerToDtoTests.cs new file mode 100644 index 000000000..cc4bc6b66 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertFreehandContainerToDtoTests.cs @@ -0,0 +1,127 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertFreehandContainerToDtoTests +{ + [Fact] + public void ConvertDomainToDto_FreehandContainer_Returns_FreehandContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var freehandContainer = new FreehandContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + CapturePoints = [new Point(10, 20), new Point(30, 40)] + }; + // see FreehandContainer.InitializeFields() for defaults + var defaultLineThickness = 3; + var defaultLineColor = Color.Red; + + // Act + var result = ConvertDomainToDto.ToDto(freehandContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + + Assert.NotNull(result); + Assert.Equal(freehandContainer.Left, result.Left); + Assert.Equal(freehandContainer.Top, result.Top); + Assert.Equal(freehandContainer.Width, result.Width); + Assert.Equal(freehandContainer.Height, result.Height); + Assert.NotNull(result.CapturePoints); + Assert.Equal(freehandContainer.CapturePoints.Count, result.CapturePoints.Count); + Assert.Equal(freehandContainer.CapturePoints[0].X, result.CapturePoints[0].X); + Assert.Equal(freehandContainer.CapturePoints[0].Y, result.CapturePoints[0].Y); + Assert.Equal(freehandContainer.CapturePoints[1].X, result.CapturePoints[1].X); + Assert.Equal(freehandContainer.CapturePoints[1].Y, result.CapturePoints[1].Y); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(defaultLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(defaultLineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + } + + [Fact] + public void ConvertDomainToDto_FreehandContainer_with_Field_Values_Returns_FreehandContainerDto_with_same_Values() + { + // Arrange + var colorBlue = Color.Blue; + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var freehandContainer = new FreehandContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + CapturePoints = [new Point(10, 20), new Point(30, 40)] + }; + freehandContainer.SetFieldValue(FieldType.LINE_THICKNESS, 5); + freehandContainer.SetFieldValue(FieldType.LINE_COLOR, colorBlue); + + // Act + var result = ConvertDomainToDto.ToDto(freehandContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + + Assert.NotNull(result); + Assert.Equal(freehandContainer.Left, result.Left); + Assert.Equal(freehandContainer.Top, result.Top); + Assert.Equal(freehandContainer.Width, result.Width); + Assert.Equal(freehandContainer.Height, result.Height); + Assert.NotNull(result.CapturePoints); + Assert.Equal(freehandContainer.CapturePoints.Count, result.CapturePoints.Count); + Assert.Equal(freehandContainer.CapturePoints[0].X, result.CapturePoints[0].X); + Assert.Equal(freehandContainer.CapturePoints[0].Y, result.CapturePoints[0].Y); + Assert.Equal(freehandContainer.CapturePoints[1].X, result.CapturePoints[1].X); + Assert.Equal(freehandContainer.CapturePoints[1].Y, result.CapturePoints[1].Y); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(5, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertHighlightContainerToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertHighlightContainerToDtoTests.cs new file mode 100644 index 000000000..3e933d929 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertHighlightContainerToDtoTests.cs @@ -0,0 +1,133 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; +using static Greenshot.Editor.Drawing.FilterContainer; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertHighlightContainerToDtoTests +{ + [Fact] + public void ConvertDomainToDto_HighlightContainer_Returns_HighlightContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var highlightContainer = new HighlightContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + // see HighlightContainer.InitializeFields() for defaults + var defaultLineThickness = 0; + var defaultLineColor = Color.Red; + var defaultShadow = false; + var defaultPreparedFilter = PreparedFilter.TEXT_HIGHTLIGHT; + + // Act + var result = ConvertDomainToDto.ToDto(highlightContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + var resultPreparedFilter = DtoHelper.GetFieldValue(result, FieldType.PREPARED_FILTER_HIGHLIGHT); + + Assert.NotNull(result); + Assert.Equal(highlightContainer.Left, result.Left); + Assert.Equal(highlightContainer.Top, result.Top); + Assert.Equal(highlightContainer.Width, result.Width); + Assert.Equal(highlightContainer.Height, result.Height); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(defaultShadow,(bool)resultShadow); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(defaultLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(defaultLineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultPreparedFilter); + Assert.IsType(resultPreparedFilter); + Assert.Equal(defaultPreparedFilter, resultPreparedFilter); + } + + [Fact] + public void ConvertDomainToDto_HighlightContainer_with_Field_Values_Returns_HighlightContainerDto_with_same_Values() + { + // Arrange + var colorBlue = Color.Blue; + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var highlightContainer = new HighlightContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + highlightContainer.SetFieldValue(FieldType.LINE_THICKNESS, 3); + highlightContainer.SetFieldValue(FieldType.LINE_COLOR, colorBlue); + highlightContainer.SetFieldValue(FieldType.SHADOW, true); + highlightContainer.SetFieldValue(FieldType.PREPARED_FILTER_HIGHLIGHT, PreparedFilter.AREA_HIGHLIGHT); + + // Act + var result = ConvertDomainToDto.ToDto(highlightContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + var resultPreparedFilter = DtoHelper.GetFieldValue(result, FieldType.PREPARED_FILTER_HIGHLIGHT); + + Assert.NotNull(result); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3,resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.True((bool)resultShadow); + + Assert.NotNull(resultPreparedFilter); + Assert.IsType(resultPreparedFilter); + Assert.Equal(PreparedFilter.AREA_HIGHLIGHT, resultPreparedFilter); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertIconContainerToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertIconContainerToDtoTests.cs new file mode 100644 index 000000000..89a2db31e --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertIconContainerToDtoTests.cs @@ -0,0 +1,63 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.IO; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertIconContainerToDtoTests +{ + [Fact] + public void ConvertDomainToDto_IconContainer_Returns_IconContainerDto() + { + // Arrange + var iconPath = Path.Combine("TestData", "Images", "Greenshot.ico"); + using var iconStream = File.OpenRead(iconPath); + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var iconContainer = new IconContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Icon = new Icon(iconStream) + }; + + // Act + var result = ConvertDomainToDto.ToDto(iconContainer); + + // Assert + Assert.NotNull(result); + Assert.Equal(iconContainer.Left, result.Left); + Assert.Equal(iconContainer.Top, result.Top); + Assert.Equal(iconContainer.Width, result.Width); + Assert.Equal(iconContainer.Height, result.Height); + Assert.NotNull(result.Icon); + Assert.True(result.Icon.Length > 0); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertImageContainerToDtoTest.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertImageContainerToDtoTest.cs new file mode 100644 index 000000000..f7419253f --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertImageContainerToDtoTest.cs @@ -0,0 +1,93 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertImageContainerToDtoTest +{ + [Fact] + public void ConvertDomainToDto_ImageContainer_Returns_ImageContainerDto() + { + // Arrange + var image = new Bitmap(100, 100); // Create a sample image + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var imageContainer = new ImageContainer(surface); + imageContainer.Image = image; + + // Act + var result = ConvertDomainToDto.ToDto(imageContainer); + + // Assert + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + + Assert.NotNull(result); + Assert.Equal(imageContainer.Left, result.Left); + Assert.Equal(imageContainer.Top, result.Top); + Assert.Equal(imageContainer.Width, result.Width); + Assert.Equal(imageContainer.Height, result.Height); + Assert.NotNull(result.Image); + Assert.True(result.Image.Length > 0); // Ensure the image was serialized + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); // Ensure the shadow flag is false by default + + } + + [Fact] + public void ConvertDomainToDto_ImageContainer_with_shadow_Returns_ImageContainerDto_with_Shadow() + { + // Arrange + var image = new Bitmap(100, 100); // Create a sample image + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var imageContainer = new ImageContainer(surface); + imageContainer.Image = image; + imageContainer.SetFieldValue(FieldType.SHADOW, true); + + // Act + var result = ConvertDomainToDto.ToDto(imageContainer); + + // Assert + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + + Assert.NotNull(result); + Assert.Equal(imageContainer.Left, result.Left); + Assert.Equal(imageContainer.Top, result.Top); + Assert.Equal(imageContainer.Width, result.Width); + Assert.Equal(imageContainer.Height, result.Height); + Assert.NotNull(result.Image); + Assert.True(result.Image.Length > 0); // Ensure the image was serialized + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.True((bool)resultShadow); + } + +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertLineContainerToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertLineContainerToDtoTests.cs new file mode 100644 index 000000000..4c896c253 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertLineContainerToDtoTests.cs @@ -0,0 +1,120 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertLineContainerToDtoTests +{ + [Fact] + public void ConvertDomainToDto_LineContainer_Returns_LineContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var lineContainer = new LineContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + // see LineContainer.InitializeFields() for defaults + var defaultLineThickness = 2; + var defaultLineColor = Color.Red; + var defaultShadow = true; + + // Act + var result = ConvertDomainToDto.ToDto(lineContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + + Assert.NotNull(result); + Assert.Equal(lineContainer.Left, result.Left); + Assert.Equal(lineContainer.Top, result.Top); + Assert.Equal(lineContainer.Width, result.Width); + Assert.Equal(lineContainer.Height, result.Height); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(defaultShadow,(bool)resultShadow); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(defaultLineThickness, resultLineThickness); + + Assert.NotNull(resultColor); + Assert.IsType(resultColor); + Assert.True(DtoHelper.CompareColorValue(defaultLineColor, (Color)resultColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultLineColor)} result:{DtoHelper.ArgbString((Color)resultColor)}"); + } + + [Fact] + public void ConvertDomainToDto_LineContainer_with_Field_Values_Returns_LineContainerDto_with_same_Values() + { + // Arrange + var colorBlue = Color.Blue; + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var lineContainer = new LineContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + lineContainer.SetFieldValue(FieldType.LINE_THICKNESS, 3); + lineContainer.SetFieldValue(FieldType.LINE_COLOR, colorBlue); + lineContainer.SetFieldValue(FieldType.SHADOW, false); + + // Act + var result = ConvertDomainToDto.ToDto(lineContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + + Assert.NotNull(result); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3,resultLineThickness); + + Assert.NotNull(resultColor); + Assert.IsType(resultColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertMetafileContainerToDtoTest.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertMetafileContainerToDtoTest.cs new file mode 100644 index 000000000..3487df8f3 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertMetafileContainerToDtoTest.cs @@ -0,0 +1,58 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.IO; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertMetafileContainerToDtoTest +{ + [Fact] + public void ConvertDomainToDto_MetafileContainer_Returns_MetafileContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var metafilePath = Path.Combine("TestData", "Images", "Logo_G_with_Border.emf"); + using var metafileStream = File.OpenRead(metafilePath); + + var metafileContainer = new MetafileContainer(metafileStream, surface); + + // Act + var result = ConvertDomainToDto.ToDto(metafileContainer); + + // Assert + Assert.NotNull(result); + Assert.Equal(metafileContainer.Left, result.Left); + Assert.Equal(metafileContainer.Top, result.Top); + Assert.Equal(metafileContainer.Width, result.Width); + Assert.Equal(metafileContainer.Height, result.Height); + + // Ensure the metafile was serialized + Assert.NotNull(result.MetafileData); + Assert.True(result.MetafileData.Length > 0); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertObfuscateContainerToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertObfuscateContainerToDtoTests.cs new file mode 100644 index 000000000..f23aba5ab --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertObfuscateContainerToDtoTests.cs @@ -0,0 +1,133 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; +using static Greenshot.Editor.Drawing.FilterContainer; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertObfuscateContainerToDtoTests +{ + [Fact] + public void ConvertDomainToDto_ObfuscateContainer_Returns_ObfuscateContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var obfuscateContainer = new ObfuscateContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + // see ObfuscateContainer.InitializeFields() for defaults + var defaultLineThickness = 0; + var defaultLineColor = Color.Red; + var defaultShadow = false; + var defaultPreparedFilter = PreparedFilter.PIXELIZE; + + // Act + var result = ConvertDomainToDto.ToDto(obfuscateContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + var resultPreparedFilter = DtoHelper.GetFieldValue(result, FieldType.PREPARED_FILTER_OBFUSCATE); + + Assert.NotNull(result); + Assert.Equal(obfuscateContainer.Left, result.Left); + Assert.Equal(obfuscateContainer.Top, result.Top); + Assert.Equal(obfuscateContainer.Width, result.Width); + Assert.Equal(obfuscateContainer.Height, result.Height); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(defaultShadow,(bool)resultShadow); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(defaultLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(defaultLineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultPreparedFilter); + Assert.IsType(resultPreparedFilter); + Assert.Equal(defaultPreparedFilter, resultPreparedFilter); + } + + [Fact] + public void ConvertDomainToDto_ObfuscateContainer_with_Field_Values_Returns_ObfuscateContainerDto_with_same_Values() + { + // Arrange + var colorBlue = Color.Blue; + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var obfuscateContainer = new ObfuscateContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + obfuscateContainer.SetFieldValue(FieldType.LINE_THICKNESS, 3); + obfuscateContainer.SetFieldValue(FieldType.LINE_COLOR, colorBlue); + obfuscateContainer.SetFieldValue(FieldType.SHADOW, true); + obfuscateContainer.SetFieldValue(FieldType.PREPARED_FILTER_OBFUSCATE, PreparedFilter.BLUR); + + // Act + var result = ConvertDomainToDto.ToDto(obfuscateContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + var resultPreparedFilter = DtoHelper.GetFieldValue(result, FieldType.PREPARED_FILTER_OBFUSCATE); + + Assert.NotNull(result); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3,resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.True((bool)resultShadow); + + Assert.NotNull(resultPreparedFilter); + Assert.IsType(resultPreparedFilter); + Assert.Equal(PreparedFilter.BLUR, resultPreparedFilter); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertRectangleContainerToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertRectangleContainerToDtoTests.cs new file mode 100644 index 000000000..cd7d65ed2 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertRectangleContainerToDtoTests.cs @@ -0,0 +1,135 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertRectangleContainerToDtoTests +{ + [Fact] + public void ConvertDomainToDto_RectangleContainer_Returns_RectangleContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var rectangleContainer = new RectangleContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + // see RectangleContainer.InitializeFields() for defaults + var defaultLineThickness = 2; + var defaultLineColor = Color.Red; + var defaultFillColor = Color.Transparent; + var defaultShadow = true; + + // Act + var result = ConvertDomainToDto.ToDto(rectangleContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultFillColor = DtoHelper.GetFieldValue(result, FieldType.FILL_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + + Assert.NotNull(result); + Assert.Equal(rectangleContainer.Left, result.Left); + Assert.Equal(rectangleContainer.Top, result.Top); + Assert.Equal(rectangleContainer.Width, result.Width); + Assert.Equal(rectangleContainer.Height, result.Height); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(defaultShadow,(bool)resultShadow); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(defaultLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(defaultLineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(defaultFillColor, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + } + + [Fact] + public void ConvertDomainToDto_RectangleContainer_with_Field_Values_Returns_RectangleContainerDto_with_same_Values() + { + // Arrange + var colorBlue = Color.Blue; + var colorGreen = Color.Green; + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var rectangleContainer = new RectangleContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + rectangleContainer.SetFieldValue(FieldType.LINE_THICKNESS, 3); + rectangleContainer.SetFieldValue(FieldType.LINE_COLOR, colorBlue); + rectangleContainer.SetFieldValue(FieldType.FILL_COLOR, colorGreen); + rectangleContainer.SetFieldValue(FieldType.SHADOW, false); + + // Act + var result = ConvertDomainToDto.ToDto(rectangleContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultFillColor = DtoHelper.GetFieldValue(result, FieldType.FILL_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + + Assert.NotNull(result); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3,resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(colorGreen, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorGreen)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertSpeechbubbleContainerToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertSpeechbubbleContainerToDtoTests.cs new file mode 100644 index 000000000..8fbd02c2a --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertSpeechbubbleContainerToDtoTests.cs @@ -0,0 +1,223 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertSpeechbubbleContainerToDtoTests +{ + [Fact] + public void ConvertDomainToDto_SpeechbubbleContainer_Returns_SpeechbubbleContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var speechbubbleContainer = new SpeechbubbleContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Text = "Hello, greenshot!", + StoredTargetGripperLocation = new Point(30, 40) + }; + // see SpeechbubbleContainer.InitializeFields() for defaults + var defaultLineThickness = 2; + var defaultLineColor = Color.Blue; + var defaultFillColor = Color.White; + var defaultShadow = false; + var defaultFontItalic = false; + var defaultFontBold = true; + var defaultFontFamily = FontFamily.GenericSansSerif.Name; + var defaultFontSize = 20f; + var defaultTextHorizontalAlignment = StringAlignment.Center; + var defaultTextVerticalAlignment = StringAlignment.Center; + + // Act + var result = ConvertDomainToDto.ToDto(speechbubbleContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultFillColor = DtoHelper.GetFieldValue(result, FieldType.FILL_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + var resultFontItalic = DtoHelper.GetFieldValue(result, FieldType.FONT_ITALIC); + var resultFontBold = DtoHelper.GetFieldValue(result, FieldType.FONT_BOLD); + var resultFontFamily = DtoHelper.GetFieldValue(result, FieldType.FONT_FAMILY); + var resultFontSize = DtoHelper.GetFieldValue(result, FieldType.FONT_SIZE); + var resultTextHorizontalAlignment = DtoHelper.GetFieldValue(result, FieldType.TEXT_HORIZONTAL_ALIGNMENT); + var resultTextVerticalAlignment = DtoHelper.GetFieldValue(result, FieldType.TEXT_VERTICAL_ALIGNMENT); + + Assert.NotNull(result); + Assert.Equal(speechbubbleContainer.Left, result.Left); + Assert.Equal(speechbubbleContainer.Top, result.Top); + Assert.Equal(speechbubbleContainer.Width, result.Width); + Assert.Equal(speechbubbleContainer.Height, result.Height); + Assert.Equal(speechbubbleContainer.Text, ((SpeechbubbleContainerDto)result).Text); + Assert.Equal(speechbubbleContainer.StoredTargetGripperLocation.X, ((SpeechbubbleContainerDto)result).StoredTargetGripperLocation.X); + Assert.Equal(speechbubbleContainer.StoredTargetGripperLocation.Y, ((SpeechbubbleContainerDto)result).StoredTargetGripperLocation.Y); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(defaultLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(defaultLineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(defaultFillColor, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(defaultShadow, (bool)resultShadow); + + Assert.NotNull(resultFontItalic); + Assert.IsType(resultFontItalic); + Assert.Equal(defaultFontItalic, (bool)resultFontItalic); + + Assert.NotNull(resultFontBold); + Assert.IsType(resultFontBold); + Assert.Equal(defaultFontBold, (bool)resultFontBold); + + Assert.NotNull(resultFontFamily); + Assert.IsType(resultFontFamily); + Assert.Equal(defaultFontFamily, resultFontFamily); + + Assert.NotNull(resultFontSize); + Assert.IsType(resultFontSize); + Assert.Equal(defaultFontSize, resultFontSize); + + Assert.NotNull(resultTextHorizontalAlignment); + Assert.IsType(resultTextHorizontalAlignment); + Assert.Equal(defaultTextHorizontalAlignment, resultTextHorizontalAlignment); + + Assert.NotNull(resultTextVerticalAlignment); + Assert.IsType(resultTextVerticalAlignment); + Assert.Equal(defaultTextVerticalAlignment, resultTextVerticalAlignment); + } + + [Fact] + public void ConvertDomainToDto_SpeechbubbleContainer_with_Field_Values_Returns_SpeechbubbleContainerDto_with_same_Values() + { + // Arrange + var colorBlue = Color.Blue; + var colorWhite = Color.White; + var fontFamily = FontFamily.GenericSansSerif.Name; + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var speechbubbleContainer = new SpeechbubbleContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Text = "Hello, greenshot!", + StoredTargetGripperLocation = new Point(30, 40) + }; + speechbubbleContainer.SetFieldValue(FieldType.LINE_THICKNESS, 2); + speechbubbleContainer.SetFieldValue(FieldType.LINE_COLOR, colorBlue); + speechbubbleContainer.SetFieldValue(FieldType.FILL_COLOR, colorWhite); + speechbubbleContainer.SetFieldValue(FieldType.SHADOW, false); + speechbubbleContainer.SetFieldValue(FieldType.FONT_ITALIC, false); + speechbubbleContainer.SetFieldValue(FieldType.FONT_BOLD, true); + speechbubbleContainer.SetFieldValue(FieldType.FONT_FAMILY, fontFamily); + speechbubbleContainer.SetFieldValue(FieldType.FONT_SIZE, 20f); + speechbubbleContainer.SetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT, StringAlignment.Center); + speechbubbleContainer.SetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT, StringAlignment.Center); + + // Act + var result = ConvertDomainToDto.ToDto(speechbubbleContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultFillColor = DtoHelper.GetFieldValue(result, FieldType.FILL_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + var resultFontItalic = DtoHelper.GetFieldValue(result, FieldType.FONT_ITALIC); + var resultFontBold = DtoHelper.GetFieldValue(result, FieldType.FONT_BOLD); + var resultFontFamily = DtoHelper.GetFieldValue(result, FieldType.FONT_FAMILY); + var resultFontSize = DtoHelper.GetFieldValue(result, FieldType.FONT_SIZE); + var resultTextHorizontalAlignment = DtoHelper.GetFieldValue(result, FieldType.TEXT_HORIZONTAL_ALIGNMENT); + var resultTextVerticalAlignment = DtoHelper.GetFieldValue(result, FieldType.TEXT_VERTICAL_ALIGNMENT); + + Assert.NotNull(result); + Assert.Equal(speechbubbleContainer.Left, result.Left); + Assert.Equal(speechbubbleContainer.Top, result.Top); + Assert.Equal(speechbubbleContainer.Width, result.Width); + Assert.Equal(speechbubbleContainer.Height, result.Height); + Assert.Equal(speechbubbleContainer.Text, ((SpeechbubbleContainerDto)result).Text); + Assert.Equal(speechbubbleContainer.StoredTargetGripperLocation.X, ((SpeechbubbleContainerDto)result).StoredTargetGripperLocation.X); + Assert.Equal(speechbubbleContainer.StoredTargetGripperLocation.Y, ((SpeechbubbleContainerDto)result).StoredTargetGripperLocation.Y); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(2,resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(colorWhite, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorWhite)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); + + Assert.NotNull(resultFontItalic); + Assert.IsType(resultFontItalic); + Assert.False((bool)resultFontItalic); + + Assert.NotNull(resultFontBold); + Assert.IsType(resultFontBold); + Assert.True((bool)resultFontBold); + + Assert.NotNull(resultFontFamily); + Assert.IsType(resultFontFamily); + Assert.Equal(fontFamily, resultFontFamily); + + Assert.NotNull(resultFontSize); + Assert.IsType(resultFontSize); + Assert.Equal(20f, resultFontSize); + + Assert.NotNull(resultTextHorizontalAlignment); + Assert.IsType(resultTextHorizontalAlignment); + Assert.Equal(StringAlignment.Center, resultTextHorizontalAlignment); + + Assert.NotNull(resultTextVerticalAlignment); + Assert.IsType(resultTextVerticalAlignment); + Assert.Equal(StringAlignment.Center, resultTextVerticalAlignment); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertStepLabelContainerToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertStepLabelContainerToDtoTests.cs new file mode 100644 index 000000000..5f5aef53b --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertStepLabelContainerToDtoTests.cs @@ -0,0 +1,138 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertStepLabelContainerToDtoTests +{ + [Fact] + public void ConvertDomainToDto_StepLabelContainer_Returns_StepLabelContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var stepLabelContainer = new StepLabelContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Number = 2, + CounterStart = 1 + }; + // see StepLabelContainer.InitializeFields() for defaults + var defaultFillColor = Color.DarkRed; + var defaultLineColor = Color.White; + var defaultFlags = FieldFlag.COUNTER; + + // Act + var result = ConvertDomainToDto.ToDto(stepLabelContainer); + + // Assert + var resultFillColor = DtoHelper.GetFieldValue(result, FieldType.FILL_COLOR); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultFlags = DtoHelper.GetFieldValue(result, FieldType.FLAGS); + + Assert.NotNull(result); + Assert.Equal(stepLabelContainer.Left, result.Left); + Assert.Equal(stepLabelContainer.Top, result.Top); + Assert.Equal(stepLabelContainer.Width, result.Width); + Assert.Equal(stepLabelContainer.Height, result.Height); + Assert.Equal(stepLabelContainer.Number, ((StepLabelContainerDto)result).Number); + Assert.Equal(stepLabelContainer.CounterStart, ((StepLabelContainerDto)result).CounterStart); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(defaultFillColor, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(defaultLineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFlags); + Assert.IsType(resultFlags); + Assert.Equal(defaultFlags, (FieldFlag)resultFlags); + } + + [Fact] + public void ConvertDomainToDto_StepLabelContainer_with_Field_Values_Returns_StepLabelContainerDto_with_same_Values() + { + // Arrange + var colorBlue = Color.Blue; + var colorGreen = Color.Green; + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var stepLabelContainer = new StepLabelContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Number = 5, + CounterStart = 3 + }; + stepLabelContainer.SetFieldValue(FieldType.FILL_COLOR, colorBlue); + stepLabelContainer.SetFieldValue(FieldType.LINE_COLOR, colorGreen); + // stays the same as default + stepLabelContainer.SetFieldValue(FieldType.FLAGS , FieldFlag.COUNTER); + + // Act + var result = ConvertDomainToDto.ToDto(stepLabelContainer); + + // Assert + var resultFillColor = DtoHelper.GetFieldValue(result, FieldType.FILL_COLOR); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultFlags = DtoHelper.GetFieldValue(result, FieldType.FLAGS); + + Assert.NotNull(result); + Assert.Equal(stepLabelContainer.Left, result.Left); + Assert.Equal(stepLabelContainer.Top, result.Top); + Assert.Equal(stepLabelContainer.Width, result.Width); + Assert.Equal(stepLabelContainer.Height, result.Height); + Assert.Equal(stepLabelContainer.Number, ((StepLabelContainerDto)result).Number); + Assert.Equal(stepLabelContainer.CounterStart, ((StepLabelContainerDto)result).CounterStart); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorGreen, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorGreen)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFlags); + Assert.IsType(resultFlags); + Assert.Equal(FieldFlag.COUNTER, (FieldFlag)resultFlags); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertSvgContainerToDtoTest.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertSvgContainerToDtoTest.cs new file mode 100644 index 000000000..19acf1b4c --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertSvgContainerToDtoTest.cs @@ -0,0 +1,58 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.IO; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertSvgContainerToDtoTest +{ + [Fact] + public void ConvertDomainToDto_SvgContainer_Returns_SvgContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var svgFilePath = Path.Combine("TestData", "Images", "Logo_G_with_Border.svg"); + using var svgStream = File.OpenRead(svgFilePath); + + var svgContainer = new SvgContainer(svgStream, surface); + + // Act + var result = ConvertDomainToDto.ToDto(svgContainer); + + // Assert + Assert.NotNull(result); + Assert.Equal(svgContainer.Left, result.Left); + Assert.Equal(svgContainer.Top, result.Top); + Assert.Equal(svgContainer.Width, result.Width); + Assert.Equal(svgContainer.Height, result.Height); + + // Ensure the SVG was serialized + Assert.NotNull(result.SvgData); + Assert.True(result.SvgData.Length > 0); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertTextContainerToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertTextContainerToDtoTests.cs new file mode 100644 index 000000000..f492c8284 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertTextContainerToDtoTests.cs @@ -0,0 +1,216 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +[Collection("DefaultCollection")] +public class ConvertTextContainerToDtoTests +{ + [Fact] + public void ConvertDomainToDto_TextContainer_Returns_TextContainerDto() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var textContainer = new TextContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Text = "Hello, greenshot!" + }; + + // see TextContainer.InitializeFields() for defaults + var defaultLineThickness = 2; + var defaultLineColor = Color.Red; + var defaultShadow = true; + var defaultFontItalic = false; + var defaultFontBold = false; + var defaultFillColor = Color.Transparent; + var defaultFontFamily = FontFamily.GenericSansSerif.Name; + var defaultFontSize = 11f; + var defaultTextHorizontalAlignment = StringAlignment.Center; + var defaultTextVerticalAlignment = StringAlignment.Center; + + // Act + var result = ConvertDomainToDto.ToDto(textContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + var resultFontItalic = DtoHelper.GetFieldValue(result, FieldType.FONT_ITALIC); + var resultFontBold = DtoHelper.GetFieldValue(result, FieldType.FONT_BOLD); + var resultFillColor = DtoHelper.GetFieldValue(result, FieldType.FILL_COLOR); + var resultFontFamily = DtoHelper.GetFieldValue(result, FieldType.FONT_FAMILY); + var resultFontSize = DtoHelper.GetFieldValue(result, FieldType.FONT_SIZE); + var resultTextHorizontalAlignment = DtoHelper.GetFieldValue(result, FieldType.TEXT_HORIZONTAL_ALIGNMENT); + var resultTextVerticalAlignment = DtoHelper.GetFieldValue(result, FieldType.TEXT_VERTICAL_ALIGNMENT); + + Assert.NotNull(result); + Assert.Equal(textContainer.Left, result.Left); + Assert.Equal(textContainer.Top, result.Top); + Assert.Equal(textContainer.Width, result.Width); + Assert.Equal(textContainer.Height, result.Height); + Assert.Equal(textContainer.Text, ((TextContainerDto)result).Text); + + //Test Workaround also + Assert.Equal(textContainer.Text, textContainer.Text); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(defaultLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(defaultLineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(defaultShadow, (bool)resultShadow); + + Assert.NotNull(resultFontItalic); + Assert.IsType(resultFontItalic); + Assert.Equal(defaultFontItalic, (bool)resultFontItalic); + + Assert.NotNull(resultFontBold); + Assert.IsType(resultFontBold); + Assert.Equal(defaultFontBold, (bool)resultFontBold); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(defaultFillColor, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(defaultFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultFontFamily); + Assert.IsType(resultFontFamily); + Assert.Equal(defaultFontFamily, resultFontFamily); + + Assert.NotNull(resultFontSize); + Assert.IsType(resultFontSize); + Assert.Equal(defaultFontSize, resultFontSize); + + Assert.NotNull(resultTextHorizontalAlignment); + Assert.IsType(resultTextHorizontalAlignment); + Assert.Equal(defaultTextHorizontalAlignment, resultTextHorizontalAlignment); + + Assert.NotNull(resultTextVerticalAlignment); + Assert.IsType(resultTextVerticalAlignment); + Assert.Equal(defaultTextVerticalAlignment, resultTextVerticalAlignment); + } + + [Fact] + public void ConvertDomainToDto_TextContainer_with_Field_Values_Returns_TextContainerDto_with_same_Values() + { + // Arrange + var colorBlue = Color.Blue; + var colorGreen = Color.Green; + var fontFamilyName = "Arial"; + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var textContainer = new TextContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Text = "Hello, greenshot!" + }; + textContainer.SetFieldValue(FieldType.LINE_THICKNESS, 3); + textContainer.SetFieldValue(FieldType.LINE_COLOR, colorBlue); + textContainer.SetFieldValue(FieldType.SHADOW, false); + textContainer.SetFieldValue(FieldType.FONT_ITALIC, true); + textContainer.SetFieldValue(FieldType.FONT_BOLD, true); + textContainer.SetFieldValue(FieldType.FILL_COLOR, colorGreen); + textContainer.SetFieldValue(FieldType.FONT_FAMILY, fontFamilyName); + textContainer.SetFieldValue(FieldType.FONT_SIZE, 12f); + textContainer.SetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT, StringAlignment.Far); + textContainer.SetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT, StringAlignment.Far); + + // Act + var result = ConvertDomainToDto.ToDto(textContainer); + + // Assert + var resultLineThickness = DtoHelper.GetFieldValue(result, FieldType.LINE_THICKNESS); + var resultLineColor = DtoHelper.GetFieldValue(result, FieldType.LINE_COLOR); + var resultShadow = DtoHelper.GetFieldValue(result, FieldType.SHADOW); + var resultFontItalic = DtoHelper.GetFieldValue(result, FieldType.FONT_ITALIC); + var resultFontBold = DtoHelper.GetFieldValue(result, FieldType.FONT_BOLD); + var resultFillColor = DtoHelper.GetFieldValue(result, FieldType.FILL_COLOR); + var resultFontFamily = DtoHelper.GetFieldValue(result, FieldType.FONT_FAMILY); + var resultFontSize = DtoHelper.GetFieldValue(result, FieldType.FONT_SIZE); + var resultTextHorizontalAlignment = DtoHelper.GetFieldValue(result, FieldType.TEXT_HORIZONTAL_ALIGNMENT); + var resultTextVerticalAlignment = DtoHelper.GetFieldValue(result, FieldType.TEXT_VERTICAL_ALIGNMENT); + + Assert.NotNull(result); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); + + Assert.NotNull(resultFontItalic); + Assert.IsType(resultFontItalic); + Assert.True((bool)resultFontItalic); + + Assert.NotNull(resultFontBold); + Assert.IsType(resultFontBold); + Assert.True((bool)resultFontBold); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(colorGreen, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorGreen)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultFontFamily); + Assert.IsType(resultFontFamily); + Assert.Equal(fontFamilyName, resultFontFamily); + + Assert.NotNull(resultFontSize); + Assert.IsType(resultFontSize); + Assert.Equal(12f, resultFontSize); + + Assert.NotNull(resultTextHorizontalAlignment); + Assert.IsType(resultTextHorizontalAlignment); + Assert.Equal(StringAlignment.Far, resultTextHorizontalAlignment); + + Assert.NotNull(resultTextVerticalAlignment); + Assert.IsType(resultTextVerticalAlignment); + Assert.Equal(StringAlignment.Far, resultTextVerticalAlignment); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertValueToDtoTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertValueToDtoTests.cs new file mode 100644 index 000000000..400ba75ea --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DomainToDto/ConvertValueToDtoTests.cs @@ -0,0 +1,115 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DomainToDto; + +public class ConvertValueToDtoTests +{ + /// + /// A collectioan of all supported value types for . + /// + public static IEnumerable GetValueTestData() + { + yield return [typeof(int), typeof(IntFieldValueDto), 42]; + yield return [typeof(string), typeof(StringFieldValueDto), "test"]; + yield return [typeof(bool), typeof(BoolFieldValueDto), true]; + yield return [typeof(float), typeof(SingleFieldValueDto), 3.14f]; + yield return [typeof(double), typeof(DoubleFieldValueDto), 3.14d]; + yield return [typeof(decimal), typeof(DecimalFieldValueDto), 3.14m]; + yield return [typeof(ArrowContainer.ArrowHeadCombination), typeof(ArrowHeadCombinationFieldValueDto), ArrowContainer.ArrowHeadCombination.END_POINT]; + yield return [typeof(FilterContainer.PreparedFilter), typeof(PreparedFilterFieldValueDto), FilterContainer.PreparedFilter.AREA_HIGHLIGHT]; + yield return [typeof(StringAlignment), typeof(StringAlignmentFieldValueDto), StringAlignment.Center]; + yield return [typeof(FieldFlag), typeof(FieldFlagFieldValueDto), FieldFlag.CONFIRMABLE]; + } + + /// + /// This test ensures that the method produces + /// a DTO of the corresponding type and that the DTO encapsulates the original domain value correctly. + /// + [Theory] + [MemberData(nameof(GetValueTestData))] + public void ConvertValueToDto_DomainType_Returns_FieldValueDtoType(Type domainValueType, Type dtoType, object value) + { + // Act + var result = ConvertDomainToDto.ConvertValueToDto(value); + + // Assert + Assert.IsType(dtoType, result); + Assert.IsType(domainValueType, ((FieldValueDto)result).GetValue()); + Assert.Equal(value, ((FieldValueDto)result).GetValue()); + } + + /// + /// Tests that the method correctly converts a value into an instance of . + /// + /// It's a dedicated test because needs a special compare. + [Fact] + public void ConvertValueToDto_Color_Returns_ColorFieldValueDtoType() + { + // Arrange + Color value = Color.Red; + + // Act + var result = ConvertDomainToDto.ConvertValueToDto(value); + + // Assert + Assert.IsType(result); + Assert.IsType(((ColorFieldValueDto)result).GetValue()); + var resultColorValue = (Color)((ColorFieldValueDto)result).GetValue(); + + // special compare, because we only store the ARGB value + Assert.True(DtoHelper.CompareColorValue(value, resultColorValue), + $"The color values are different. expected:{DtoHelper.ArgbString(value)} result:{DtoHelper.ArgbString(resultColorValue)}"); + } + + [Fact] + public void ConvertValueToDto_NullValue_ReturnsNullFieldValue() + { + // Arrange + object value = null; + + // Act + // ReSharper disable once ExpressionIsAlwaysNull + var result = ConvertDomainToDto.ConvertValueToDto(value); + + // Assert + Assert.IsType(result); + } + + [Fact] + public void ConvertValueToDto_UnsupportedType_ThrowsArgumentException() + { + // Arrange + var value = new object(); + + // Act & Assert + Assert.Throws(() => ConvertDomainToDto.ConvertValueToDto(value)); + } +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToApplicationFileTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToApplicationFileTests.cs new file mode 100644 index 000000000..a8e215e69 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToApplicationFileTests.cs @@ -0,0 +1,93 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Collections.Generic; +using System.Drawing; +using System.IO; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToApplicationFileTests +{ + [Fact] + public void ConvertDtoToDomain_ApplicationFileDto_Returns_ApplicationFile() + { + // Arrange + var lineDto = new LineContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + var rectangleDto = new RectangleContainerDto + { + Left = 30, + Top = 40, + Width = 200, + Height = 80 + }; + var dtoList = new DrawableContainerListDto + { + ContainerList = [lineDto, rectangleDto] + }; + var image = new Bitmap(100, 100); // Create a sample image + byte[] imageData; + using (var memoryStream = new MemoryStream()) + { + image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png); + imageData = memoryStream.ToArray(); + } + var dto = new GreenshotFileDto + { + ContainerList = dtoList, + Image = imageData, + SchemaVersion = GreenshotFileVersionHandler.CurrentSchemaVersion + }; + + // Act + var domain = ConvertDtoToDomain.ToDomain(dto); + + // Assert + Assert.NotNull(domain); + Assert.Equal(dto.SchemaVersion, domain.SchemaVersion); + Assert.NotNull(domain.Image); + Assert.NotNull(domain.ContainerList); + Assert.Equal(2, domain.ContainerList.Count); + Assert.IsType(domain.ContainerList[0]); + Assert.IsType(domain.ContainerList[1]); + + Assert.Equal(lineDto.Top, domain.ContainerList[0].Top); + Assert.Equal(lineDto.Left, domain.ContainerList[0].Left); + Assert.Equal(lineDto.Width, domain.ContainerList[0].Width); + Assert.Equal(lineDto.Height, domain.ContainerList[0].Height); + + Assert.Equal(rectangleDto.Top, domain.ContainerList[1].Top); + Assert.Equal(rectangleDto.Left, domain.ContainerList[1].Left); + Assert.Equal(rectangleDto.Width, domain.ContainerList[1].Width); + Assert.Equal(rectangleDto.Height, domain.ContainerList[1].Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToArrowContainerTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToArrowContainerTests.cs new file mode 100644 index 000000000..5a43ac20d --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToArrowContainerTests.cs @@ -0,0 +1,133 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; +using static Greenshot.Editor.Drawing.ArrowContainer; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToArrowContainerTests +{ + [Fact] + public void ConvertDtoToDomain_ArrowContainerDto_Returns_ArrowContainer() + { + // Arrange + var colorBlue = Color.Blue; + var colorGreen = Color.Green; + var dto = new ArrowContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Fields = [ new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_THICKNESS), + Scope = nameof(ArrowContainer), + Value = new IntFieldValueDto + { + Value = 3 // default in ArrowContainer is 2 + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_COLOR), + Scope = nameof(ArrowContainer), + Value = new ColorFieldValueDto + { + Value = colorBlue // default in ArrowContainer is Red + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FILL_COLOR), + Scope = nameof(ArrowContainer), + Value = new ColorFieldValueDto + { + Value = colorGreen // default in ArrowContainer is Transparent + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.SHADOW), + Scope = nameof(ArrowContainer), + Value = new BoolFieldValueDto + { + Value = false // default in ArrowContainer is true + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.ARROWHEADS), + Scope = nameof(ArrowContainer), + Value = new ArrowHeadCombinationFieldValueDto + { + Value = ArrowHeadCombination.BOTH // default in ArrowContainer is END_POINT + } + } + ] + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto,null); + + // Assert + var resultLineThickness = result.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = result.GetFieldValue(FieldType.LINE_COLOR); + var resultFillColor = result.GetFieldValue(FieldType.FILL_COLOR); + var resultShadow = result.GetFieldValue(FieldType.SHADOW); + var resultArrowHeads = result.GetFieldValue(FieldType.ARROWHEADS); + + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(colorGreen, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorGreen)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); + + Assert.NotNull(resultArrowHeads); + Assert.IsType(resultArrowHeads); + Assert.Equal(ArrowHeadCombination.BOTH, resultArrowHeads); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToCursorContainerTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToCursorContainerTests.cs new file mode 100644 index 000000000..5d8b44556 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToCursorContainerTests.cs @@ -0,0 +1,52 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToCursorContainerTests +{ + [Fact] + public void ConvertDtoToDomain_CursorContainerDto_Returns_CursorContainer() + { + // Arrange + var dto = new CursorContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToDrawableContainerListTest.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToDrawableContainerListTest.cs new file mode 100644 index 000000000..7defca2d8 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToDrawableContainerListTest.cs @@ -0,0 +1,74 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Collections.Generic; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToDrawableContainerListTest +{ + [Fact] + public void ConvertDtoToDomain_DrawableContainerListDto_Returns_DrawableContainerList() + { + // Arrange + var lineDto = new LineContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + var rectangleDto = new RectangleContainerDto + { + Left = 30, + Top = 40, + Width = 200, + Height = 80 + }; + var dtoList = new DrawableContainerListDto + { + ContainerList = [lineDto, rectangleDto] + }; + + // Act + var domainList = ConvertDtoToDomain.ToDomain(dtoList); + + // Assert + Assert.NotNull(domainList); + Assert.Equal(2, domainList.Count); + Assert.IsType(domainList[0]); + Assert.IsType(domainList[1]); + + Assert.Equal(lineDto.Top, domainList[0].Top); + Assert.Equal(lineDto.Left, domainList[0].Left); + Assert.Equal(lineDto.Width, domainList[0].Width); + Assert.Equal(lineDto.Height, domainList[0].Height); + + Assert.Equal(rectangleDto.Top, domainList[1].Top); + Assert.Equal(rectangleDto.Left, domainList[1].Left); + Assert.Equal(rectangleDto.Width, domainList[1].Width); + Assert.Equal(rectangleDto.Height, domainList[1].Height); + } +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToEllipseContainerTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToEllipseContainerTests.cs new file mode 100644 index 000000000..9f1a7ee56 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToEllipseContainerTests.cs @@ -0,0 +1,118 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToEllipseContainerTests +{ + [Fact] + public void ConvertDtoToDomain_EllipseContainerDto_Returns_EllipseContainer() + { + // Arrange + var colorBlue = Color.Blue; + var colorGreen = Color.Green; + var dto = new EllipseContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Fields = [ new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_THICKNESS), + Scope = nameof(EllipseContainer), + Value = new IntFieldValueDto + { + Value = 3 // default in EllipseContainer is 2 + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_COLOR), + Scope = nameof(EllipseContainer), + Value = new ColorFieldValueDto + { + Value = colorBlue // default in EllipseContainer is Red + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FILL_COLOR), + Scope = nameof(EllipseContainer), + Value = new ColorFieldValueDto + { + Value = colorGreen // default in EllipseContainer is Transparent + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.SHADOW), + Scope = nameof(EllipseContainer), + Value = new BoolFieldValueDto + { + Value = false // default in EllipseContainer is true + } + } + ] + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + var resultLineThickness = result.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = result.GetFieldValue(FieldType.LINE_COLOR); + var resultFillColor = result.GetFieldValue(FieldType.FILL_COLOR); + var resultShadow = result.GetFieldValue(FieldType.SHADOW); + + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(colorGreen, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorGreen)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToFieldTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToFieldTests.cs new file mode 100644 index 000000000..59747d360 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToFieldTests.cs @@ -0,0 +1,106 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; +using static Greenshot.Editor.Drawing.ArrowContainer; +using static Greenshot.Editor.Drawing.FilterContainer; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +public class ConvertDtoToFieldTests +{ + /// + /// A collectioan of all supported value types for . + /// + public static IEnumerable GetFieldValueTestData() + { + yield return [FieldType.FONT_BOLD, typeof(bool), new BoolFieldValueDto { Value = true }, true]; + yield return [FieldType.FONT_FAMILY, typeof(string), new StringFieldValueDto { Value = "Arial" }, "Arial"]; + yield return [FieldType.LINE_THICKNESS, typeof(int), new IntFieldValueDto { Value = 42 }, 42]; + yield return [FieldType.PREVIEW_QUALITY, typeof(float), new SingleFieldValueDto { Value = 3.14f }, 3.14f]; + yield return [FieldType.PREVIEW_QUALITY, typeof(double), new DoubleFieldValueDto { Value = 3.14d }, 3.14d]; + yield return [FieldType.PREVIEW_QUALITY, typeof(decimal), new DecimalFieldValueDto { Value = 3.14m }, 3.14m]; + yield return [FieldType.PREVIEW_QUALITY, typeof(ArrowHeadCombination), new ArrowHeadCombinationFieldValueDto { Value = ArrowHeadCombination.END_POINT }, ArrowHeadCombination.END_POINT]; + yield return [FieldType.PREPARED_FILTER_HIGHLIGHT, typeof(PreparedFilter), new PreparedFilterFieldValueDto { Value = PreparedFilter.AREA_HIGHLIGHT }, PreparedFilter.AREA_HIGHLIGHT]; + yield return [FieldType.TEXT_HORIZONTAL_ALIGNMENT, typeof(StringAlignment), new StringAlignmentFieldValueDto { Value = StringAlignment.Center }, StringAlignment.Center]; + yield return [FieldType.FLAGS, typeof(FieldFlag), new FieldFlagFieldValueDto { Value = FieldFlag.CONFIRMABLE }, FieldFlag.CONFIRMABLE]; + } + + /// + /// This test verifies that the method correctly maps + /// the properties of a to a object. + /// + [Theory] + [MemberData(nameof(GetFieldValueTestData))] + public void ConvertDTOToDomain_FieldDto_Returns_Field(IFieldType field, Type valueType, FieldValueDto dto, object value) + { + // Arrange + var fieldDto = new FieldDto + { + FieldTypeName = field.Name, + Scope = "TestScope", + Value = dto + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(fieldDto); + + // Assert + Assert.NotNull(result); + Assert.Equal(field.Name, result.FieldType.Name); + Assert.Equal("TestScope", result.Scope); + Assert.IsType(valueType, result.Value); + Assert.Equal(value, result.Value); + } + + [Fact] + public void ConvertDTOToDomain_ColorFieldValueDto_ReturnsColorField() + { + // Arrange + var redColor = Color.Red; + var dto = new ColorFieldValueDto { Value = redColor }; + var fieldDto = new FieldDto + { + FieldTypeName = FieldType.FILL_COLOR.Name, + Scope = "TestScope", + Value = dto + }; + // Act + var result = ConvertDtoToDomain.ToDomain(fieldDto); + + // Assert + Assert.NotNull(result); + Assert.Equal(FieldType.FILL_COLOR.Name, result.FieldType.Name); + Assert.Equal("TestScope", result.Scope); + + Assert.IsType(result.Value); + // special compare, because we only store the ARGB value + Assert.True(DtoHelper.CompareColorValue(redColor, (Color)result.Value), + $"The color values are different. expected:{DtoHelper.ArgbString(redColor)} result:{DtoHelper.ArgbString((Color)result.Value)}"); + } + +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToFreehandContainerTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToFreehandContainerTests.cs new file mode 100644 index 000000000..a77b353b7 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToFreehandContainerTests.cs @@ -0,0 +1,97 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Collections.Generic; +using System.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToFreehandContainerTests +{ + [Fact] + public void ConvertDtoToDomain_FreehandContainerDto_Returns_FreehandContainer() + { + // Arrange + var colorRed = System.Drawing.Color.Red; + var dto = new FreehandContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + CapturePoints = [new PointDto { X = 10, Y = 20 }, new PointDto { X = 30, Y = 40 }], + Fields = [ new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_THICKNESS), + Scope = nameof(FreehandContainer), + Value = new IntFieldValueDto + { + Value = 2 // default in FreehandContainer is 3 + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_COLOR), + Scope = nameof(FreehandContainer), + Value = new ColorFieldValueDto + { + Value = colorRed // default in FreehandContainer is Red + } + }] + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + var resultLineThickness = result.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = result.GetFieldValue(FieldType.LINE_COLOR); + + Assert.NotNull(result); + Assert.IsType(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + Assert.NotNull(result.CapturePoints); + Assert.Equal(dto.CapturePoints.Count, result.CapturePoints.Count); + Assert.Equal(dto.CapturePoints[0].X, result.CapturePoints[0].X); + Assert.Equal(dto.CapturePoints[0].Y, result.CapturePoints[0].Y); + Assert.Equal(dto.CapturePoints[1].X, result.CapturePoints[1].X); + Assert.Equal(dto.CapturePoints[1].Y, result.CapturePoints[1].Y); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(2, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorRed, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorRed)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToHighlightContainerTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToHighlightContainerTests.cs new file mode 100644 index 000000000..21de71aa0 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToHighlightContainerTests.cs @@ -0,0 +1,128 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.Linq; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Drawing.Filters; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; +using static Greenshot.Editor.Drawing.FilterContainer; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToHighlightContainerTests +{ + [Fact] + public void ConvertDtoToDomain_HighlightContainerDto_Returns_HighlightContainer() + { + // Arrange + var colorBlue = Color.Blue; + var dto = new HighlightContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Fields = [ new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_THICKNESS), + Scope = nameof(HighlightContainer), + Value = new IntFieldValueDto + { + Value = 3 // default in HighlightContainer is 0 + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_COLOR), + Scope = nameof(HighlightContainer), + Value = new ColorFieldValueDto + { + Value = colorBlue // default in HighlightContainer is Red + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.SHADOW), + Scope = nameof(HighlightContainer), + Value = new BoolFieldValueDto + { + Value = true // default in HighlightContainer is false + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.PREPARED_FILTER_HIGHLIGHT), + Scope = nameof(HighlightContainer), + Value = new PreparedFilterFieldValueDto + { + Value = PreparedFilter.AREA_HIGHLIGHT // default in HighlightContainer is TEXT_HIGHTLIGHT + } + } + ] + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + var resultLineThickness = result.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = result.GetFieldValue(FieldType.LINE_COLOR); + var resultShadow = result.GetFieldValue(FieldType.SHADOW); + var resultPreparedFilter = result.GetFieldValue(FieldType.PREPARED_FILTER_HIGHLIGHT); + var allFilter = result.Filters; + var brigthnessFilter = allFilter.OfType().FirstOrDefault(); + var blurFilter = allFilter.OfType().FirstOrDefault(); + + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.True((bool)resultShadow); + + Assert.NotNull(resultPreparedFilter); + Assert.IsType(resultPreparedFilter); + Assert.Equal(PreparedFilter.AREA_HIGHLIGHT, resultPreparedFilter); + + // PreparedFilter.AREA_HIGHLIGHT should add BrightnessFilter and BlurFilter + Assert.NotNull(allFilter); + Assert.Equal(2,allFilter?.Count); + Assert.NotNull(brigthnessFilter); + Assert.NotNull(blurFilter); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToIconContainerTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToIconContainerTests.cs new file mode 100644 index 000000000..2980e6fe9 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToIconContainerTests.cs @@ -0,0 +1,66 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.IO; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToIconContainerTests +{ + [Fact] + public void ConvertDtoToDomain_IconContainerDto_Returns_IconContainer() + { + // Arrange + var iconPath = Path.Combine("TestData", "Images", "Greenshot.ico"); + using var iconStream = File.OpenRead(iconPath); + using var memoryStream = new MemoryStream(); + iconStream.CopyTo(memoryStream); + var iconBytes = memoryStream.ToArray(); + // Default Icon size, chosen by System.Drawing.Icon ctor(stream) in ToDomain() + var defaultIconSize = new { Width = 32, Height = 32 }; + + var dto = new IconContainerDto + { + Left = 10, + Top = 20, + Width = 222, // different width for testing, Icon is scaled + Height = 222, // different height for testing, Icon is scaled + Icon = iconBytes + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + + Assert.NotNull(result.Icon); + Assert.Equal(defaultIconSize.Width, result.Icon.Width); + Assert.Equal(defaultIconSize.Height, result.Icon.Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToImageContainerTest.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToImageContainerTest.cs new file mode 100644 index 000000000..ec7112972 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToImageContainerTest.cs @@ -0,0 +1,144 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.IO; +using Greenshot.Base.Effects; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToImageContainerTest +{ + [Fact] + public void ConvertDtoToDomain_ImageContainerDto_Returns_ImageContainer() + { + // Arrange + var image = new Bitmap(100, 100); // Create a sample image + byte[] imageData; + using (var memoryStream = new MemoryStream()) + { + image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png); + imageData = memoryStream.ToArray(); + } + + var dto = new ImageContainerDto + { + Left = 10, + Top = 20, + Width = 222, // different width for testing, because the image is 100x100, so it is scaled + Height = 222, // different height for testing, because the image is 100x100, so it is scaled + Image = imageData, + Fields = [ new FieldDto + { + FieldTypeName = nameof(FieldType.SHADOW), + Scope = nameof(ImageContainer), + Value = new BoolFieldValueDto + { + Value = false + } + }] + }; + + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(222, result.Width); + Assert.Equal(222, result.Height); + Assert.NotNull(result.Image); + Assert.Equal(image.Width, result.Image.Width); + Assert.Equal(image.Height, result.Image.Height); + } + + /// + /// Test with shadow, the recalculates the size and position + /// + [Fact] + public void ConvertDtoToDomain_ImageContainerDto_with_shadow_Returns_ImageContainer_with_shadow() + { + // Arrange + var dropShadowImpact = new + { + AdditionalWidth = 14, + AdditionalHeight = 14, + OffsetTop = 1, + OffsetLeft = 1 + }; + + var image = new Bitmap(100, 100); // Create a sample image + byte[] imageData; + using (var memoryStream = new MemoryStream()) + { + image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png); + imageData = memoryStream.ToArray(); + } + + var dto = new ImageContainerDto + { + Left = 10, + Top = 20, + Width = 222, // different width for testing, because the image is 100x100 + Height = 222, // different height for testing, because the image is 100x100 + Image = imageData, + Fields = [ new FieldDto + { + FieldTypeName = nameof(FieldType.SHADOW), + Scope = nameof(ImageContainer), + Value = new BoolFieldValueDto + { + Value = true + } + }] + + }; + + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + Assert.NotNull(result); + + IField shadowField = result.GetField(FieldType.SHADOW); + Assert.NotNull(shadowField); + Assert.IsType(shadowField.Value); + Assert.True((bool)shadowField.Value); // Ensure the shadow flag is true + + Assert.Equal(dto.Left + dropShadowImpact.OffsetLeft, result.Left); + Assert.Equal(dto.Top + dropShadowImpact.OffsetTop, result.Top); + Assert.Equal(100 + dropShadowImpact.AdditionalWidth, result.Width); + Assert.Equal(100 + dropShadowImpact.AdditionalHeight, result.Height); + Assert.NotNull(result.Image); + Assert.Equal(image.Width, result.Image.Width); + Assert.Equal(image.Height, result.Image.Height); + } +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToLineContainerTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToLineContainerTests.cs new file mode 100644 index 000000000..0fb0329bb --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToLineContainerTests.cs @@ -0,0 +1,103 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToLineContainerTests +{ + [Fact] + public void ConvertDtoToDomain_LineContainerDto_Returns_LineContainer() + { + // Arrange + var colorBlue = Color.Blue; + var dto = new LineContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Fields = [ new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_THICKNESS), + Scope = nameof(LineContainer), + Value = new IntFieldValueDto + { + Value = 3 // default in LineContainer is 2 + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_COLOR), + Scope = nameof(LineContainer), + Value = new ColorFieldValueDto + { + Value = colorBlue // default in LineContainer is Red + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.SHADOW), + Scope = nameof(LineContainer), + Value = new BoolFieldValueDto + { + Value = false // default in LineContainer is true + } + } + ] + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + var resultLineThickness = result.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = result.GetFieldValue(FieldType.LINE_COLOR); + var resultShadow = result.GetFieldValue(FieldType.SHADOW); + + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); + } + +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToMetafileContainerTest.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToMetafileContainerTest.cs new file mode 100644 index 000000000..da016d323 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToMetafileContainerTest.cs @@ -0,0 +1,65 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.IO; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToMetafileContainerTest +{ + + [Fact] + public void ConvertDtoToDomain_MetafileContainerDto_Returns_MetafileContainer() + { + // Arrange + var metafilePath = Path.Combine("TestData", "Images", "Logo_G_with_Border.wmf"); + byte[] metafileData = File.ReadAllBytes(metafilePath); + + var dto = new MetafileContainerDto + { + Left = 10, + Top = 20, + Width = 222, + Height = 222, + MetafileData = metafileData + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + + // only simple check + Assert.NotNull(result.MetafileContent); + + // check current values from Logo_G_with_Border.wmf + Assert.Equal(1499, result.Metafile.Width); + Assert.Equal(1249, result.Metafile.Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToObfuscateContainerTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToObfuscateContainerTests.cs new file mode 100644 index 000000000..a1162d717 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToObfuscateContainerTests.cs @@ -0,0 +1,127 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.Linq; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Drawing.Filters; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; +using static Greenshot.Editor.Drawing.FilterContainer; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToObfuscateContainerTests +{ + [Fact] + public void ConvertDtoToDomain_ObfuscateContainerDto_Returns_ObfuscateContainer() + { + // Arrange + var colorBlue = Color.Blue; + var dto = new ObfuscateContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Fields = [ new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_THICKNESS), + Scope = nameof(ObfuscateContainer), + Value = new IntFieldValueDto + { + Value = 3 // default in ObfuscateContainer is 0 + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_COLOR), + Scope = nameof(ObfuscateContainer), + Value = new ColorFieldValueDto + { + Value = colorBlue // default in ObfuscateContainer is Red + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.SHADOW), + Scope = nameof(ObfuscateContainer), + Value = new BoolFieldValueDto + { + Value = true // default in ObfuscateContainer is false + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.PREPARED_FILTER_OBFUSCATE), + Scope = nameof(ObfuscateContainer), + Value = new PreparedFilterFieldValueDto + { + Value = PreparedFilter.BLUR // default in ObfuscateContainer is PIXELIZE + } + } + ] + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + var resultLineThickness = result.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = result.GetFieldValue(FieldType.LINE_COLOR); + var resultShadow = result.GetFieldValue(FieldType.SHADOW); + var resultPreparedFilter = result.GetFieldValue(FieldType.PREPARED_FILTER_OBFUSCATE); + var allFilter = result.Filters; + var blurFilter = allFilter.OfType().FirstOrDefault(); + + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.True((bool)resultShadow); + + Assert.NotNull(resultPreparedFilter); + Assert.IsType(resultPreparedFilter); + Assert.Equal(PreparedFilter.BLUR, resultPreparedFilter); + + // PreparedFilter.BLUR should add BlurFilter + Assert.NotNull(allFilter); + Assert.Equal(1, allFilter?.Count); + Assert.NotNull(blurFilter); + + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToRectangleContainerTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToRectangleContainerTests.cs new file mode 100644 index 000000000..a78fee9d8 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToRectangleContainerTests.cs @@ -0,0 +1,119 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToRectangleContainerTests +{ + [Fact] + public void ConvertDtoToDomain_RectangleContainerDto_Returns_RectangleContainer() + { + // Arrange + var colorBlue = Color.Blue; + var colorGreen = Color.Green; + var dto = new RectangleContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Fields = [ new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_THICKNESS), + Scope = nameof(RectangleContainer), + Value = new IntFieldValueDto + { + Value = 3 // default in RectangleContainer is 2 + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_COLOR), + Scope = nameof(RectangleContainer), + Value = new ColorFieldValueDto + { + Value = colorBlue // default in RectangleContainer is Red + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FILL_COLOR), + Scope = nameof(RectangleContainer), + Value = new ColorFieldValueDto + { + Value = colorGreen // default in RectangleContainer is Transparent + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.SHADOW), + Scope = nameof(RectangleContainer), + Value = new BoolFieldValueDto + { + Value = false // default in RectangleContainer is true + } + } + ] + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + var resultLineThickness = result.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = result.GetFieldValue(FieldType.LINE_COLOR); + var resultFillColor = result.GetFieldValue(FieldType.FILL_COLOR); + var resultShadow = result.GetFieldValue(FieldType.SHADOW); + + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(colorGreen, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorGreen)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); + } + +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToSpeechbubbleContainerTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToSpeechbubbleContainerTests.cs new file mode 100644 index 000000000..81604b0b5 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToSpeechbubbleContainerTests.cs @@ -0,0 +1,208 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToSpeechbubbleContainerTests +{ + [Fact] + public void ConvertDtoToDomain_SpeechbubbleContainerDto_Returns_SpeechbubbleContainer() + { + // Arrange + var colorBlue = Color.Blue; + var colorWhite = Color.White; + var fontFamily = FontFamily.GenericSansSerif.Name; + var dto = new SpeechbubbleContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Text = "Hello, greenshot!", + StoredTargetGripperLocation = new PointDto { X = 30, Y = 40 }, + Fields = [ new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_THICKNESS), + Scope = nameof(SpeechbubbleContainer), + Value = new IntFieldValueDto + { + Value = 2 // default in SpeechbubbleContainer is 2 + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_COLOR), + Scope = nameof(SpeechbubbleContainer), + Value = new ColorFieldValueDto + { + Value = colorBlue // default in SpeechbubbleContainer is Blue + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FILL_COLOR), + Scope = nameof(SpeechbubbleContainer), + Value = new ColorFieldValueDto + { + Value = colorWhite // default in SpeechbubbleContainer is White + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.SHADOW), + Scope = nameof(SpeechbubbleContainer), + Value = new BoolFieldValueDto + { + Value = false // default in SpeechbubbleContainer is true + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FONT_ITALIC), + Scope = nameof(SpeechbubbleContainer), + Value = new BoolFieldValueDto + { + Value = false // default in SpeechbubbleContainer is false + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FONT_BOLD), + Scope = nameof(SpeechbubbleContainer), + Value = new BoolFieldValueDto + { + Value = true // default in SpeechbubbleContainer is true + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FONT_FAMILY), + Scope = nameof(SpeechbubbleContainer), + Value = new StringFieldValueDto + { + Value = fontFamily // default in SpeechbubbleContainer is "Microsoft Sans Serif" + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FONT_SIZE), + Scope = nameof(SpeechbubbleContainer), + Value = new SingleFieldValueDto + { + Value = 20f // default in SpeechbubbleContainer is 20f + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.TEXT_HORIZONTAL_ALIGNMENT), + Scope = nameof(SpeechbubbleContainer), + Value = new StringAlignmentFieldValueDto + { + Value = StringAlignment.Center // default in SpeechbubbleContainer is Center + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.TEXT_VERTICAL_ALIGNMENT), + Scope = nameof(SpeechbubbleContainer), + Value = new StringAlignmentFieldValueDto + { + Value = StringAlignment.Center // default in SpeechbubbleContainer is Center + } + } + ] + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null) as SpeechbubbleContainer; + + // Assert + var resultLineThickness = result.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = result.GetFieldValue(FieldType.LINE_COLOR); + var resultFillColor = result.GetFieldValue(FieldType.FILL_COLOR); + var resultShadow = result.GetFieldValue(FieldType.SHADOW); + var resultFontItalic = result.GetFieldValue(FieldType.FONT_ITALIC); + var resultFontBold = result.GetFieldValue(FieldType.FONT_BOLD); + var resultFontFamily = result.GetFieldValue(FieldType.FONT_FAMILY); + var resultFontSize = result.GetFieldValue(FieldType.FONT_SIZE); + var resultTextHorizontalAlignment = result.GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); + var resultTextVerticalAlignment = result.GetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT); + + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + Assert.Equal(dto.Text, result.Text); + Assert.Equal(dto.StoredTargetGripperLocation.X, result.StoredTargetGripperLocation.X); + Assert.Equal(dto.StoredTargetGripperLocation.Y, result.StoredTargetGripperLocation.Y); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(2, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorBlue, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorBlue)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(colorWhite, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorWhite)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); + + Assert.NotNull(resultFontItalic); + Assert.IsType(resultFontItalic); + Assert.False((bool)resultFontItalic); + + Assert.NotNull(resultFontBold); + Assert.IsType(resultFontBold); + Assert.True((bool)resultFontBold); + + Assert.NotNull(resultFontFamily); + Assert.IsType(resultFontFamily); + Assert.Equal(fontFamily, resultFontFamily); + + Assert.NotNull(resultFontSize); + Assert.IsType(resultFontSize); + Assert.Equal(20f, resultFontSize); + + Assert.NotNull(resultTextHorizontalAlignment); + Assert.IsType(resultTextHorizontalAlignment); + Assert.Equal(StringAlignment.Center, resultTextHorizontalAlignment); + + Assert.NotNull(resultTextVerticalAlignment); + Assert.IsType(resultTextVerticalAlignment); + Assert.Equal(StringAlignment.Center, resultTextVerticalAlignment); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToStepLabelContainerTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToStepLabelContainerTests.cs new file mode 100644 index 000000000..c06bd0acf --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToStepLabelContainerTests.cs @@ -0,0 +1,77 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Base.Interfaces.Drawing; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToStepLabelContainerTests +{ + [Fact] + public void ConvertDtoToDomain_StepLabelContainerDto_Returns_StepLabelContainer() + { + // Arrange + var dto = new StepLabelContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Number = 2, + CounterStart = 1 + }; + // see StepLabelContainer.InitializeFields() for defaults + var defaultFillColor = System.Drawing.Color.DarkRed; + var defaultLineColor = System.Drawing.Color.White; + var defaultFlags = FieldFlag.COUNTER; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + var resultFillColor = result.GetFieldValue(Greenshot.Editor.Drawing.Fields.FieldType.FILL_COLOR); + var resultLineColor = result.GetFieldValue(Greenshot.Editor.Drawing.Fields.FieldType.LINE_COLOR); + var resultFlags = result.GetFieldValue(Greenshot.Editor.Drawing.Fields.FieldType.FLAGS); + + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + Assert.Equal(dto.Number, result.Number); + Assert.Equal(dto.CounterStart, result.CounterStart); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.Equal(defaultFillColor.ToArgb(), ((System.Drawing.Color)resultFillColor).ToArgb()); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.Equal(defaultLineColor.ToArgb(), ((System.Drawing.Color)resultLineColor).ToArgb()); + + Assert.NotNull(resultFlags); + Assert.IsType(resultFlags); + Assert.Equal(defaultFlags, (FieldFlag)resultFlags); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToSvgContainerTest.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToSvgContainerTest.cs new file mode 100644 index 000000000..de4a5cd79 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToSvgContainerTest.cs @@ -0,0 +1,59 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.IO; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToSvgContainerTest +{ + [Fact] + public void ConvertDtoToDomain_SvgContainerDto_Returns_SvgContainer() + { + // Arrange + var svgFilePath = Path.Combine("TestData", "Images", "Logo_G_with_Border.svg"); + byte[] svgData = File.ReadAllBytes(svgFilePath); + + var dto = new SvgContainerDto + { + Left = 10, + Top = 20, + Width = 222, + Height = 222, + SvgData = svgData + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null); + + // Assert + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + + Assert.NotNull(result.SvgContent); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToTextContainerTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToTextContainerTests.cs new file mode 100644 index 000000000..2f20fdca9 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToTextContainerTests.cs @@ -0,0 +1,206 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; + +[Collection("DefaultCollection")] +public class ConvertDtoToTextContainerTests +{ + [Fact] + public void ConvertDtoToDomain_TextContainerDto_Returns_TextContainer() + { + // Arrange + var colorRed = Color.Red; + var colorTransparent = Color.Transparent; + var fontFamilyName = FontFamily.GenericSansSerif.Name; + var dto = new TextContainerDto + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Text = "Hello, greenshot!", + Fields = [ + new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_THICKNESS), + Scope = nameof(TextContainer), + Value = new IntFieldValueDto + { + Value = 3 // default in TextContainer is 2 + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.LINE_COLOR), + Scope = nameof(TextContainer), + Value = new ColorFieldValueDto + { + Value = colorRed // default in TextContainer is Red + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.SHADOW), + Scope = nameof(TextContainer), + Value = new BoolFieldValueDto + { + Value = false // default in TextContainer is true + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FONT_ITALIC), + Scope = nameof(TextContainer), + Value = new BoolFieldValueDto + { + Value = true // default in TextContainer is false + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FONT_BOLD), + Scope = nameof(TextContainer), + Value = new BoolFieldValueDto + { + Value = true // default in TextContainer is false + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FILL_COLOR), + Scope = nameof(TextContainer), + Value = new ColorFieldValueDto + { + Value = colorTransparent // default in TextContainer is Transparent + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FONT_FAMILY), + Scope = nameof(TextContainer), + Value = new StringFieldValueDto + { + Value = "Arial" // default in TextContainer is GenericSansSerif.Name + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.FONT_SIZE), + Scope = nameof(TextContainer), + Value = new SingleFieldValueDto + { + Value = 12f // default in TextContainer is 11f + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.TEXT_HORIZONTAL_ALIGNMENT), + Scope = nameof(TextContainer), + Value = new StringAlignmentFieldValueDto + { + Value = StringAlignment.Far // default in TextContainer is StringAlignment.Center + } + }, + new FieldDto + { + FieldTypeName = nameof(FieldType.TEXT_VERTICAL_ALIGNMENT), + Scope = nameof(TextContainer), + Value = new StringAlignmentFieldValueDto + { + Value = StringAlignment.Far // default in TextContainer is StringAlignment.Center + } + } + ] + }; + + // Act + var result = ConvertDtoToDomain.ToDomain(dto, null) as TextContainer; + + // Assert + var resultLineThickness = result.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = result.GetFieldValue(FieldType.LINE_COLOR); + var resultShadow = result.GetFieldValue(FieldType.SHADOW); + var resultFontItalic = result.GetFieldValue(FieldType.FONT_ITALIC); + var resultFontBold = result.GetFieldValue(FieldType.FONT_BOLD); + var resultFillColor = result.GetFieldValue(FieldType.FILL_COLOR); + var resultFontFamily = result.GetFieldValue(FieldType.FONT_FAMILY); + var resultFontSize = result.GetFieldValue(FieldType.FONT_SIZE); + var resultTextHorizontalAlignment = result.GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); + var resultTextVerticalAlignment = result.GetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT); + + Assert.NotNull(result); + Assert.Equal(dto.Left, result.Left); + Assert.Equal(dto.Top, result.Top); + Assert.Equal(dto.Width, result.Width); + Assert.Equal(dto.Height, result.Height); + Assert.Equal(dto.Text, result.Text); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(3, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(colorRed, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorRed)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.False((bool)resultShadow); + + Assert.NotNull(resultFontItalic); + Assert.IsType(resultFontItalic); + Assert.True((bool)resultFontItalic); + + Assert.NotNull(resultFontBold); + Assert.IsType(resultFontBold); + Assert.True((bool)resultFontBold); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(colorTransparent, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(colorTransparent)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultFontFamily); + Assert.IsType(resultFontFamily); + Assert.Equal("Arial", resultFontFamily); + + Assert.NotNull(resultFontSize); + Assert.IsType(resultFontSize); + Assert.Equal(12f, resultFontSize); + + Assert.NotNull(resultTextHorizontalAlignment); + Assert.IsType(resultTextHorizontalAlignment); + Assert.Equal(StringAlignment.Far, resultTextHorizontalAlignment); + + Assert.NotNull(resultTextVerticalAlignment); + Assert.IsType(resultTextVerticalAlignment); + Assert.Equal(StringAlignment.Far, resultTextVerticalAlignment); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToValueTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToValueTests.cs new file mode 100644 index 000000000..400d3f6e4 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/DtoToDomain/ConvertDtoToValueTests.cs @@ -0,0 +1,100 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Fields; +using Xunit; +using static Greenshot.Editor.Drawing.ArrowContainer; +using static Greenshot.Editor.Drawing.FilterContainer; + +namespace Greenshot.Test.Editor.FileFormat.Dto.DtoToDomain; +public class ConvertDtoToValueTests +{ + /// + /// A collectioan of all supported value types for . + /// + public static IEnumerable GetFieldValueTestData() + { + yield return [typeof(bool), new BoolFieldValueDto { Value = true }, true]; + yield return [typeof(string), new StringFieldValueDto { Value = "test" }, "test"]; + yield return [typeof(int), new IntFieldValueDto { Value = 42 }, 42]; + yield return [typeof(float), new SingleFieldValueDto { Value = 3.14f }, 3.14f]; + yield return [typeof(double), new DoubleFieldValueDto { Value = 3.14d }, 3.14d]; + yield return [typeof(decimal), new DecimalFieldValueDto { Value = 3.14m }, 3.14m]; + yield return [typeof(ArrowHeadCombination), new ArrowHeadCombinationFieldValueDto { Value = ArrowHeadCombination.END_POINT }, ArrowHeadCombination.END_POINT]; + yield return [typeof(FieldFlag), new FieldFlagFieldValueDto { Value = FieldFlag.CONFIRMABLE }, FieldFlag.CONFIRMABLE]; + yield return [typeof(PreparedFilter), new PreparedFilterFieldValueDto { Value = PreparedFilter.AREA_HIGHLIGHT }, PreparedFilter.AREA_HIGHLIGHT]; + yield return [typeof(StringAlignment), new StringAlignmentFieldValueDto { Value = StringAlignment.Center }, StringAlignment.Center]; + } + /// + /// Verifies that the method correctly converts a to its corresponding domain value type. + /// + [Theory] + [MemberData(nameof(GetFieldValueTestData))] + public void ConvertDtoToValue_FieldValueDtoType_Returns_DomainValueType(Type valueType, FieldValueDto dto, object value) + { + // Act + var result = ConvertDtoToDomain.ConvertDtoToValue(dto); + + // Assert + Assert.IsType(valueType, result); + Assert.Equal(value, result); + } + + /// + /// Tests the conversion of a to a value. + /// + /// It's a dedicated test because needs a special compare. + [Fact] + public void ConvertDtoToValue_ColorFieldValueDto_ReturnsColor() + { + // Arrange + var redColor = Color.Red; + var dto = new ColorFieldValueDto { Value = redColor }; + + // Act + var result = ConvertDtoToDomain.ConvertDtoToValue(dto); + + // Assert + Assert.IsType(result); + + // special compare, because we only store the ARGB value + Assert.True(DtoHelper.CompareColorValue(redColor, (Color)result), + $"The color values are different. expected:{DtoHelper.ArgbString(redColor)} result:{DtoHelper.ArgbString((Color)result)}"); + } + + [Fact] + public void ConvertDtoToValue_NullFieldValueDto_ReturnsNull() + { + // Arrange + var dto = new NullFieldValueDto(); + + // Act + var result = ConvertDtoToDomain.ConvertDtoToValue(dto); + + // Assert + Assert.Null(result); + } +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/ApplicationFileSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/ApplicationFileSerializationTests.cs new file mode 100644 index 000000000..88c4dec81 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/ApplicationFileSerializationTests.cs @@ -0,0 +1,91 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat; +using Greenshot.Editor.FileFormat.Dto; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class ApplicationFileSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_ApplicationFile() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var lineContainer = new LineContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + var rectangleContainer = new RectangleContainer(surface) + { + Left = 30, + Top = 40, + Width = 200, + Height = 80 + }; + var domainList = new DrawableContainerList { lineContainer, rectangleContainer }; + var image = new Bitmap(10, 10); + var domain = new GreenshotFile + { + ContainerList = domainList, + Image = image, + SchemaVersion = GreenshotFileVersionHandler.CurrentSchemaVersion + }; + + // Act + var dto = ConvertDomainToDto.ToDto(domain); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto); + + // Assert + Assert.NotNull(result); + Assert.Equal(domain.SchemaVersion, result.SchemaVersion); + Assert.NotNull(result.Image); + Assert.NotNull(result.ContainerList); + Assert.Equal(2, result.ContainerList.Count); + Assert.IsType(result.ContainerList[0]); + Assert.IsType(result.ContainerList[1]); + Assert.Equal(lineContainer.Top, result.ContainerList[0].Top); + Assert.Equal(lineContainer.Left, result.ContainerList[0].Left); + Assert.Equal(lineContainer.Width, result.ContainerList[0].Width); + Assert.Equal(lineContainer.Height, result.ContainerList[0].Height); + Assert.Equal(rectangleContainer.Top, result.ContainerList[1].Top); + Assert.Equal(rectangleContainer.Left, result.ContainerList[1].Left); + Assert.Equal(rectangleContainer.Width, result.ContainerList[1].Width); + Assert.Equal(rectangleContainer.Height, result.ContainerList[1].Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/ArrowContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/ArrowContainerSerializationTests.cs new file mode 100644 index 000000000..ee2196ce1 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/ArrowContainerSerializationTests.cs @@ -0,0 +1,66 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + + +public class ArrowContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_ArrowContainer() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var arrowContainer = new ArrowContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + + // Act + var dto = ConvertDomainToDto.ToDto(arrowContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto,null) as ArrowContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(arrowContainer.Left, result.Left); + Assert.Equal(arrowContainer.Top, result.Top); + Assert.Equal(arrowContainer.Width, result.Width); + Assert.Equal(arrowContainer.Height, result.Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/CursorContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/CursorContainerSerializationTests.cs new file mode 100644 index 000000000..b8b4cd1eb --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/CursorContainerSerializationTests.cs @@ -0,0 +1,60 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class CursorContainerSerializationTests +{ + [Fact] + public void SerializeDeserialize_IconContainer() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var cursorContainer = new CursorContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + + // Act + var dto = ConvertDomainToDto.ToDto(cursorContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as CursorContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(cursorContainer.Left, result.Left); + Assert.Equal(cursorContainer.Top, result.Top); + Assert.Equal(cursorContainer.Width, result.Width); + Assert.Equal(cursorContainer.Height, result.Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/DrawableContainerListSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/DrawableContainerListSerializationTests.cs new file mode 100644 index 000000000..ffcbc7b35 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/DrawableContainerListSerializationTests.cs @@ -0,0 +1,210 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.IO; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Drawing.Filters; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class DrawableContainerListSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_DrawableContainerList() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var lineContainer = new LineContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + var rectangleContainer = new RectangleContainer(surface) + { + Left = 30, + Top = 40, + Width = 200, + Height = 80 + }; + var domainList = new DrawableContainerList { lineContainer, rectangleContainer }; + + // Act + var dto = ConvertDomainToDto.ToDto(domainList); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto) as DrawableContainerList; + + // Assert + Assert.NotNull(result); + Assert.Equal(2, result.Count); + Assert.IsType(result[0]); + Assert.IsType(result[1]); + + Assert.Equal(lineContainer.Top, result[0].Top); + Assert.Equal(lineContainer.Left, result[0].Left); + Assert.Equal(lineContainer.Width, result[0].Width); + Assert.Equal(lineContainer.Height, result[0].Height); + + Assert.Equal(rectangleContainer.Top, result[1].Top); + Assert.Equal(rectangleContainer.Left, result[1].Left); + Assert.Equal(rectangleContainer.Width, result[1].Width); + Assert.Equal(rectangleContainer.Height, result[1].Height); + } + + /// + /// Tests the serialization and deserialization process of an object that contains all possible drawable container. + /// + /// It only checks the types to proof of + [Fact] + public void SerializeDeserialize_DrawableContainerList_with_all_Container() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var metafilePath = Path.Combine("TestData", "Images", "Logo_G_with_Border.emf"); + using var metafileStream = File.OpenRead(metafilePath); + + var svgFilePath = Path.Combine("TestData", "Images", "Logo_G_with_Border.svg"); + using var svgStream = File.OpenRead(svgFilePath); + + + var domainList = new DrawableContainerList + { + new LineContainer(surface), + new RectangleContainer(surface), + new ImageContainer(surface), + new IconContainer(surface), + new TextContainer(surface), + new SpeechbubbleContainer(surface), + new ArrowContainer(surface), + new CursorContainer(surface), + new EllipseContainer(surface), + new FreehandContainer(surface), + new HighlightContainer(surface), + new MetafileContainer(metafileStream, surface), + new ObfuscateContainer(surface), + new StepLabelContainer(surface), + new SvgContainer(svgStream, surface) + }; + + // Act + var dto = ConvertDomainToDto.ToDto(domainList); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto) as DrawableContainerList; + + // Assert + Assert.NotNull(result); + Assert.Equal(domainList.Count, result.Count); + Assert.IsType(result[0]); + Assert.IsType(result[1]); + Assert.IsType(result[2]); + Assert.IsType(result[3]); + Assert.IsType(result[4]); + Assert.IsType(result[5]); + Assert.IsType(result[6]); + Assert.IsType(result[7]); + Assert.IsType(result[8]); + Assert.IsType(result[9]); + Assert.IsType(result[10]); + Assert.IsType(result[11]); + Assert.IsType(result[12]); + Assert.IsType(result[13]); + Assert.IsType(result[14]); + } + + /// + /// Tests the serialization and deserialization of a DrawableContainerList with two HighlightContainers using different highlight colors. + /// This is to test issue #500. + /// + [Fact] + public void SerializeDeserialize_DrawableContainerList_with_two_different_HighlightContainer() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var highlight1 = new HighlightContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + var highlight2 = new HighlightContainer(surface) + { + Left = 30, + Top = 40, + Width = 120, + Height = 60 + }; + var color1Yellow = Color.Yellow; + var color2Lime = Color.Lime; + // Set different highlight colors + ((HighlightFilter)highlight1.Children[0]).SetFieldValue(FieldType.FILL_COLOR, color1Yellow); + ((HighlightFilter)highlight2.Children[0]).SetFieldValue(FieldType.FILL_COLOR, color2Lime); + + var domainList = new DrawableContainerList { highlight1, highlight2 }; + + // Act + var dto = ConvertDomainToDto.ToDto(domainList); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto) as DrawableContainerList; + + // Assert + Assert.NotNull(result); + Assert.Equal(2, result.Count); + + Assert.IsType(result[0]); + Assert.IsType(result[1]); + + var resultcontainer1 = (HighlightContainer)result[0]; + var resultcontainer2 = (HighlightContainer)result[1]; + + var color1 = ((HighlightFilter)resultcontainer1.Children[0]).GetFieldValue(FieldType.FILL_COLOR); + var color2 = ((HighlightFilter)resultcontainer2.Children[0]).GetFieldValue(FieldType.FILL_COLOR); + + Assert.NotNull(color1); + Assert.IsType(color1); + Assert.True(DtoHelper.CompareColorValue(color1Yellow, (Color)color1), + $"The color values are different. expected:{DtoHelper.ArgbString(color1Yellow)} result:{DtoHelper.ArgbString((Color)color1)}"); + + + Assert.NotNull(color2); + Assert.IsType(color2); + Assert.True(DtoHelper.CompareColorValue(color2Lime, (Color)color2), + $"The color values are different. expected:{DtoHelper.ArgbString(color2Lime)} result:{DtoHelper.ArgbString((Color)color2)}"); + + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/EllipseContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/EllipseContainerSerializationTests.cs new file mode 100644 index 000000000..834d1c6d8 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/EllipseContainerSerializationTests.cs @@ -0,0 +1,65 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class EllipseContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_EllipseContainer() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var ellipseContainer = new EllipseContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + + // Act + var dto = ConvertDomainToDto.ToDto(ellipseContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as EllipseContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(ellipseContainer.Left, result.Left); + Assert.Equal(ellipseContainer.Top, result.Top); + Assert.Equal(ellipseContainer.Width, result.Width); + Assert.Equal(ellipseContainer.Height, result.Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/FieldSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/FieldSerializationTests.cs new file mode 100644 index 000000000..86f9dec17 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/FieldSerializationTests.cs @@ -0,0 +1,126 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Fields; +using MessagePack; +using Xunit; +using static Greenshot.Editor.Drawing.ArrowContainer; +using static Greenshot.Editor.Drawing.FilterContainer; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class FieldSerializationTests +{ + /// + /// A collectioan of all supported value types for . + /// + public static IEnumerable GetFieldTestData() + { + yield return [FieldType.FONT_BOLD, typeof(bool), true]; + yield return [FieldType.FONT_FAMILY, typeof(string), "Arial"]; + yield return [FieldType.LINE_THICKNESS, typeof(int), 42]; + yield return [FieldType.PREVIEW_QUALITY, typeof(float), 3.14f]; + yield return [FieldType.PREVIEW_QUALITY, typeof(double), 3.14d]; + yield return [FieldType.PREVIEW_QUALITY, typeof(decimal), 3.14m]; + yield return [FieldType.PREPARED_FILTER_HIGHLIGHT, typeof(PreparedFilter), PreparedFilter.AREA_HIGHLIGHT]; + yield return [FieldType.ARROWHEADS, typeof(ArrowHeadCombination), ArrowHeadCombination.END_POINT]; + yield return [FieldType.FLAGS, typeof(FieldFlag), FieldFlag.CONFIRMABLE]; + yield return [FieldType.TEXT_HORIZONTAL_ALIGNMENT, typeof(StringAlignment), StringAlignment.Center]; + } + + /// + /// Convert a domain object to , serialize , deserialize and convert back to domain object + /// + /// Different from this method tests and + /// instead of and + /// + /// + /// + [Theory] + [MemberData(nameof(GetFieldTestData))] + public void SerializeDeserialize_Field(IFieldType field, Type valueType, object value) + { + // Arrange + var original = new Field(field, "scope") { Value = value }; + + // Act + var dto = ConvertDomainToDto.ToDto(original); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto); + + // Assert + Assert.IsType(valueType, result.Value); + Assert.Equal(original.FieldType, result.FieldType); + Assert.Equal(original.Scope, result.Scope); + Assert.Equal(original.Value, result.Value); + } + + /// + /// Convert a domain object with a value to , serialize , deserialize and convert back to domain object + /// + [Fact] + public void SerializeDeserialize_ColorField() + { + // Arrange + var redColor = Color.Red; + var original = new Field(FieldType.LINE_COLOR, "scope") { Value = redColor }; + + // Act + var dto = ConvertDomainToDto.ToDto(original); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto); + + // Assert + Assert.Equal(original.FieldType, result.FieldType); + Assert.Equal(original.Scope, result.Scope); + + Assert.IsType(result.Value); + // special compare, because we only store the ARGB value + Assert.True(DtoHelper.CompareColorValue(redColor, (Color)result.Value), + $"The color values are different. expected:{DtoHelper.ArgbString(redColor)} result:{DtoHelper.ArgbString((Color)result.Value)}"); + } + + /// + /// Convert a domain object with a null value to , serialize, deserialize and convert back to domain object + /// + [Fact] + public void SerializeDeserialize_NullField() + { + // Arrange + var original = new Field(FieldType.FONT_FAMILY, "scope") { Value = null }; + + // Act + var dto = ConvertDomainToDto.ToDto(original); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto); + + // Assert + Assert.Null(result.Value); + } +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/FieldValueSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/FieldValueSerializationTests.cs new file mode 100644 index 000000000..e2c1c2281 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/FieldValueSerializationTests.cs @@ -0,0 +1,111 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Fields; +using MessagePack; +using Xunit; +using static Greenshot.Editor.Drawing.ArrowContainer; +using static Greenshot.Editor.Drawing.FilterContainer; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class FieldValueSerializationTests +{ + /// + /// A collectioan of all supported value types for . + /// + public static IEnumerable GetValueTestData() + { + yield return [typeof(bool), true]; + yield return [typeof(string), "Arial"]; + yield return [typeof(int), 42]; + yield return [typeof(float), 3.14f]; + yield return [typeof(double), 3.14d]; + yield return [typeof(decimal), 3.14m]; + yield return [typeof(ArrowHeadCombination), ArrowHeadCombination.END_POINT]; + yield return [typeof(PreparedFilter), PreparedFilter.BLUR]; + yield return [typeof(StringAlignment), StringAlignment.Center]; + yield return [typeof(FieldFlag), FieldFlag.CONFIRMABLE]; + } + + /// + /// Convert a value (basic types, that are allowed in a ) to , serialize , deserialize and convert back to domain object + /// + /// Different from this method tests and + /// instead of and + /// + /// + [Theory] + [MemberData(nameof(GetValueTestData))] + public void SerializeDeserialize_FieldValue(Type valueType, object value) + { + // Act + var dto = ConvertDomainToDto.ConvertValueToDto(value); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ConvertDtoToValue(deserializedDto); + + // Assert + Assert.IsType(valueType, result); + Assert.Equal(value, result); + } + + /// + /// Convert a to , serialize , deserialize and convert back to domain object + /// + /// It's a dedicated test because needs a special compare. + [Fact] + public void SerializeDeserialize_ColorFieldValue() + { + // Arrange + var redColor = Color.Red; + + // Act + var dto = ConvertDomainToDto.ConvertValueToDto(redColor); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ConvertDtoToValue(deserializedDto); + + // Assert + Assert.IsType( result); + // special compare, because we only store the ARGB value + Assert.True(DtoHelper.CompareColorValue(redColor, (Color)result), + $"The color values are different. expected:{DtoHelper.ArgbString(redColor)} result:{DtoHelper.ArgbString((Color)result)}"); + } + + [Fact] + public void SerializeDeserialize_NullFieldValue() + { + // Act + var dto = ConvertDomainToDto.ConvertValueToDto(null); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ConvertDtoToValue(deserializedDto); + + // Assert + Assert.Null(result); + } +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/FreehandContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/FreehandContainerSerializationTests.cs new file mode 100644 index 000000000..168e9a118 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/FreehandContainerSerializationTests.cs @@ -0,0 +1,74 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class FreehandContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_FreehandContainer() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var freehandContainer = new FreehandContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + CapturePoints = [new Point(10, 20), new Point(30, 40)] + }; + + // Act + var dto = ConvertDomainToDto.ToDto(freehandContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as FreehandContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(freehandContainer.Left, result.Left); + Assert.Equal(freehandContainer.Top, result.Top); + Assert.Equal(freehandContainer.Width, result.Width); + Assert.Equal(freehandContainer.Height, result.Height); + Assert.NotNull(result.CapturePoints); + Assert.Equal(freehandContainer.CapturePoints.Count, result.CapturePoints.Count); + Assert.Equal(freehandContainer.CapturePoints[0].X, result.CapturePoints[0].X); + Assert.Equal(freehandContainer.CapturePoints[0].Y, result.CapturePoints[0].Y); + Assert.Equal(freehandContainer.CapturePoints[1].X, result.CapturePoints[1].X); + Assert.Equal(freehandContainer.CapturePoints[1].Y, result.CapturePoints[1].Y); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/HighlightContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/HighlightContainerSerializationTests.cs new file mode 100644 index 000000000..78c49bdc7 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/HighlightContainerSerializationTests.cs @@ -0,0 +1,65 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class HighlightContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_HighlightContainer() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var highlightContainer = new HighlightContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + + // Act + var dto = ConvertDomainToDto.ToDto(highlightContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as HighlightContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(highlightContainer.Left, result.Left); + Assert.Equal(highlightContainer.Top, result.Top); + Assert.Equal(highlightContainer.Width, result.Width); + Assert.Equal(highlightContainer.Height, result.Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/IconContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/IconContainerSerializationTests.cs new file mode 100644 index 000000000..ea21aa218 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/IconContainerSerializationTests.cs @@ -0,0 +1,71 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.IO; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class IconContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_IconContainer() + { + // Arrange + var iconPath = Path.Combine("TestData", "Images", "Greenshot.ico"); + using var iconStream = File.OpenRead(iconPath); + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var iconContainer = new IconContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Icon = new Icon(iconStream) + }; + + // Act + var dto = ConvertDomainToDto.ToDto(iconContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as IconContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(iconContainer.Left, result.Left); + Assert.Equal(iconContainer.Top, result.Top); + Assert.Equal(iconContainer.Width, result.Width); + Assert.Equal(iconContainer.Height, result.Height); + Assert.NotNull(result.Icon); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/ImageContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/ImageContainerSerializationTests.cs new file mode 100644 index 000000000..76051cea0 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/ImageContainerSerializationTests.cs @@ -0,0 +1,73 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class ImageContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_ImageContainer() + { + // Arrange + using var bitmap = new Bitmap(50, 50); + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var imageContainer = new ImageContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Image = bitmap + }; + + // Act + var dto = ConvertDomainToDto.ToDto(imageContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as ImageContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(imageContainer.Left, result.Left); + Assert.Equal(imageContainer.Top, result.Top); + Assert.Equal(imageContainer.Width, result.Width); + Assert.Equal(imageContainer.Height, result.Height); + + // Compare the images (simple check) + Assert.NotNull(result.Image); + Assert.Equal(imageContainer.Image.Width, result.Image.Width); + Assert.Equal(imageContainer.Image.Height, result.Image.Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/LineContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/LineContainerSerializationTests.cs new file mode 100644 index 000000000..cd5cd3c1b --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/LineContainerSerializationTests.cs @@ -0,0 +1,65 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class LineContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_LineContainer() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var lineContainer = new LineContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + + // Act + var dto = ConvertDomainToDto.ToDto(lineContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as LineContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(lineContainer.Left, result.Left); + Assert.Equal(lineContainer.Top, result.Top); + Assert.Equal(lineContainer.Width, result.Width); + Assert.Equal(lineContainer.Height, result.Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/MetafileContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/MetafileContainerSerializationTests.cs new file mode 100644 index 000000000..6b1c2e563 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/MetafileContainerSerializationTests.cs @@ -0,0 +1,79 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.IO; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + + +public class MetafileContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_MetafileContainer() + { + // Arrange + var metafilePath = Path.Combine("TestData", "Images", "Logo_G_with_Border.emf"); + using var metafileStream = File.OpenRead(metafilePath); + + + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var metafileContainer = new MetafileContainer(metafileStream, surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + + // Act + var dto = ConvertDomainToDto.ToDto(metafileContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as MetafileContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(metafileContainer.Left, result.Left); + Assert.Equal(metafileContainer.Top, result.Top); + Assert.Equal(metafileContainer.Width, result.Width); + Assert.Equal(metafileContainer.Height, result.Height); + + // Compare the metafiles (simple check) + Assert.NotNull(result.MetafileContent); + + Assert.Equal(metafileContainer.Metafile.Width, result.Metafile.Width); + Assert.Equal(metafileContainer.Metafile.Height, result.Metafile.Height); + + } +} + diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/ObfuscateContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/ObfuscateContainerSerializationTests.cs new file mode 100644 index 000000000..70060edd6 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/ObfuscateContainerSerializationTests.cs @@ -0,0 +1,65 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class ObfuscateContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_ObfuscateContainer() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var obfuscateContainer = new ObfuscateContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + + // Act + var dto = ConvertDomainToDto.ToDto(obfuscateContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as ObfuscateContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(obfuscateContainer.Left, result.Left); + Assert.Equal(obfuscateContainer.Top, result.Top); + Assert.Equal(obfuscateContainer.Width, result.Width); + Assert.Equal(obfuscateContainer.Height, result.Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/RectangleContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/RectangleContainerSerializationTests.cs new file mode 100644 index 000000000..6d939c1d3 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/RectangleContainerSerializationTests.cs @@ -0,0 +1,65 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class RectangleContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_RectangleContainer() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var rectangleContainer = new RectangleContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + + // Act + var dto = ConvertDomainToDto.ToDto(rectangleContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as RectangleContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(rectangleContainer.Left, result.Left); + Assert.Equal(rectangleContainer.Top, result.Top); + Assert.Equal(rectangleContainer.Width, result.Width); + Assert.Equal(rectangleContainer.Height, result.Height); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/SpeechbubbleContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/SpeechbubbleContainerSerializationTests.cs new file mode 100644 index 000000000..23f52c761 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/SpeechbubbleContainerSerializationTests.cs @@ -0,0 +1,70 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class SpeechbubbleContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_SpeechbubbleContainer() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var speechbubbleContainer = new SpeechbubbleContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Text = "Hello, greenshot!", + StoredTargetGripperLocation = new Point(30, 40) + }; + + // Act + var dto = ConvertDomainToDto.ToDto(speechbubbleContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as SpeechbubbleContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(speechbubbleContainer.Left, result.Left); + Assert.Equal(speechbubbleContainer.Top, result.Top); + Assert.Equal(speechbubbleContainer.Width, result.Width); + Assert.Equal(speechbubbleContainer.Height, result.Height); + Assert.Equal(speechbubbleContainer.Text, result.Text); + Assert.Equal(speechbubbleContainer.StoredTargetGripperLocation, result.StoredTargetGripperLocation); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/StepLabelContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/StepLabelContainerSerializationTests.cs new file mode 100644 index 000000000..a164bda22 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/StepLabelContainerSerializationTests.cs @@ -0,0 +1,69 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class StepLabelContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_StepLabelContainer() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var stepLabelContainer = new StepLabelContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Number = 2, + CounterStart = 1 + }; + + // Act + var dto = ConvertDomainToDto.ToDto(stepLabelContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as StepLabelContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(stepLabelContainer.Left, result.Left); + Assert.Equal(stepLabelContainer.Top, result.Top); + Assert.Equal(stepLabelContainer.Width, result.Width); + Assert.Equal(stepLabelContainer.Height, result.Height); + Assert.Equal(stepLabelContainer.Number, result.Number); + Assert.Equal(stepLabelContainer.CounterStart, result.CounterStart); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/SurfaceSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/SurfaceSerializationTests.cs new file mode 100644 index 000000000..66cf0719e --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/SurfaceSerializationTests.cs @@ -0,0 +1,125 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat; +using Greenshot.Editor.FileFormat.Dto; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +[Collection("DefaultCollection")] +public class SurfaceSerializationTests +{ + /// + /// Tests the serialization and deserialization process of a plain Surface without any elements. + /// + [Fact] + public void SerializeDeserialize_PlainSurface() + { + // Arrange + var image = new Bitmap(200, 150); + var surface = new Surface(image); + + // Act + var greenshotFile = GreenshotFileVersionHandler.CreateGreenshotFile(surface); + var dto = ConvertDomainToDto.ToDto(greenshotFile); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var deserializedGreenshotFile = ConvertDtoToDomain.ToDomain(deserializedDto) as GreenshotFile; + var resultSurface = GreenshotFileVersionHandler.CreateSurface(deserializedGreenshotFile); + + // Assert + Assert.NotNull(resultSurface); + Assert.Equal(image.Width, resultSurface.Image.Width); + Assert.Equal(image.Height, resultSurface.Image.Height); + Assert.Empty(resultSurface.Elements); + + } + + /// + /// Tests the serialization and deserialization process of a surface with two StepLabelContainer, where the counter starts at 3. + /// + [Fact] + public void SerializeDeserialize_SurfaceWith2StepLabelWichStartsAt3() + { + // Arrange + var image = new Bitmap(100, 100); + var surface = new Surface(image); + surface.CounterStart = 3; + var stepLabel1 = new StepLabelContainer(surface) + { + Left = 5, + Top = 10, + Width = 50, + Height = 25 + }; + var stepLabel2 = new StepLabelContainer(surface) + { + Left = 15, + Top = 20, + Width = 60, + Height = 35 + }; + surface.AddElement(stepLabel1, false, false); + surface.AddElement(stepLabel2, false, false); + + // Act + var greenshotfile = GreenshotFileVersionHandler.CreateGreenshotFile(surface); + var dto= ConvertDomainToDto.ToDto(greenshotfile); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var deserializedGreenshotFile = ConvertDtoToDomain.ToDomain(deserializedDto) as GreenshotFile; + + var resultSurface = GreenshotFileVersionHandler.CreateSurface(deserializedGreenshotFile); + + // Assert + Assert.NotNull(resultSurface); + var resultContainerList = resultSurface.Elements; + + Assert.NotNull(resultContainerList); + Assert.Equal(2, resultContainerList.Count); + Assert.IsType(resultContainerList[0]); + Assert.IsType(resultContainerList[1]); + + var resultStepLabel1 = resultContainerList[0] as StepLabelContainer; + var resultStepLabel2 = resultContainerList[1] as StepLabelContainer; + Assert.NotNull(resultStepLabel1); + Assert.NotNull(resultStepLabel2); + Assert.Equal(stepLabel1.Left, resultStepLabel1.Left); + Assert.Equal(stepLabel1.Top, resultStepLabel1.Top); + Assert.Equal(stepLabel1.Width, resultStepLabel1.Width); + Assert.Equal(stepLabel1.Height, resultStepLabel1.Height); + Assert.Equal(stepLabel2.Left, resultStepLabel2.Left); + Assert.Equal(stepLabel2.Top, resultStepLabel2.Top); + Assert.Equal(stepLabel2.Width, resultStepLabel2.Width); + Assert.Equal(stepLabel2.Height, resultStepLabel2.Height); + + // both StepLabels should have the same CounterStart, which is 3 + Assert.Equal(3, resultStepLabel1.CounterStart); + Assert.Equal(3, resultStepLabel2.CounterStart); + + // The StepLabels should have the correct numbers, which are 3 and 4 + Assert.Equal(3, resultStepLabel1.Number); + Assert.Equal(4, resultStepLabel2.Number); + } +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/SvgContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/SvgContainerSerializationTests.cs new file mode 100644 index 000000000..f38cd13f0 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/SvgContainerSerializationTests.cs @@ -0,0 +1,124 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.Drawing.Drawing2D; +using System.IO; +using System.Windows.Media.Effects; +using Greenshot.Base.Core; +using Greenshot.Base.Effects; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +[Collection("DefaultCollection")] +public class SvgContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_SvgContainer() + { + // Arrange + var svgFilePath = Path.Combine("TestData", "Images", "Logo_G_with_Border.svg"); + using var svgStream = File.OpenRead(svgFilePath); + + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var svgContainer = new SvgContainer(svgStream, surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + + // Act + var dto = ConvertDomainToDto.ToDto(svgContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as SvgContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(svgContainer.Left, result.Left); + Assert.Equal(svgContainer.Top, result.Top); + Assert.Equal(svgContainer.Width, result.Width); + Assert.Equal(svgContainer.Height, result.Height); + + // only simple check because the SVG content is here a memory stream + Assert.NotNull(result.SvgContent); + } + + /// + /// Tests the serialization and deserialization of a rotated object. + /// + [Fact] + public void SerializeDeserialize_RotatedSvgContainer() + { + // Arrange + var svgFilePath = Path.Combine("TestData", "Images", "Logo_G_with_Border.svg"); + using var svgStream = File.OpenRead(svgFilePath); + + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var svgContainer = new SvgContainer(svgStream, surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + // imitate surface.ApplyBitmapEffect(new RotateEffect(90)); + var effect = new RotateEffect(90); + var image = new Bitmap(300, 400); + Matrix matrix = new Matrix(); + Image newImage = ImageHelper.ApplyEffect(image, effect, matrix); + svgContainer.Transform(matrix); + + // Act + var dto = ConvertDomainToDto.ToDto(svgContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as SvgContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(svgContainer.Left, result.Left); + Assert.Equal(svgContainer.Top, result.Top); + Assert.Equal(svgContainer.Width, result.Width); + Assert.Equal(svgContainer.Height, result.Height); + + // only simple check because the SVG content is here a memory stream + Assert.NotNull(result.SvgContent); + + // Check if the rotation angle is preserved + Assert.Equal(90, result.RotationAngle); + + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/TextContainerSerializationTests.cs b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/TextContainerSerializationTests.cs new file mode 100644 index 000000000..a34e50681 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/Dto/GreenshotFileV2/TextContainerSerializationTests.cs @@ -0,0 +1,67 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormat.Dto.Container; +using MessagePack; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.Dto.GreenshotFileV2; + +public class TextContainerSerializationTests +{ + /// + /// Tests the serialization and deserialization process of an object. + /// + /// This test verifies that an object can be correctly converted to + /// its DTO representation, serialized, deserialized and then converted back to . + [Fact] + public void SerializeDeserialize_TextContainer() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var textContainer = new TextContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50, + Text = "Hello, greenshot!" + }; + + // Act + var dto = ConvertDomainToDto.ToDto(textContainer); + var serialized = MessagePackSerializer.Serialize(dto); + var deserializedDto = MessagePackSerializer.Deserialize(serialized); + var result = ConvertDtoToDomain.ToDomain(deserializedDto, null) as TextContainer; + + // Assert + Assert.NotNull(result); + Assert.Equal(textContainer.Left, result.Left); + Assert.Equal(textContainer.Top, result.Top); + Assert.Equal(textContainer.Width, result.Width); + Assert.Equal(textContainer.Height, result.Height); + Assert.Equal(textContainer.Text, result.Text); + } +} diff --git a/src/Greenshot.Test/Editor/FileFormat/V1/Legacy/LegacyGreenshotFileTest.cs b/src/Greenshot.Test/Editor/FileFormat/V1/Legacy/LegacyGreenshotFileTest.cs new file mode 100644 index 000000000..f9b95556f --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/V1/Legacy/LegacyGreenshotFileTest.cs @@ -0,0 +1,96 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.IO; +using Greenshot.Base.Core; +using Greenshot.Editor.FileFormat.V1.Legacy; +using Greenshot.Test.Editor.FileFormatHandlers; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.V1.Legacy; + +[Collection("DefaultCollection")] +public class LegacyGreenshotFileTest +{ + public static IEnumerable TestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "Surface_with_Image_800x400.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "LineContainer_lt_200_200_w_400.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "RectangleContainer_lt_100_200_wh_150_80.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "ArrowContainer_lt_100_200_wh_400_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "EllipseContainer_lt_200_200_wh_400_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "TextContainer_lt_300_200_wh_300_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "IconContainer_lt_400_200_wh_32_32.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "ImageContainer_lt_300_200_wh_100_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "StepLabelContainer_lt_200_200_lt_500_300.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "FreehandContainer_with_4_points.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "Surface_with_11_different_DrawableContainer.greenshot")]; + + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "Surface_with_Image_800x400.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "LineContainer_lt_200_200_w_400.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "RectangleContainer_lt_100_200_wh_150_80.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "ArrowContainer_lt_100_200_wh_400_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "EllipseContainer_lt_200_200_wh_400_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "TextContainer_lt_300_200_wh_300_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "IconContainer_lt_400_200_wh_32_32.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "ImageContainer_lt_300_200_wh_100_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "MetafileContainer_lt_300_200_wh_120_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "StepLabelContainer_lt_200_200_lt_500_300.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "SvgContainer_lt_300_200_wh_120_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "FreehandContainer_with_4_points.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "Surface_with_14_different_DrawableContainer.greenshot")]; + } + + /// + /// This is more or less a test while develop. + /// A very simple test that loads a legacy Greenshot file and check if the image and container list are not null and the deserialization throws no exception. + /// A more detailed test is done in the . + /// + /// + [Theory] + [MemberData(nameof(TestData))] + public void LoadLegacyGreenshotFileTest(string filePath) + { + // Arrange + var surfaceFileStream = File.OpenRead(filePath); + + // Act + // We create a copy of the bitmap, so everything else can be disposed + surfaceFileStream.Position = 0; + using Image tmpImage = Image.FromStream(surfaceFileStream, true, true); + var fileImage = ImageHelper.Clone(tmpImage); + + var containerList = LegacyFileHelper.GetContainerListFromGreenshotfile(surfaceFileStream); + + // Assert + Assert.NotNull(fileImage); + Assert.NotNull(containerList); + } + +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormat/V1/Legacy/LegacySerializationBinderTests.cs b/src/Greenshot.Test/Editor/FileFormat/V1/Legacy/LegacySerializationBinderTests.cs new file mode 100644 index 000000000..93886af43 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormat/V1/Legacy/LegacySerializationBinderTests.cs @@ -0,0 +1,74 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.IO; +using System.Runtime.Serialization.Formatters.Binary; +using System.ServiceModel.Security; +using Greenshot.Editor.FileFormat.V1.Legacy; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormat.V1.Legacy; + +/// +/// Tests for the LegacySerializationBinder class which handles the mapping +/// of types during deserialization of legacy Greenshot files. +/// +[Collection("DefaultCollection")] +public class LegacySerializationBinderTests +{ + /// + /// Test that verifies that a SecurityAccessDeniedException is thrown when + /// attempting to deserialize an object with a type that is not mapped in the binder. + /// + /// This covers the vulnerability attack created with ysoserial. #579 + [Fact] + public void Deserialize_UnmappedType_ThrowsSecurityAccessDeniedException() + { + // Arrange + var unmappedObject = new UnmappedTestClass { Value = "Test Value" }; + var binaryFormatter = new BinaryFormatter(); + + // Serialize the object without a custom binder + using var memoryStream = new MemoryStream(); + binaryFormatter.Serialize(memoryStream, unmappedObject); + memoryStream.Position = 0; + + // Act & Assert + // This should throw a SecurityAccessDeniedException when LegacyFileHelper tries to deserialize + // our unmapped type through the LegacySerializationBinder + var exception = Assert.Throws(() => + LegacyFileHelper.GetContainerListFromLegacyContainerListStream(memoryStream)); + + // Verify the exception message contains information about the suspicious type + Assert.Contains("Suspicious type", exception.Message); + // ReSharper disable once AssignNullToNotNullAttribute + Assert.Contains(typeof(UnmappedTestClass).FullName, exception.Message); + } + + /// + /// A test class that is intentionally not mapped in the LegacySerializationBinder. + /// + [Serializable] + private class UnmappedTestClass + { + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormatHandlers/GreenshotTemplateFormatHandlerTests.cs b/src/Greenshot.Test/Editor/FileFormatHandlers/GreenshotTemplateFormatHandlerTests.cs new file mode 100644 index 000000000..8a1dc6cb1 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormatHandlers/GreenshotTemplateFormatHandlerTests.cs @@ -0,0 +1,170 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.IO; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormatHandlers; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormatHandlers; + +/// +/// Tests for the class. +/// +[Collection("DefaultCollection")] +public class GreenshotTemplateFormatHandlerTests +{ + /// + /// Verifies that calling + /// with a surface containing two arrow containers results in a new file being created. + /// The test also ensures cleanup by deleting the temporary file. + /// + [Fact] + public void SaveTemplateToFile_ShouldSaveAndCreateFile() + { + // Arrange + var surface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + surface.AddElement(new ArrowContainer(surface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }); + surface.AddElement(new ArrowContainer(surface) + { + Left = 110, + Top = 120, + Width = 200, + Height = 150 + }); + var handler = new GreenshotTemplateFormatHandler(); + var tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.gst"); + + try + { + // Act + handler.SaveTemplateToFile(tempFile, surface); + + // Assert + Assert.True(File.Exists(tempFile)); + } + finally + { + // Cleanup + if (File.Exists(tempFile)) + { + File.Delete(tempFile); + } + } + } + + /// + /// Verifies that a template saved with + /// can be successfully loaded using . + /// The test checks if the loaded surface contains the same elements as the original surface. + /// + [Fact] + public void LoadTemplateFromFile_FromSavedTemplate_ShouldLoadSurfaceWithElements() + { + // Arrange + var originalSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + var lineColor = Color.Red; + var arrow1 = new ArrowContainer(originalSurface) + { + Left = 10, + Top = 20, + Width = 100, + Height = 50 + }; + arrow1.SetFieldValue(FieldType.LINE_THICKNESS, 3); + arrow1.SetFieldValue(FieldType.LINE_COLOR, lineColor); + arrow1.SetFieldValue(FieldType.ARROWHEADS, ArrowContainer.ArrowHeadCombination.BOTH); + + var arrow2 = new ArrowContainer(originalSurface) + { + Left = 110, + Top = 120, + Width = 200, + Height = 150 + }; + arrow2.SetFieldValue(FieldType.LINE_THICKNESS, 5); + arrow2.SetFieldValue(FieldType.LINE_COLOR, lineColor); + arrow2.SetFieldValue(FieldType.SHADOW, false); + + originalSurface.AddElement(arrow1); + originalSurface.AddElement(arrow2); + var handler = new GreenshotTemplateFormatHandler(); + var tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.gst"); + + try + { + handler.SaveTemplateToFile(tempFile, originalSurface); + + var newSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + + // Act + handler.LoadTemplateFromFile(tempFile, newSurface); + + // Assert + Assert.Equal(2, newSurface.Elements.Count); + Assert.All(newSurface.Elements, e => Assert.IsType(e)); + + var loadedArrow1 = (ArrowContainer)newSurface.Elements[0]; + Assert.NotNull(loadedArrow1); + Assert.Equal(10, loadedArrow1.Left); + Assert.Equal(20, loadedArrow1.Top); + Assert.Equal(100, loadedArrow1.Width); + Assert.Equal(50, loadedArrow1.Height); + Assert.Equal(3, loadedArrow1.GetFieldValue(FieldType.LINE_THICKNESS)); + var resultLineColor1 = loadedArrow1.GetFieldValue(FieldType.LINE_COLOR); + Assert.True(DtoHelper.CompareColorValue(lineColor, (Color)resultLineColor1), + $"The color values are different. expected:{DtoHelper.ArgbString(lineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor1)}"); + + Assert.Equal(ArrowContainer.ArrowHeadCombination.BOTH, loadedArrow1.GetFieldValue(FieldType.ARROWHEADS)); + + var loadedArrow2 = (ArrowContainer)newSurface.Elements[1]; + Assert.NotNull(loadedArrow2); + Assert.Equal(110, loadedArrow2.Left); + Assert.Equal(120, loadedArrow2.Top); + Assert.Equal(200, loadedArrow2.Width); + Assert.Equal(150, loadedArrow2.Height); + Assert.Equal(5, loadedArrow2.GetFieldValue(FieldType.LINE_THICKNESS)); + var resultLineColor2 = loadedArrow1.GetFieldValue(FieldType.LINE_COLOR); + Assert.True(DtoHelper.CompareColorValue(lineColor, (Color)resultLineColor2), + $"The color values are different. expected:{DtoHelper.ArgbString(lineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor2)}"); + Assert.False((bool)loadedArrow2.GetFieldValue(FieldType.SHADOW)); + } + finally + { + // Cleanup + if (File.Exists(tempFile)) + { + File.Delete(tempFile); + } + } + } +} diff --git a/src/Greenshot.Test/Editor/FileFormatHandlers/LoadGreenshotSurfaceTests.cs b/src/Greenshot.Test/Editor/FileFormatHandlers/LoadGreenshotSurfaceTests.cs new file mode 100644 index 000000000..2a111d0f9 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormatHandlers/LoadGreenshotSurfaceTests.cs @@ -0,0 +1,1140 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Drawing.Filters; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormatHandlers; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormatHandlers; + +/// +/// Contains unit tests for loading and validating various container types and surfaces from Greenshot files using the +/// . +/// +/// Every test methode uses a .greenshot file from the TestData folder, which is expected to be present. +/// For every test method there is a .greenshot file in every supported Greenshot file version. +/// +[Collection("DefaultCollection")] +public class LoadGreenshotSurfaceTests +{ + private readonly GreenshotFileFormatHandler _greenshotFileFormatHandler = new(); + + public static IEnumerable ImageSurfaceTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "Surface_with_Image_800x400.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "Surface_with_Image_800x400.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "Surface_with_Image_800x400.greenshot")]; + } + + [Theory] + [MemberData(nameof(ImageSurfaceTestData))] + public void LoadImageSurfaceFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(0, resultElementList.Count); + } + + public static IEnumerable RectangleContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "RectangleContainer_lt_100_200_wh_150_80.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "RectangleContainer_lt_100_200_wh_150_80.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "RectangleContainer_lt_100_200_wh_150_80.greenshot")]; + } + + [Theory] + [MemberData(nameof(RectangleContainerTestData))] + public void LoadRectangleContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + var rectangleInTestfile = new Rectangle(100, 200, 150, 80); + var rectangleLineThickness = 2; + var rectangleLineColor = Color.Red; + var rectangleFillColor = Color.Transparent; + var rectangleShadow = true; + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var resultRectangleContainer = (RectangleContainer)resultFirstElement; + + Assert.Equal(rectangleInTestfile.Top, resultRectangleContainer.Top); + Assert.Equal(rectangleInTestfile.Left, resultRectangleContainer.Left); + Assert.Equal(rectangleInTestfile.Width, resultRectangleContainer.Width); + Assert.Equal(rectangleInTestfile.Height, resultRectangleContainer.Height); + + var resultAdorerList = resultRectangleContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + var resultLineThickness = resultRectangleContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = resultRectangleContainer.GetFieldValue(FieldType.LINE_COLOR); + var resultFillColor = resultRectangleContainer.GetFieldValue(FieldType.FILL_COLOR); + var resultShadow = resultRectangleContainer.GetFieldValue(FieldType.SHADOW); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(rectangleLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(rectangleLineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(rectangleLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(rectangleFillColor, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(rectangleFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(rectangleShadow, resultShadow); + } + + public static IEnumerable LineContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "LineContainer_lt_200_200_w_400.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "LineContainer_lt_200_200_w_400.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "LineContainer_lt_200_200_w_400.greenshot")]; + } + + [Theory] + [MemberData(nameof(LineContainerTestData))] + public void LoadLineContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + var linePosInTestfile = new Point(200, 200); + var lineWidthInTestfile = 400; + var lineThickness = 4; + var lineColor = Color.Blue; + var lineShadow = false; + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var resultLineContainer = (LineContainer)resultFirstElement; + + Assert.Equal(linePosInTestfile.X, resultLineContainer.Left); + Assert.Equal(linePosInTestfile.Y, resultLineContainer.Top); + Assert.Equal(lineWidthInTestfile, resultLineContainer.Width); + + // LineContainer has no height, so it is set to -1 + Assert.Equal(-1, resultLineContainer.Height); + + var resultAdorerList = resultLineContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 2 Adorners for start and end + Assert.Equal(2, resultAdorerList.Count); + + var resultLineThickness = resultLineContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = resultLineContainer.GetFieldValue(FieldType.LINE_COLOR); + var resultShadow = resultLineContainer.GetFieldValue(FieldType.SHADOW); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(lineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(lineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(lineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(lineShadow, resultShadow); + } + + public static IEnumerable ArrowContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "ArrowContainer_lt_100_200_wh_400_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "ArrowContainer_lt_100_200_wh_400_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "ArrowContainer_lt_100_200_wh_400_100.greenshot")]; + } + + [Theory] + [MemberData(nameof(ArrowContainerTestData))] + public void LoadArrowContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + var arrowPosInTestfile = new Point(100, 200); + var arrowWidthInTestfile = 400; + var arrowHeightInTestfile = 100; + var lineThickness = 3; + var lineColor = Color.Red; + var lineShadow = true; + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var resultArrowContainer = (ArrowContainer)resultFirstElement; + + Assert.Equal(arrowPosInTestfile.X, resultArrowContainer.Left); + Assert.Equal(arrowPosInTestfile.Y, resultArrowContainer.Top); + Assert.Equal(arrowWidthInTestfile, resultArrowContainer.Width); + Assert.Equal(arrowHeightInTestfile, resultArrowContainer.Height); + + var resultAdorerList = resultArrowContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 2 Adorners for start and end + Assert.Equal(2, resultAdorerList.Count); + + var resultLineThickness = resultArrowContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = resultArrowContainer.GetFieldValue(FieldType.LINE_COLOR); + var resultShadow = resultArrowContainer.GetFieldValue(FieldType.SHADOW); + var resultArrowheads = resultArrowContainer.GetFieldValue(FieldType.ARROWHEADS); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(lineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(lineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(lineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(lineShadow, resultShadow); + + Assert.NotNull(resultArrowheads); + Assert.IsType(resultArrowheads); + Assert.Equal(ArrowContainer.ArrowHeadCombination.BOTH, resultArrowheads); + } + + public static IEnumerable EllipseContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "EllipseContainer_lt_200_200_wh_400_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "EllipseContainer_lt_200_200_wh_400_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "EllipseContainer_lt_200_200_wh_400_100.greenshot")]; + } + + [Theory] + [MemberData(nameof(EllipseContainerTestData))] + public void LoadEllipseContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + var ellipseRectangleInTestfile = new Rectangle(200, 200, 400, 100); + var lineThickness = 6; + var lineColor = Color.FromArgb(255,0,255,0); + var fillColor = Color.Blue; + var lineShadow = false; + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var resultEllipseContainer = (EllipseContainer)resultFirstElement; + + Assert.Equal(ellipseRectangleInTestfile.Top, resultEllipseContainer.Top); + Assert.Equal(ellipseRectangleInTestfile.Left, resultEllipseContainer.Left); + Assert.Equal(ellipseRectangleInTestfile.Width, resultEllipseContainer.Width); + Assert.Equal(ellipseRectangleInTestfile.Height, resultEllipseContainer.Height); + + var resultAdorerList = resultEllipseContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + var resultLineThickness = resultEllipseContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = resultEllipseContainer.GetFieldValue(FieldType.LINE_COLOR); + var resultFillColor = resultEllipseContainer.GetFieldValue(FieldType.FILL_COLOR); + var resultShadow = resultEllipseContainer.GetFieldValue(FieldType.SHADOW); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(lineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(lineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(lineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(fillColor, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(fillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(lineShadow, resultShadow); + } + + public static IEnumerable FreehandContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "FreehandContainer_with_4_points.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "FreehandContainer_with_4_points.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "FreehandContainer_with_4_points.greenshot")]; + } + + [Theory] + [MemberData(nameof(FreehandContainerTestData))] + public void LoadFreehandContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + // The Rectangle of the FreehandContainer is inherited from the Parent-Surface.Image, not from DrawingBounds. + var freehandRectangleInTestfile = new Rectangle(0, 0, 800,400); + var expectedCapturePoints = new List + { + new Point(240, 171), + new Point(246, 170), + new Point(246, 170), + new Point(252, 170) + }; + var lineThickness = 3; + var lineColor = Color.Red; + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + + Assert.Equal(freehandRectangleInTestfile.Top, ((FreehandContainer)resultFirstElement).Top); + Assert.Equal(freehandRectangleInTestfile.Left, ((FreehandContainer)resultFirstElement).Left); + Assert.Equal(freehandRectangleInTestfile.Width, ((FreehandContainer)resultFirstElement).Width); + Assert.Equal(freehandRectangleInTestfile.Height, ((FreehandContainer)resultFirstElement).Height); + + var resultPoints = ((FreehandContainer)resultFirstElement).CapturePoints; + Assert.NotNull(resultPoints); + Assert.Equal(expectedCapturePoints.Count, resultPoints.Count); + for (int i = 0; i < expectedCapturePoints.Count; i++) + { + Assert.Equal(expectedCapturePoints[i], resultPoints[i]); + } + + var resultLineThickness = ((FreehandContainer)resultFirstElement).GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = ((FreehandContainer)resultFirstElement).GetFieldValue(FieldType.LINE_COLOR); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(lineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(lineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(lineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + } + + public static IEnumerable TextContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "TextContainer_lt_300_200_wh_300_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "TextContainer_lt_300_200_wh_300_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "TextContainer_lt_300_200_wh_300_100.greenshot")]; + } + + [Theory] + [MemberData(nameof(TextContainerTestData))] + public void LoadTextContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + var textContainerRectInTestfile = new Rectangle(300, 200, 300, 100); + var expectedText = "Hello Greenshot"; + var expectedLineThickness = 4; + var expectedLineColor = Color.Red; + var expectedFillColor = Color.FromArgb(255,204,255,229); + var expectedShadow = true; + var expectedFontFamily = "Arial"; + var expectedFontSize = 30f; + var expectedFontBold = true; + var expectedFontItalic = false; + var expectedHorizontalAlignment = StringAlignment.Far; + var expectedVerticalAlignment = StringAlignment.Far; + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var textContainer = (TextContainer)resultFirstElement; + + Assert.Equal(textContainerRectInTestfile.Top, textContainer.Top); + Assert.Equal(textContainerRectInTestfile.Left, textContainer.Left); + Assert.Equal(textContainerRectInTestfile.Width, textContainer.Width); + Assert.Equal(textContainerRectInTestfile.Height, textContainer.Height); + + var resultAdorerList = textContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + Assert.Equal(expectedText, textContainer.Text); + + var resultLineThickness = textContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = textContainer.GetFieldValue(FieldType.LINE_COLOR); + var resultFillColor = textContainer.GetFieldValue(FieldType.FILL_COLOR); + var resultShadow = textContainer.GetFieldValue(FieldType.SHADOW); + var resultFontFamily = textContainer.GetFieldValue(FieldType.FONT_FAMILY); + var resultFontSize = textContainer.GetFieldValue(FieldType.FONT_SIZE); + var resultFontBold = textContainer.GetFieldValue(FieldType.FONT_BOLD); + var resultFontItalic = textContainer.GetFieldValue(FieldType.FONT_ITALIC); + var resultHorizontalAlignment = textContainer.GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); + var resultVerticalAlignment = textContainer.GetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(expectedLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(expectedLineColor, (Color)resultLineColor), + $"The line color values are different. expected:{DtoHelper.ArgbString(expectedLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(expectedFillColor, (Color)resultFillColor), + $"The fill color values are different. expected:{DtoHelper.ArgbString(expectedFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(expectedShadow, resultShadow); + + Assert.NotNull(resultFontFamily); + Assert.IsType(resultFontFamily); + Assert.Equal(expectedFontFamily, resultFontFamily); + + Assert.NotNull(resultFontSize); + Assert.IsType(resultFontSize); + Assert.Equal(expectedFontSize, resultFontSize); + + Assert.NotNull(resultFontBold); + Assert.IsType(resultFontBold); + Assert.Equal(expectedFontBold, resultFontBold); + + Assert.NotNull(resultFontItalic); + Assert.IsType(resultFontItalic); + Assert.Equal(expectedFontItalic, resultFontItalic); + + Assert.NotNull(resultHorizontalAlignment); + Assert.IsType(resultHorizontalAlignment); + Assert.Equal(expectedHorizontalAlignment, resultHorizontalAlignment); + + Assert.NotNull(resultVerticalAlignment); + Assert.IsType(resultVerticalAlignment); + Assert.Equal(expectedVerticalAlignment, resultVerticalAlignment); + } + + public static IEnumerable SpeechbubbleContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot")]; + } + + [Theory] + [MemberData(nameof(SpeechbubbleContainerTestData))] + public void LoadSpeechbubbleContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + var speechbubbleRectInTestfile = new Rectangle(200, 200, 150, 80); + var expectedText = "Point on 100x300"; + var expectedLineThickness = 3; + var expectedLineColor = Color.Blue; + var expectedFillColor = Color.White; + var expectedShadow = true; + var expectedFontFamily = "Arial"; + var expectedFontSize = 20f; + var expectedFontBold = false; + var expectedFontItalic = false; + var expectedHorizontalAlignment = StringAlignment.Center; + var expectedVerticalAlignment = StringAlignment.Center; + var expectedTargetPoint = new Point(100, 300); + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var speechbubbleContainer = (SpeechbubbleContainer)resultFirstElement; + + Assert.Equal(speechbubbleRectInTestfile.Top, speechbubbleContainer.Top); + Assert.Equal(speechbubbleRectInTestfile.Left, speechbubbleContainer.Left); + Assert.Equal(speechbubbleRectInTestfile.Width, speechbubbleContainer.Width); + Assert.Equal(speechbubbleRectInTestfile.Height, speechbubbleContainer.Height); + + Assert.Equal(expectedText, speechbubbleContainer.Text); + Assert.Equal(expectedTargetPoint, speechbubbleContainer.StoredTargetGripperLocation); + + var resultAdorerList = speechbubbleContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + 1 Target Adorner + Assert.Equal(9, resultAdorerList.Count); + + Assert.Equal(expectedTargetPoint.X, speechbubbleContainer.TargetAdorner.Location.X); + Assert.Equal(expectedTargetPoint.Y, speechbubbleContainer.TargetAdorner.Location.Y); + + var resultLineThickness = speechbubbleContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = speechbubbleContainer.GetFieldValue(FieldType.LINE_COLOR); + var resultFillColor = speechbubbleContainer.GetFieldValue(FieldType.FILL_COLOR); + var resultShadow = speechbubbleContainer.GetFieldValue(FieldType.SHADOW); + var resultFontFamily = speechbubbleContainer.GetFieldValue(FieldType.FONT_FAMILY); + var resultFontSize = speechbubbleContainer.GetFieldValue(FieldType.FONT_SIZE); + var resultFontBold = speechbubbleContainer.GetFieldValue(FieldType.FONT_BOLD); + var resultFontItalic = speechbubbleContainer.GetFieldValue(FieldType.FONT_ITALIC); + var resultHorizontalAlignment = speechbubbleContainer.GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); + var resultVerticalAlignment = speechbubbleContainer.GetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(expectedLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(expectedLineColor, (Color)resultLineColor), + $"The line color values are different. expected:{DtoHelper.ArgbString(expectedLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(expectedFillColor, (Color)resultFillColor), + $"The fill color values are different. expected:{DtoHelper.ArgbString(expectedFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(expectedShadow, resultShadow); + + Assert.NotNull(resultFontFamily); + Assert.IsType(resultFontFamily); + Assert.Equal(expectedFontFamily, resultFontFamily); + + Assert.NotNull(resultFontSize); + Assert.IsType(resultFontSize); + Assert.Equal(expectedFontSize, resultFontSize); + + Assert.NotNull(resultFontBold); + Assert.IsType(resultFontBold); + Assert.Equal(expectedFontBold, resultFontBold); + + Assert.NotNull(resultFontItalic); + Assert.IsType(resultFontItalic); + Assert.Equal(expectedFontItalic, resultFontItalic); + + Assert.NotNull(resultHorizontalAlignment); + Assert.IsType(resultHorizontalAlignment); + Assert.Equal(expectedHorizontalAlignment, resultHorizontalAlignment); + + Assert.NotNull(resultVerticalAlignment); + Assert.IsType(resultVerticalAlignment); + Assert.Equal(expectedVerticalAlignment, resultVerticalAlignment); + } + + public static IEnumerable HighlightContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot")]; + } + + [Theory] + [MemberData(nameof(HighlightContainerTestData))] + public void LoadHighlightContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + var highlightRectInTestfile = new Rectangle(310, 70, 195, 60); + var expectedPreparedFilter = FilterContainer.PreparedFilter.TEXT_HIGHTLIGHT; + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var highlightContainer = (HighlightContainer)resultFirstElement; + + Assert.Equal(highlightRectInTestfile.Top, highlightContainer.Top); + Assert.Equal(highlightRectInTestfile.Left, highlightContainer.Left); + Assert.Equal(highlightRectInTestfile.Width, highlightContainer.Width); + Assert.Equal(highlightRectInTestfile.Height, highlightContainer.Height); + + var resultAdorerList = highlightContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + var resultPreparedFilter = highlightContainer.GetFieldValue(FieldType.PREPARED_FILTER_HIGHLIGHT); + + Assert.NotNull(resultPreparedFilter); + Assert.IsType(resultPreparedFilter); + Assert.Equal(expectedPreparedFilter, resultPreparedFilter); + + // Check the actual filter applied based on the prepared filter + Assert.NotNull(highlightContainer.Filters); + Assert.Equal(1, highlightContainer.Filters.Count); + Assert.IsType(highlightContainer.Filters[0]); + } + + public static IEnumerable ObfuscateContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot")]; + } + + [Theory] + [MemberData(nameof(ObfuscateContainerTestData))] + public void LoadObfuscateContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + var obfuscateRectInTestfile = new Rectangle(130, 70, 180, 70); + var expectedPreparedFilter = FilterContainer.PreparedFilter.BLUR; + var expectedLineThickness = 1; + var expectedLineColor = Color.FromArgb(255,0,255,0); + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var obfuscateContainer = (ObfuscateContainer)resultFirstElement; + + Assert.Equal(obfuscateRectInTestfile.Top, obfuscateContainer.Top); + Assert.Equal(obfuscateRectInTestfile.Left, obfuscateContainer.Left); + Assert.Equal(obfuscateRectInTestfile.Width, obfuscateContainer.Width); + Assert.Equal(obfuscateRectInTestfile.Height, obfuscateContainer.Height); + + var resultAdorerList = obfuscateContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + var resultLineThickness = obfuscateContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = obfuscateContainer.GetFieldValue(FieldType.LINE_COLOR); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(expectedLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(expectedLineColor, (Color)resultLineColor), + $"The line color values are different. expected:{DtoHelper.ArgbString(expectedLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + var resultPreparedFilter = obfuscateContainer.GetFieldValue(FieldType.PREPARED_FILTER_OBFUSCATE); + + Assert.NotNull(resultPreparedFilter); + Assert.IsType(resultPreparedFilter); + Assert.Equal(expectedPreparedFilter, resultPreparedFilter); + + // Check the actual filter applied based on the prepared filter + Assert.NotNull(obfuscateContainer.Filters); + Assert.Equal(1, obfuscateContainer.Filters.Count); + Assert.IsType(obfuscateContainer.Filters[0]); + } + + public static IEnumerable IconContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "IconContainer_lt_400_200_wh_32_32.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "IconContainer_lt_400_200_wh_32_32.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "IconContainer_lt_400_200_wh_32_32.greenshot")]; + } + + [Theory] + [MemberData(nameof(IconContainerTestData))] + public void LoadIconContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + var iconRectInTestfile = new Rectangle(400, 200, 32, 32); + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var iconContainer = (IconContainer)resultFirstElement; + + Assert.Equal(iconRectInTestfile.Top, iconContainer.Top); + Assert.Equal(iconRectInTestfile.Left, iconContainer.Left); + Assert.Equal(iconRectInTestfile.Width, iconContainer.Width); + Assert.Equal(iconRectInTestfile.Height, iconContainer.Height); + + var resultAdorerList = iconContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + Assert.NotNull(iconContainer.Icon); + Assert.Equal(iconRectInTestfile.Size, iconContainer.Icon.Size); + } + + public static IEnumerable StepLabelContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "StepLabelContainer_lt_200_200_lt_500_300.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "StepLabelContainer_lt_200_200_lt_500_300.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "StepLabelContainer_lt_200_200_lt_500_300.greenshot")]; + } + + [Theory] + [MemberData(nameof(StepLabelContainerTestData))] + public void LoadStepLabelContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + var stepLabel1Pos = new Point(200, 200); + var stepLabel2Pos = new Point(500, 300); + var stepLabelSize = new Size(30, 30); + var expectedFillColor = Color.Blue; + var expectedLineColor = Color.White; + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(2, resultElementList.Count); + + // Assertions for the first StepLabelContainer + var resultFirstElement = resultElementList[0]; + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var stepLabelContainer1 = (StepLabelContainer)resultFirstElement; + + Assert.Equal(stepLabel1Pos.X, stepLabelContainer1.Left); + Assert.Equal(stepLabel1Pos.Y, stepLabelContainer1.Top); + Assert.Equal(stepLabelSize.Width, stepLabelContainer1.Width); + Assert.Equal(stepLabelSize.Height, stepLabelContainer1.Height); + Assert.Equal(1, stepLabelContainer1.Number); + + var resultFillColor1 = stepLabelContainer1.GetFieldValue(FieldType.FILL_COLOR); + var resultLineColor1 = stepLabelContainer1.GetFieldValue(FieldType.LINE_COLOR); + + Assert.NotNull(resultFillColor1); + Assert.IsType(resultFillColor1); + Assert.True(DtoHelper.CompareColorValue(expectedFillColor, (Color)resultFillColor1), + $"The fill color values for StepLabelContainer 1 are different. expected:{DtoHelper.ArgbString(expectedFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor1)}"); + + Assert.NotNull(resultLineColor1); + Assert.IsType(resultLineColor1); + Assert.True(DtoHelper.CompareColorValue(expectedLineColor, (Color)resultLineColor1), + $"The line color values for StepLabelContainer 1 are different. expected:{DtoHelper.ArgbString(expectedLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor1)}"); + + + // Assertions for the second StepLabelContainer + var resultSecondElement = resultElementList[1]; + Assert.NotNull(resultSecondElement); + Assert.IsType(resultSecondElement); + var stepLabelContainer2 = (StepLabelContainer)resultSecondElement; + + Assert.Equal(stepLabel2Pos.X, stepLabelContainer2.Left); + Assert.Equal(stepLabel2Pos.Y, stepLabelContainer2.Top); + Assert.Equal(stepLabelSize.Width, stepLabelContainer2.Width); + Assert.Equal(stepLabelSize.Height, stepLabelContainer2.Height); + Assert.Equal(2, stepLabelContainer2.Number); + + var resultFillColor2 = stepLabelContainer2.GetFieldValue(FieldType.FILL_COLOR); + var resultLineColor2 = stepLabelContainer2.GetFieldValue(FieldType.LINE_COLOR); + + Assert.NotNull(resultFillColor2); + Assert.IsType(resultFillColor2); + Assert.True(DtoHelper.CompareColorValue(expectedFillColor, (Color)resultFillColor2), + $"The fill color values for StepLabelContainer 2 are different. expected:{DtoHelper.ArgbString(expectedFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor2)}"); + + Assert.NotNull(resultLineColor2); + Assert.IsType(resultLineColor2); + Assert.True(DtoHelper.CompareColorValue(expectedLineColor, (Color)resultLineColor2), + $"The line color values for StepLabelContainer 2 are different. expected:{DtoHelper.ArgbString(expectedLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor2)}"); + } + + public static IEnumerable ImageContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "ImageContainer_lt_300_200_wh_100_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "ImageContainer_lt_300_200_wh_100_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "ImageContainer_lt_300_200_wh_100_100.greenshot")]; + } + + [Theory] + [MemberData(nameof(ImageContainerTestData))] + public void LoadImageContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + // The ImageContainer has been scaled down from (256, 256) to (100, 100) + var containerImageSize = new Size(256, 256); + var imageRectInTestfile = new Rectangle(300, 200, 100, 100); + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var imageContainer = (ImageContainer)resultFirstElement; + + Assert.Equal(imageRectInTestfile.Top, imageContainer.Top); + Assert.Equal(imageRectInTestfile.Left, imageContainer.Left); + Assert.Equal(imageRectInTestfile.Width, imageContainer.Width); + Assert.Equal(imageRectInTestfile.Height, imageContainer.Height); + + var resultAdorerList = imageContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + Assert.NotNull(imageContainer.Image); + Assert.Equal(containerImageSize, imageContainer.Image.Size); + } + + public static IEnumerable SvgContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "SvgContainer_lt_300_200_wh_120_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "SvgContainer_lt_300_200_wh_120_100.greenshot")]; + } + + [Theory] + [MemberData(nameof(SvgContainerTestData))] + public void LoadSvgContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + var svgRectInTestfile = new Rectangle(300, 200, 120, 100); + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var svgContainer = (SvgContainer)resultFirstElement; + + Assert.Equal(svgRectInTestfile.Top, svgContainer.Top); + Assert.Equal(svgRectInTestfile.Left, svgContainer.Left); + Assert.Equal(svgRectInTestfile.Width, svgContainer.Width); + Assert.Equal(svgRectInTestfile.Height, svgContainer.Height); + + var resultAdorerList = svgContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + Assert.Equal(4, resultAdorerList.Count); + + Assert.NotNull(svgContainer.SvgContent); + Assert.True( svgContainer.SvgContent.Length > 0); + } + + public static IEnumerable MetafileContainerTestData() + { + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "MetafileContainer_lt_300_200_wh_120_100.greenshot")]; + yield return [Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "MetafileContainer_lt_300_200_wh_120_100.greenshot")]; + } + + [Theory] + [MemberData(nameof(MetafileContainerTestData))] + public void LoadMetafileContainerFromGreenshotFile(string filePath) + { + // Arrange + var imageSizeInTestfile = new Size(800, 400); + var metafileRectInTestfile = new Rectangle(300, 200, 120, 100); + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.Equal(imageSizeInTestfile, resultSurface.Image.Size); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var metafileContainer = (MetafileContainer)resultFirstElement; + + Assert.Equal(metafileRectInTestfile.Top, metafileContainer.Top); + Assert.Equal(metafileRectInTestfile.Left, metafileContainer.Left); + Assert.Equal(metafileRectInTestfile.Width, metafileContainer.Width); + Assert.Equal(metafileRectInTestfile.Height, metafileContainer.Height); + + var resultAdorerList = metafileContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + Assert.Equal(4, resultAdorerList.Count); + + Assert.NotNull(metafileContainer.MetafileContent); + Assert.True(metafileContainer.MetafileContent.Length > 0); + } + + /// + /// Tests to load a Greenshot surface from a file created with version 01.02, ensuring that the surface + /// contains the expected number and types of drawable containers. + /// + /// The test file contains all possible container types for version 01.02 + [Fact] + public void LoadFromV0102GreenshotFileWithDifferentContainer() + { + // Arrange + string filePath = Path.Combine("TestData", "Greenshotfile", "File_Version_1.02", "Surface_with_11_different_DrawableContainer.greenshot"); + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + + var resultElementList = resultSurface.Elements; + + Assert.NotNull(resultElementList); + Assert.Equal(11, resultElementList.Count); + + Assert.IsType(resultElementList[0]); + Assert.IsType(resultElementList[1]); + Assert.IsType(resultElementList[2]); + Assert.IsType(resultElementList[3]); + Assert.IsType(resultElementList[4]); + Assert.IsType(resultElementList[5]); + Assert.IsType(resultElementList[6]); + Assert.IsType(resultElementList[7]); + Assert.IsType(resultElementList[8]); + Assert.IsType(resultElementList[9]); + Assert.IsType(resultElementList[10]); + } + + /// + /// Tests to load a Greenshot surface from a file created with version 01.03, ensuring that the surface + /// contains the expected number and types of drawable containers. + /// + /// The test file contains all possible container types for version 01.03 + [Fact] + public void LoadFromV0103GreenshotFileWithDifferentContainer() + { + // Arrange + string filePath = Path.Combine("TestData", "Greenshotfile", "File_Version_1.03", "Surface_with_14_different_DrawableContainer.greenshot"); + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + + var resultElementList = resultSurface.Elements; + + Assert.NotNull(resultElementList); + Assert.Equal(14, resultElementList.Count); + + Assert.IsType(resultElementList[0]); + Assert.IsType(resultElementList[1]); + Assert.IsType(resultElementList[2]); + Assert.IsType(resultElementList[3]); + Assert.IsType(resultElementList[4]); + Assert.IsType(resultElementList[5]); + Assert.IsType(resultElementList[6]); + Assert.IsType(resultElementList[7]); + Assert.IsType(resultElementList[8]); + Assert.IsType(resultElementList[9]); + Assert.IsType(resultElementList[10]); + Assert.IsType(resultElementList[11]); + Assert.IsType(resultElementList[12]); + Assert.IsType(resultElementList[13]); + } + /// + /// Tests to load a Greenshot surface from a file created with version 02.01, ensuring that the surface + /// contains the expected number and types of drawable containers. + /// + /// The test file contains all possible container types for version 02.01 + [Fact] + public void LoadFromV0201GreenshotFileWithDifferentContainer() + { + // Arrange + string filePath = Path.Combine("TestData", "Greenshotfile", "File_Version_2.01", "Surface_with_14_different_DrawableContainer.greenshot"); + + // Act + var resultSurface = _greenshotFileFormatHandler.LoadGreenshotSurface(filePath); + + // Assert + Assert.NotNull(resultSurface); + + var resultElementList = resultSurface.Elements; + + Assert.NotNull(resultElementList); + Assert.Equal(14, resultElementList.Count); + + Assert.IsType(resultElementList[0]); + Assert.IsType(resultElementList[1]); + Assert.IsType(resultElementList[2]); + Assert.IsType(resultElementList[3]); + Assert.IsType(resultElementList[4]); + Assert.IsType(resultElementList[5]); + Assert.IsType(resultElementList[6]); + Assert.IsType(resultElementList[7]); + Assert.IsType(resultElementList[8]); + Assert.IsType(resultElementList[9]); + Assert.IsType(resultElementList[10]); + Assert.IsType(resultElementList[11]); + Assert.IsType(resultElementList[12]); + Assert.IsType(resultElementList[13]); + } +} \ No newline at end of file diff --git a/src/Greenshot.Test/Editor/FileFormatHandlers/LoadTemplateFromFileTest.cs b/src/Greenshot.Test/Editor/FileFormatHandlers/LoadTemplateFromFileTest.cs new file mode 100644 index 000000000..b74ba08a4 --- /dev/null +++ b/src/Greenshot.Test/Editor/FileFormatHandlers/LoadTemplateFromFileTest.cs @@ -0,0 +1,1103 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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.Drawing; +using System.IO; +using System.Linq; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.FileFormat.Dto; +using Greenshot.Editor.FileFormatHandlers; +using Greenshot.Editor.Drawing.Filters; +using Xunit; + +namespace Greenshot.Test.Editor.FileFormatHandlers; + +/// +/// Contains unit tests for loading and validating various container types from Greenshot template using the +/// . +/// +/// Every test methode uses a .gst file from the TestData folder, which is expected to be present. +/// For every test method there is a .gst file in every supported Greenshot file version. +/// +[Collection("DefaultCollection")] +public class LoadTemplateFromFileTest +{ + private readonly GreenshotTemplateFormatHandler _greenshotTemplateFormatHandler = new(); + + + public static IEnumerable ArrowContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "ArrowContainer_lt_100_200_wh_400_100.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "ArrowContainer_lt_100_200_wh_400_100.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "ArrowContainer_lt_100_200_wh_400_100.gst")]; + } + + [Theory] + [MemberData(nameof(ArrowContainerTestData))] + public void LoadArrowContainerFromGreenshotTemplate(string filePath) + { + // Arrange + + var image = new Bitmap(800, 400); + var arrowPosInTestfile = new Point(100, 200); + var arrowWidthInTestfile = 400; + var arrowHeightInTestfile = 100; + var lineThickness = 3; + var lineColor = Color.Red; + var lineShadow = true; + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var resultArrowContainer = (ArrowContainer)resultFirstElement; + + Assert.Equal(arrowPosInTestfile.X, resultArrowContainer.Left); + Assert.Equal(arrowPosInTestfile.Y, resultArrowContainer.Top); + Assert.Equal(arrowWidthInTestfile, resultArrowContainer.Width); + Assert.Equal(arrowHeightInTestfile, resultArrowContainer.Height); + + var resultAdorerList = resultArrowContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 2 Adorners for start and end + Assert.Equal(2, resultAdorerList.Count); + + var resultLineThickness = resultArrowContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = resultArrowContainer.GetFieldValue(FieldType.LINE_COLOR); + var resultShadow = resultArrowContainer.GetFieldValue(FieldType.SHADOW); + var resultArrowheads = resultArrowContainer.GetFieldValue(FieldType.ARROWHEADS); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(lineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(lineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(lineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(lineShadow, resultShadow); + + Assert.NotNull(resultArrowheads); + Assert.IsType(resultArrowheads); + Assert.Equal(ArrowContainer.ArrowHeadCombination.BOTH, resultArrowheads); + } + + public static IEnumerable RectangleContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "RectangleContainer_lt_100_200_wh_150_80.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "RectangleContainer_lt_100_200_wh_150_80.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "RectangleContainer_lt_100_200_wh_150_80.gst")]; + } + + [Theory] + [MemberData(nameof(RectangleContainerTestData))] + public void LoadRectangleContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var rectangleInTestfile = new Rectangle(100, 200, 150, 80); + var rectangleLineThickness = 2; + var rectangleLineColor = Color.Red; + var rectangleFillColor = Color.Transparent; + var rectangleShadow = true; + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var resultRectangleContainer = (RectangleContainer)resultFirstElement; + + Assert.Equal(rectangleInTestfile.Top, resultRectangleContainer.Top); + Assert.Equal(rectangleInTestfile.Left, resultRectangleContainer.Left); + Assert.Equal(rectangleInTestfile.Width, resultRectangleContainer.Width); + Assert.Equal(rectangleInTestfile.Height, resultRectangleContainer.Height); + + var resultAdorerList = resultRectangleContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + var resultLineThickness = resultRectangleContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = resultRectangleContainer.GetFieldValue(FieldType.LINE_COLOR); + var resultFillColor = resultRectangleContainer.GetFieldValue(FieldType.FILL_COLOR); + var resultShadow = resultRectangleContainer.GetFieldValue(FieldType.SHADOW); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(rectangleLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(rectangleLineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(rectangleLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(rectangleFillColor, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(rectangleFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(rectangleShadow, resultShadow); + } + + public static IEnumerable LineContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "LineContainer_lt_200_200_w_400.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "LineContainer_lt_200_200_w_400.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "LineContainer_lt_200_200_w_400.gst")]; + } + + [Theory] + [MemberData(nameof(LineContainerTestData))] + public void LoadLineContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var linePosInTestfile = new Point(200, 200); + var lineWidthInTestfile = 400; + var lineThickness = 4; + var lineColor = Color.Blue; + var lineShadow = false; + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var resultLineContainer = (LineContainer)resultFirstElement; + + Assert.Equal(linePosInTestfile.X, resultLineContainer.Left); + Assert.Equal(linePosInTestfile.Y, resultLineContainer.Top); + Assert.Equal(lineWidthInTestfile, resultLineContainer.Width); + + // LineContainer has no height, so it is set to -1 + Assert.Equal(-1, resultLineContainer.Height); + + var resultAdorerList = resultLineContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 2 Adorners for start and end + Assert.Equal(2, resultAdorerList.Count); + + var resultLineThickness = resultLineContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = resultLineContainer.GetFieldValue(FieldType.LINE_COLOR); + var resultShadow = resultLineContainer.GetFieldValue(FieldType.SHADOW); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(lineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(lineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(lineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(lineShadow, resultShadow); + } + + public static IEnumerable EllipseContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "EllipseContainer_lt_200_200_wh_400_100.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "EllipseContainer_lt_200_200_wh_400_100.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "EllipseContainer_lt_200_200_wh_400_100.gst")]; + } + + [Theory] + [MemberData(nameof(EllipseContainerTestData))] + public void LoadEllipseContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var ellipseRectangleInTestfile = new Rectangle(200, 200, 400, 100); + var lineThickness = 6; + var lineColor = Color.FromArgb(255,0,255,0); + var fillColor = Color.Blue; + var lineShadow = false; + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var resultEllipseContainer = (EllipseContainer)resultFirstElement; + + Assert.Equal(ellipseRectangleInTestfile.Top, resultEllipseContainer.Top); + Assert.Equal(ellipseRectangleInTestfile.Left, resultEllipseContainer.Left); + Assert.Equal(ellipseRectangleInTestfile.Width, resultEllipseContainer.Width); + Assert.Equal(ellipseRectangleInTestfile.Height, resultEllipseContainer.Height); + + var resultAdorerList = resultEllipseContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + var resultLineThickness = resultEllipseContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = resultEllipseContainer.GetFieldValue(FieldType.LINE_COLOR); + var resultFillColor = resultEllipseContainer.GetFieldValue(FieldType.FILL_COLOR); + var resultShadow = resultEllipseContainer.GetFieldValue(FieldType.SHADOW); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(lineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(lineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(lineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(fillColor, (Color)resultFillColor), + $"The color values are different. expected:{DtoHelper.ArgbString(fillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(lineShadow, resultShadow); + } + + public static IEnumerable FreehandContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "FreehandContainer_with_4_points.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "FreehandContainer_with_4_points.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "FreehandContainer_with_4_points.gst")]; + } + + [Theory] + [MemberData(nameof(FreehandContainerTestData))] + public void LoadFreehandContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var freehandRectangleInTestfile = new Rectangle(0, 0, 800,400); + var expectedCapturePoints = new List + { + new Point(240, 171), + new Point(246, 170), + new Point(246, 170), + new Point(252, 170) + }; + var lineThickness = 3; + var lineColor = Color.Red; + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + + Assert.Equal(freehandRectangleInTestfile.Top, ((FreehandContainer)resultFirstElement).Top); + Assert.Equal(freehandRectangleInTestfile.Left, ((FreehandContainer)resultFirstElement).Left); + Assert.Equal(freehandRectangleInTestfile.Width, ((FreehandContainer)resultFirstElement).Width); + Assert.Equal(freehandRectangleInTestfile.Height, ((FreehandContainer)resultFirstElement).Height); + + var resultPoints = ((FreehandContainer)resultFirstElement).CapturePoints; + Assert.NotNull(resultPoints); + Assert.Equal(expectedCapturePoints.Count, resultPoints.Count); + for (int i = 0; i < expectedCapturePoints.Count; i++) + { + Assert.Equal(expectedCapturePoints[i], resultPoints[i]); + } + + var resultLineThickness = ((FreehandContainer)resultFirstElement).GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = ((FreehandContainer)resultFirstElement).GetFieldValue(FieldType.LINE_COLOR); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(lineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(lineColor, (Color)resultLineColor), + $"The color values are different. expected:{DtoHelper.ArgbString(lineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + } + + public static IEnumerable TextContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "TextContainer_lt_300_200_wh_300_100.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "TextContainer_lt_300_200_wh_300_100.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "TextContainer_lt_300_200_wh_300_100.gst")]; + } + + [Theory] + [MemberData(nameof(TextContainerTestData))] + public void LoadTextContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var textContainerRectInTestfile = new Rectangle(300, 200, 300, 100); + var expectedText = "Hello Greenshot"; + var expectedLineThickness = 4; + var expectedLineColor = Color.Red; + var expectedFillColor = Color.FromArgb(255,204,255,229); + var expectedShadow = true; + var expectedFontFamily = "Arial"; + var expectedFontSize = 30f; + var expectedFontBold = true; + var expectedFontItalic = false; + var expectedHorizontalAlignment = StringAlignment.Far; + var expectedVerticalAlignment = StringAlignment.Far; + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var textContainer = (TextContainer)resultFirstElement; + + Assert.Equal(textContainerRectInTestfile.Top, textContainer.Top); + Assert.Equal(textContainerRectInTestfile.Left, textContainer.Left); + Assert.Equal(textContainerRectInTestfile.Width, textContainer.Width); + Assert.Equal(textContainerRectInTestfile.Height, textContainer.Height); + + var resultAdorerList = textContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + Assert.Equal(expectedText, textContainer.Text); + + var resultLineThickness = textContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = textContainer.GetFieldValue(FieldType.LINE_COLOR); + var resultFillColor = textContainer.GetFieldValue(FieldType.FILL_COLOR); + var resultShadow = textContainer.GetFieldValue(FieldType.SHADOW); + var resultFontFamily = textContainer.GetFieldValue(FieldType.FONT_FAMILY); + var resultFontSize = textContainer.GetFieldValue(FieldType.FONT_SIZE); + var resultFontBold = textContainer.GetFieldValue(FieldType.FONT_BOLD); + var resultFontItalic = textContainer.GetFieldValue(FieldType.FONT_ITALIC); + var resultHorizontalAlignment = textContainer.GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); + var resultVerticalAlignment = textContainer.GetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(expectedLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(expectedLineColor, (Color)resultLineColor), + $"The line color values are different. expected:{DtoHelper.ArgbString(expectedLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(expectedFillColor, (Color)resultFillColor), + $"The fill color values are different. expected:{DtoHelper.ArgbString(expectedFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(expectedShadow, resultShadow); + + Assert.NotNull(resultFontFamily); + Assert.IsType(resultFontFamily); + Assert.Equal(expectedFontFamily, resultFontFamily); + + Assert.NotNull(resultFontSize); + Assert.IsType(resultFontSize); + Assert.Equal(expectedFontSize, resultFontSize); + + Assert.NotNull(resultFontBold); + Assert.IsType(resultFontBold); + Assert.Equal(expectedFontBold, resultFontBold); + + Assert.NotNull(resultFontItalic); + Assert.IsType(resultFontItalic); + Assert.Equal(expectedFontItalic, resultFontItalic); + + Assert.NotNull(resultHorizontalAlignment); + Assert.IsType(resultHorizontalAlignment); + Assert.Equal(expectedHorizontalAlignment, resultHorizontalAlignment); + + Assert.NotNull(resultVerticalAlignment); + Assert.IsType(resultVerticalAlignment); + Assert.Equal(expectedVerticalAlignment, resultVerticalAlignment); + } + + public static IEnumerable SpeechbubbleContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "SpeechbubbleContainer_lt_200_200_wh_150_80.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "SpeechbubbleContainer_lt_200_200_wh_150_80.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "SpeechbubbleContainer_lt_200_200_wh_150_80.gst")]; + } + + [Theory] + [MemberData(nameof(SpeechbubbleContainerTestData))] + public void LoadSpeechbubbleContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var speechbubbleRectInTestfile = new Rectangle(200, 200, 150, 80); + var expectedText = "Point on 100x300"; + var expectedLineThickness = 3; + var expectedLineColor = Color.Blue; + var expectedFillColor = Color.White; + var expectedShadow = true; + var expectedFontFamily = "Arial"; + var expectedFontSize = 20f; + var expectedFontBold = false; + var expectedFontItalic = false; + var expectedHorizontalAlignment = StringAlignment.Center; + var expectedVerticalAlignment = StringAlignment.Center; + var expectedTargetPoint = new Point(100, 300); + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var speechbubbleContainer = (SpeechbubbleContainer)resultFirstElement; + + Assert.Equal(speechbubbleRectInTestfile.Top, speechbubbleContainer.Top); + Assert.Equal(speechbubbleRectInTestfile.Left, speechbubbleContainer.Left); + Assert.Equal(speechbubbleRectInTestfile.Width, speechbubbleContainer.Width); + Assert.Equal(speechbubbleRectInTestfile.Height, speechbubbleContainer.Height); + + Assert.Equal(expectedText, speechbubbleContainer.Text); + Assert.Equal(expectedTargetPoint, speechbubbleContainer.StoredTargetGripperLocation); + + var resultAdorerList = speechbubbleContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + 1 Target Adorner + Assert.Equal(9, resultAdorerList.Count); + + Assert.Equal(expectedTargetPoint.X, speechbubbleContainer.TargetAdorner.Location.X); + Assert.Equal(expectedTargetPoint.Y, speechbubbleContainer.TargetAdorner.Location.Y); + + var resultLineThickness = speechbubbleContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = speechbubbleContainer.GetFieldValue(FieldType.LINE_COLOR); + var resultFillColor = speechbubbleContainer.GetFieldValue(FieldType.FILL_COLOR); + var resultShadow = speechbubbleContainer.GetFieldValue(FieldType.SHADOW); + var resultFontFamily = speechbubbleContainer.GetFieldValue(FieldType.FONT_FAMILY); + var resultFontSize = speechbubbleContainer.GetFieldValue(FieldType.FONT_SIZE); + var resultFontBold = speechbubbleContainer.GetFieldValue(FieldType.FONT_BOLD); + var resultFontItalic = speechbubbleContainer.GetFieldValue(FieldType.FONT_ITALIC); + var resultHorizontalAlignment = speechbubbleContainer.GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); + var resultVerticalAlignment = speechbubbleContainer.GetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(expectedLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(expectedLineColor, (Color)resultLineColor), + $"The line color values are different. expected:{DtoHelper.ArgbString(expectedLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + Assert.NotNull(resultFillColor); + Assert.IsType(resultFillColor); + Assert.True(DtoHelper.CompareColorValue(expectedFillColor, (Color)resultFillColor), + $"The fill color values are different. expected:{DtoHelper.ArgbString(expectedFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor)}"); + + Assert.NotNull(resultShadow); + Assert.IsType(resultShadow); + Assert.Equal(expectedShadow, resultShadow); + + Assert.NotNull(resultFontFamily); + Assert.IsType(resultFontFamily); + Assert.Equal(expectedFontFamily, resultFontFamily); + + Assert.NotNull(resultFontSize); + Assert.IsType(resultFontSize); + Assert.Equal(expectedFontSize, resultFontSize); + + Assert.NotNull(resultFontBold); + Assert.IsType(resultFontBold); + Assert.Equal(expectedFontBold, resultFontBold); + + Assert.NotNull(resultFontItalic); + Assert.IsType(resultFontItalic); + Assert.Equal(expectedFontItalic, resultFontItalic); + + Assert.NotNull(resultHorizontalAlignment); + Assert.IsType(resultHorizontalAlignment); + Assert.Equal(expectedHorizontalAlignment, resultHorizontalAlignment); + + Assert.NotNull(resultVerticalAlignment); + Assert.IsType(resultVerticalAlignment); + Assert.Equal(expectedVerticalAlignment, resultVerticalAlignment); + } + + public static IEnumerable HighlightContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "HighlightContainer_TextFilter_lt_310_70_wh_195_60.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "HighlightContainer_TextFilter_lt_310_70_wh_195_60.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "HighlightContainer_TextFilter_lt_310_70_wh_195_60.gst")]; + } + + [Theory] + [MemberData(nameof(HighlightContainerTestData))] + public void LoadHighlightContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var highlightRectInTestfile = new Rectangle(310, 70, 195, 60); + var expectedPreparedFilter = FilterContainer.PreparedFilter.TEXT_HIGHTLIGHT; + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var highlightContainer = (HighlightContainer)resultFirstElement; + + Assert.Equal(highlightRectInTestfile.Top, highlightContainer.Top); + Assert.Equal(highlightRectInTestfile.Left, highlightContainer.Left); + Assert.Equal(highlightRectInTestfile.Width, highlightContainer.Width); + Assert.Equal(highlightRectInTestfile.Height, highlightContainer.Height); + + var resultAdorerList = highlightContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + var resultPreparedFilter = highlightContainer.GetFieldValue(FieldType.PREPARED_FILTER_HIGHLIGHT); + + Assert.NotNull(resultPreparedFilter); + Assert.IsType(resultPreparedFilter); + Assert.Equal(expectedPreparedFilter, resultPreparedFilter); + + // Check the actual filter applied based on the prepared filter + Assert.NotNull(highlightContainer.Filters); + Assert.Equal(1, highlightContainer.Filters.Count); + Assert.IsType(highlightContainer.Filters[0]); + } + + public static IEnumerable ObfuscateContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.gst")]; + } + + [Theory] + [MemberData(nameof(ObfuscateContainerTestData))] + public void LoadObfuscateContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var obfuscateRectInTestfile = new Rectangle(130, 70, 180, 70); + var expectedPreparedFilter = FilterContainer.PreparedFilter.BLUR; + var expectedLineThickness = 1; + var expectedLineColor = Color.FromArgb(255,0,255,0); + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var obfuscateContainer = (ObfuscateContainer)resultFirstElement; + + Assert.Equal(obfuscateRectInTestfile.Top, obfuscateContainer.Top); + Assert.Equal(obfuscateRectInTestfile.Left, obfuscateContainer.Left); + Assert.Equal(obfuscateRectInTestfile.Width, obfuscateContainer.Width); + Assert.Equal(obfuscateRectInTestfile.Height, obfuscateContainer.Height); + + var resultAdorerList = obfuscateContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + var resultLineThickness = obfuscateContainer.GetFieldValue(FieldType.LINE_THICKNESS); + var resultLineColor = obfuscateContainer.GetFieldValue(FieldType.LINE_COLOR); + + Assert.NotNull(resultLineThickness); + Assert.IsType(resultLineThickness); + Assert.Equal(expectedLineThickness, resultLineThickness); + + Assert.NotNull(resultLineColor); + Assert.IsType(resultLineColor); + Assert.True(DtoHelper.CompareColorValue(expectedLineColor, (Color)resultLineColor), + $"The line color values are different. expected:{DtoHelper.ArgbString(expectedLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor)}"); + + var resultPreparedFilter = obfuscateContainer.GetFieldValue(FieldType.PREPARED_FILTER_OBFUSCATE); + + Assert.NotNull(resultPreparedFilter); + Assert.IsType(resultPreparedFilter); + Assert.Equal(expectedPreparedFilter, resultPreparedFilter); + + // Check the actual filter applied based on the prepared filter + Assert.NotNull(obfuscateContainer.Filters); + Assert.Equal(1, obfuscateContainer.Filters.Count); + Assert.IsType(obfuscateContainer.Filters[0]); + } + + public static IEnumerable IconContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "IconContainer_lt_400_200_wh_32_32.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "IconContainer_lt_400_200_wh_32_32.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "IconContainer_lt_400_200_wh_32_32.gst")]; + } + + [Theory] + [MemberData(nameof(IconContainerTestData))] + public void LoadIconContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var iconRectInTestfile = new Rectangle(400, 200, 32, 32); + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var iconContainer = (IconContainer)resultFirstElement; + + Assert.Equal(iconRectInTestfile.Top, iconContainer.Top); + Assert.Equal(iconRectInTestfile.Left, iconContainer.Left); + Assert.Equal(iconRectInTestfile.Width, iconContainer.Width); + Assert.Equal(iconRectInTestfile.Height, iconContainer.Height); + + var resultAdorerList = iconContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + Assert.NotNull(iconContainer.Icon); + Assert.Equal(iconRectInTestfile.Size, iconContainer.Icon.Size); + } + + public static IEnumerable StepLabelContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "StepLabelContainer_lt_200_200_lt_500_300.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "StepLabelContainer_lt_200_200_lt_500_300.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "StepLabelContainer_lt_200_200_lt_500_300.gst")]; + } + + [Theory] + [MemberData(nameof(StepLabelContainerTestData))] + public void LoadStepLabelContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var stepLabel1Pos = new Point(200, 200); + var stepLabel2Pos = new Point(500, 300); + var stepLabelSize = new Size(30, 30); + var expectedFillColor = Color.Blue; + var expectedLineColor = Color.White; + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + + Assert.NotNull(resultElementList); + Assert.Equal(2, resultElementList.Count); + + // Assertions for the first StepLabelContainer + var resultFirstElement = resultElementList[0]; + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var stepLabelContainer1 = (StepLabelContainer)resultFirstElement; + + Assert.Equal(stepLabel1Pos.X, stepLabelContainer1.Left); + Assert.Equal(stepLabel1Pos.Y, stepLabelContainer1.Top); + Assert.Equal(stepLabelSize.Width, stepLabelContainer1.Width); + Assert.Equal(stepLabelSize.Height, stepLabelContainer1.Height); + Assert.Equal(1, stepLabelContainer1.Number); + + var resultFillColor1 = stepLabelContainer1.GetFieldValue(FieldType.FILL_COLOR); + var resultLineColor1 = stepLabelContainer1.GetFieldValue(FieldType.LINE_COLOR); + + Assert.NotNull(resultFillColor1); + Assert.IsType(resultFillColor1); + Assert.True(DtoHelper.CompareColorValue(expectedFillColor, (Color)resultFillColor1), + $"The fill color values for StepLabelContainer 1 are different. expected:{DtoHelper.ArgbString(expectedFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor1)}"); + + Assert.NotNull(resultLineColor1); + Assert.IsType(resultLineColor1); + Assert.True(DtoHelper.CompareColorValue(expectedLineColor, (Color)resultLineColor1), + $"The line color values for StepLabelContainer 1 are different. expected:{DtoHelper.ArgbString(expectedLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor1)}"); + + // Assertions for the second StepLabelContainer + var resultSecondElement = resultElementList[1]; + Assert.NotNull(resultSecondElement); + Assert.IsType(resultSecondElement); + var stepLabelContainer2 = (StepLabelContainer)resultSecondElement; + + Assert.Equal(stepLabel2Pos.X, stepLabelContainer2.Left); + Assert.Equal(stepLabel2Pos.Y, stepLabelContainer2.Top); + Assert.Equal(stepLabelSize.Width, stepLabelContainer2.Width); + Assert.Equal(stepLabelSize.Height, stepLabelContainer2.Height); + Assert.Equal(2, stepLabelContainer2.Number); + + var resultFillColor2 = stepLabelContainer2.GetFieldValue(FieldType.FILL_COLOR); + var resultLineColor2 = stepLabelContainer2.GetFieldValue(FieldType.LINE_COLOR); + + Assert.NotNull(resultFillColor2); + Assert.IsType(resultFillColor2); + Assert.True(DtoHelper.CompareColorValue(expectedFillColor, (Color)resultFillColor2), + $"The fill color values for StepLabelContainer 2 are different. expected:{DtoHelper.ArgbString(expectedFillColor)} result:{DtoHelper.ArgbString((Color)resultFillColor2)}"); + + Assert.NotNull(resultLineColor2); + Assert.IsType(resultLineColor2); + Assert.True(DtoHelper.CompareColorValue(expectedLineColor, (Color)resultLineColor2), + $"The line color values for StepLabelContainer 2 are different. expected:{DtoHelper.ArgbString(expectedLineColor)} result:{DtoHelper.ArgbString((Color)resultLineColor2)}"); + } + + public static IEnumerable ImageContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "ImageContainer_lt_300_200_wh_100_100.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "ImageContainer_lt_300_200_wh_100_100.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "ImageContainer_lt_300_200_wh_100_100.gst")]; + } + + [Theory] + [MemberData(nameof(ImageContainerTestData))] + public void LoadImageContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var containerImageSize = new Size(256, 256); + var imageRectInTestfile = new Rectangle(300, 200, 100, 100); + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var imageContainer = (ImageContainer)resultFirstElement; + + Assert.Equal(imageRectInTestfile.Top, imageContainer.Top); + Assert.Equal(imageRectInTestfile.Left, imageContainer.Left); + Assert.Equal(imageRectInTestfile.Width, imageContainer.Width); + Assert.Equal(imageRectInTestfile.Height, imageContainer.Height); + + var resultAdorerList = imageContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + 4 Adorners for the sides + Assert.Equal(8, resultAdorerList.Count); + + Assert.NotNull(imageContainer.Image); + Assert.Equal(containerImageSize, imageContainer.Image.Size); + } + + public static IEnumerable SvgContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "SvgContainer_lt_300_200_wh_120_100.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "SvgContainer_lt_300_200_wh_120_100.gst")]; + } + + [Theory] + [MemberData(nameof(SvgContainerTestData))] + public void LoadSvgContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var svgRectInTestfile = new Rectangle(300, 200, 120, 100); + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var svgContainer = (SvgContainer)resultFirstElement; + + Assert.Equal(svgRectInTestfile.Top, svgContainer.Top); + Assert.Equal(svgRectInTestfile.Left, svgContainer.Left); + Assert.Equal(svgRectInTestfile.Width, svgContainer.Width); + Assert.Equal(svgRectInTestfile.Height, svgContainer.Height); + + var resultAdorerList = svgContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + Assert.Equal(4, resultAdorerList.Count); + + Assert.NotNull(svgContainer.SvgContent); + Assert.True(svgContainer.SvgContent.Length > 0); + } + + public static IEnumerable MetafileContainerTestData() + { + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "MetafileContainer_lt_300_200_wh_120_100.gst")]; + yield return [Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "MetafileContainer_lt_300_200_wh_120_100.gst")]; + } + + [Theory] + [MemberData(nameof(MetafileContainerTestData))] + public void LoadMetafileContainerFromGreenshotTemplate(string filePath) + { + // Arrange + var image = new Bitmap(800, 400); + var metafileRectInTestfile = new Rectangle(300, 200, 120, 100); + + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + var resultFirstElement = resultSurface.Elements.FirstOrDefault(); + + Assert.NotNull(resultElementList); + Assert.Equal(1, resultElementList.Count); + + Assert.NotNull(resultFirstElement); + Assert.IsType(resultFirstElement); + var metafileContainer = (MetafileContainer)resultFirstElement; + + Assert.Equal(metafileRectInTestfile.Top, metafileContainer.Top); + Assert.Equal(metafileRectInTestfile.Left, metafileContainer.Left); + Assert.Equal(metafileRectInTestfile.Width, metafileContainer.Width); + Assert.Equal(metafileRectInTestfile.Height, metafileContainer.Height); + + var resultAdorerList = metafileContainer.Adorners; + Assert.NotNull(resultAdorerList); + // 4 Adorners for corners + Assert.Equal(4, resultAdorerList.Count); + + Assert.NotNull(metafileContainer.MetafileContent); + Assert.True(metafileContainer.MetafileContent.Length > 0); + } + + [Fact] + public void LoadFromV0102GreenshotTemplateWithDifferentContainer() + { + // Arrange + string filePath = Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.02", "Surface_with_11_different_DrawableContainer.gst"); + var image = new Bitmap(800, 400); + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + + Assert.NotNull(resultElementList); + Assert.Equal(11, resultElementList.Count); + + Assert.IsType(resultElementList[0]); + Assert.IsType(resultElementList[1]); + Assert.IsType(resultElementList[2]); + Assert.IsType(resultElementList[3]); + Assert.IsType(resultElementList[4]); + Assert.IsType(resultElementList[5]); + Assert.IsType(resultElementList[6]); + Assert.IsType(resultElementList[7]); + Assert.IsType(resultElementList[8]); + Assert.IsType(resultElementList[9]); + Assert.IsType(resultElementList[10]); + } + + [Fact] + public void LoadFromV0103GreenshotTemplateWithDifferentContainer() + { + // Arrange + string filePath = Path.Combine("TestData", "GreenshotTemplate", "File_Version_1.03", "Surface_with_14_different_DrawableContainer.gst"); + var image = new Bitmap(800, 400); + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + + Assert.NotNull(resultElementList); + Assert.Equal(14, resultElementList.Count); + + Assert.IsType(resultElementList[0]); + Assert.IsType(resultElementList[1]); + Assert.IsType(resultElementList[2]); + Assert.IsType(resultElementList[3]); + Assert.IsType(resultElementList[4]); + Assert.IsType(resultElementList[5]); + Assert.IsType(resultElementList[6]); + Assert.IsType(resultElementList[7]); + Assert.IsType(resultElementList[8]); + Assert.IsType(resultElementList[9]); + Assert.IsType(resultElementList[10]); + Assert.IsType(resultElementList[11]); + Assert.IsType(resultElementList[12]); + Assert.IsType(resultElementList[13]); + } + + [Fact] + public void LoadFromV0201GreenshotTemplateWithDifferentContainer() + { + // Arrange + string filePath = Path.Combine("TestData", "GreenshotTemplate", "File_Version_2.01", "Surface_with_14_different_DrawableContainer.gst"); + var image = new Bitmap(800, 400); + var resultSurface = SimpleServiceProvider.Current.GetInstance>().Invoke(); + resultSurface.Image = image; + + // Act + _greenshotTemplateFormatHandler.LoadTemplateFromFile(filePath, resultSurface); + + // Assert + var resultElementList = resultSurface.Elements; + + Assert.NotNull(resultElementList); + Assert.Equal(14, resultElementList.Count); + + Assert.IsType(resultElementList[0]); + Assert.IsType(resultElementList[1]); + Assert.IsType(resultElementList[2]); + Assert.IsType(resultElementList[3]); + Assert.IsType(resultElementList[4]); + Assert.IsType(resultElementList[5]); + Assert.IsType(resultElementList[6]); + Assert.IsType(resultElementList[7]); + Assert.IsType(resultElementList[8]); + Assert.IsType(resultElementList[9]); + Assert.IsType(resultElementList[10]); + Assert.IsType(resultElementList[11]); + Assert.IsType(resultElementList[12]); + Assert.IsType(resultElementList[13]); + } +} \ No newline at end of file diff --git a/src/Greenshot.Test/Fixtures/Base/SimpleServiceProviderFixture.cs b/src/Greenshot.Test/Fixtures/Base/SimpleServiceProviderFixture.cs new file mode 100644 index 000000000..731c24624 --- /dev/null +++ b/src/Greenshot.Test/Fixtures/Base/SimpleServiceProviderFixture.cs @@ -0,0 +1,87 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Editor.Drawing; +using Greenshot.Test.Fixtures.Collections; + +namespace Greenshot.Test.Fixtures.Base; + +/// +/// Provides a test fixture that manages a shared +/// instance with pre-configured services for integration testing. +/// +/// +/// +/// This fixture follows the xUnit collection fixture pattern, ensuring that: +/// +/// Setup occurs once before any tests in the collection run +/// Cleanup occurs once after all tests in the collection complete +/// The same fixture instance is shared across all tests in the collection +/// +/// +/// +/// Usage: Apply i.e. the [Collection("DefaultCollection")] attribute +/// to your test class to use this fixture. The fixture will be automatically injected +/// as a constructor parameter. +/// +/// +/// Current Services: +/// +/// factory - Provides instances +/// +/// +/// +/// +/// +/// [Collection("DefaultCollection")] +/// public class MyIntegrationTests +/// { +/// private readonly SimpleServiceProviderFixture _fixture; +/// +/// public MyIntegrationTests(SimpleServiceProviderFixture fixture) +/// { +/// _fixture = fixture; +/// } +/// } +/// +/// +public class SimpleServiceProviderFixture : IDisposable +{ + /// + /// Initializes the fixture and configures the shared service provider. + /// This constructor runs once before the first test in the collection executes. + /// + public SimpleServiceProviderFixture() + { + SimpleServiceProvider.Current.AddService>(() => new Surface()); + } + + /// + /// Cleans up the service provider configuration. + /// This method runs once after all tests in the collection have completed. + /// + public void Dispose() + { + SimpleServiceProvider.Current.RemoveService>(); + } +} diff --git a/src/Greenshot.Test/Fixtures/Collections/SimplServiceProviderCollection.cs b/src/Greenshot.Test/Fixtures/Collections/SimplServiceProviderCollection.cs new file mode 100644 index 000000000..88a608e96 --- /dev/null +++ b/src/Greenshot.Test/Fixtures/Collections/SimplServiceProviderCollection.cs @@ -0,0 +1,49 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2025 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * 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 1 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 Greenshot.Test.Fixtures.Base; +using Xunit; + +namespace Greenshot.Test.Fixtures.Collections; + +/// +/// Defines a test collection that groups related integration tests together. +/// This class serves as a marker for xUnit's collection definition and has no implementation. +/// +/// +/// +/// This collection ensures that all tests within it share the same fixture instance +/// and run sequentially rather than in parallel, which is essential for integration +/// tests that may have shared dependencies or state. +/// +/// +/// Currently includes: +/// +/// - Provides shared service container setup +/// +/// +/// +/// To use this collection, apply the [Collection("DefaultCollection")] attribute +/// to your test classes. Additional fixtures can be added by extending this class +/// or creating new collection definitions as needed. +/// +/// +[CollectionDefinition("DefaultCollection")] +public class DefaultCollection : ICollectionFixture { } diff --git a/src/Greenshot.Test/Greenshot.Test.csproj b/src/Greenshot.Test/Greenshot.Test.csproj new file mode 100644 index 000000000..c54ff1110 --- /dev/null +++ b/src/Greenshot.Test/Greenshot.Test.csproj @@ -0,0 +1,316 @@ + + + + net472 + + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + + diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/ArrowContainer_lt_100_200_wh_400_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/ArrowContainer_lt_100_200_wh_400_100.gst new file mode 100644 index 000000000..e29b565d0 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/ArrowContainer_lt_100_200_wh_400_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/EllipseContainer_lt_200_200_wh_400_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/EllipseContainer_lt_200_200_wh_400_100.gst new file mode 100644 index 000000000..64f213472 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/EllipseContainer_lt_200_200_wh_400_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/FreehandContainer_with_4_points.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/FreehandContainer_with_4_points.gst new file mode 100644 index 000000000..909c5e49a Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/FreehandContainer_with_4_points.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/HighlightContainer_TextFilter_lt_310_70_wh_195_60.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/HighlightContainer_TextFilter_lt_310_70_wh_195_60.gst new file mode 100644 index 000000000..38821e5a0 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/HighlightContainer_TextFilter_lt_310_70_wh_195_60.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/IconContainer_lt_400_200_wh_32_32.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/IconContainer_lt_400_200_wh_32_32.gst new file mode 100644 index 000000000..7043e9ee5 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/IconContainer_lt_400_200_wh_32_32.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/ImageContainer_lt_300_200_wh_100_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/ImageContainer_lt_300_200_wh_100_100.gst new file mode 100644 index 000000000..93a535d4c Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/ImageContainer_lt_300_200_wh_100_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/LineContainer_lt_200_200_w_400.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/LineContainer_lt_200_200_w_400.gst new file mode 100644 index 000000000..3e31c972c Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/LineContainer_lt_200_200_w_400.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.gst new file mode 100644 index 000000000..12699259d Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/RectangleContainer_lt_100_200_wh_150_80.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/RectangleContainer_lt_100_200_wh_150_80.gst new file mode 100644 index 000000000..7ed6cbe08 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/RectangleContainer_lt_100_200_wh_150_80.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/SpeechbubbleContainer_lt_200_200_wh_150_80.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/SpeechbubbleContainer_lt_200_200_wh_150_80.gst new file mode 100644 index 000000000..79caa693e Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/SpeechbubbleContainer_lt_200_200_wh_150_80.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/StepLabelContainer_lt_200_200_lt_500_300.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/StepLabelContainer_lt_200_200_lt_500_300.gst new file mode 100644 index 000000000..15fa7f9dc Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/StepLabelContainer_lt_200_200_lt_500_300.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/Surface_with_11_different_DrawableContainer.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/Surface_with_11_different_DrawableContainer.gst new file mode 100644 index 000000000..c98f859a9 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/Surface_with_11_different_DrawableContainer.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/TextContainer_lt_300_200_wh_300_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/TextContainer_lt_300_200_wh_300_100.gst new file mode 100644 index 000000000..07be9bef4 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.02/TextContainer_lt_300_200_wh_300_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/ArrowContainer_lt_100_200_wh_400_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/ArrowContainer_lt_100_200_wh_400_100.gst new file mode 100644 index 000000000..22a4d4d1c Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/ArrowContainer_lt_100_200_wh_400_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/EllipseContainer_lt_200_200_wh_400_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/EllipseContainer_lt_200_200_wh_400_100.gst new file mode 100644 index 000000000..8c62ff92a Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/EllipseContainer_lt_200_200_wh_400_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/FreehandContainer_with_4_points.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/FreehandContainer_with_4_points.gst new file mode 100644 index 000000000..bfd5a7320 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/FreehandContainer_with_4_points.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/HighlightContainer_TextFilter_lt_310_70_wh_195_60.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/HighlightContainer_TextFilter_lt_310_70_wh_195_60.gst new file mode 100644 index 000000000..709c75bc4 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/HighlightContainer_TextFilter_lt_310_70_wh_195_60.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/IconContainer_lt_400_200_wh_32_32.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/IconContainer_lt_400_200_wh_32_32.gst new file mode 100644 index 000000000..34ef5c880 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/IconContainer_lt_400_200_wh_32_32.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/ImageContainer_lt_300_200_wh_100_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/ImageContainer_lt_300_200_wh_100_100.gst new file mode 100644 index 000000000..ce8ca26dd Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/ImageContainer_lt_300_200_wh_100_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/LineContainer_lt_200_200_w_400.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/LineContainer_lt_200_200_w_400.gst new file mode 100644 index 000000000..1a38a23ae Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/LineContainer_lt_200_200_w_400.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/MetafileContainer_lt_300_200_wh_120_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/MetafileContainer_lt_300_200_wh_120_100.gst new file mode 100644 index 000000000..e7b83d6b5 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/MetafileContainer_lt_300_200_wh_120_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.gst new file mode 100644 index 000000000..38139e008 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/RectangleContainer_lt_100_200_wh_150_80.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/RectangleContainer_lt_100_200_wh_150_80.gst new file mode 100644 index 000000000..6a736967d Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/RectangleContainer_lt_100_200_wh_150_80.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/SpeechbubbleContainer_lt_200_200_wh_150_80.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/SpeechbubbleContainer_lt_200_200_wh_150_80.gst new file mode 100644 index 000000000..2701209f8 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/SpeechbubbleContainer_lt_200_200_wh_150_80.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/StepLabelContainer_lt_200_200_lt_500_300.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/StepLabelContainer_lt_200_200_lt_500_300.gst new file mode 100644 index 000000000..ff5f3f7f4 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/StepLabelContainer_lt_200_200_lt_500_300.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/Surface_with_14_different_DrawableContainer.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/Surface_with_14_different_DrawableContainer.gst new file mode 100644 index 000000000..334ec5e26 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/Surface_with_14_different_DrawableContainer.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/SvgContainer_lt_300_200_wh_120_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/SvgContainer_lt_300_200_wh_120_100.gst new file mode 100644 index 000000000..1a16366a8 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/SvgContainer_lt_300_200_wh_120_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/TextContainer_lt_300_200_wh_300_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/TextContainer_lt_300_200_wh_300_100.gst new file mode 100644 index 000000000..7f24b11f4 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_1.03/TextContainer_lt_300_200_wh_300_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/ArrowContainer_lt_100_200_wh_400_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/ArrowContainer_lt_100_200_wh_400_100.gst new file mode 100644 index 000000000..f64291d51 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/ArrowContainer_lt_100_200_wh_400_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/EllipseContainer_lt_200_200_wh_400_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/EllipseContainer_lt_200_200_wh_400_100.gst new file mode 100644 index 000000000..8da78f352 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/EllipseContainer_lt_200_200_wh_400_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/FreehandContainer_with_4_points.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/FreehandContainer_with_4_points.gst new file mode 100644 index 000000000..e229771d8 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/FreehandContainer_with_4_points.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/HighlightContainer_TextFilter_lt_310_70_wh_195_60.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/HighlightContainer_TextFilter_lt_310_70_wh_195_60.gst new file mode 100644 index 000000000..712e7b4d1 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/HighlightContainer_TextFilter_lt_310_70_wh_195_60.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/IconContainer_lt_400_200_wh_32_32.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/IconContainer_lt_400_200_wh_32_32.gst new file mode 100644 index 000000000..14af36389 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/IconContainer_lt_400_200_wh_32_32.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/ImageContainer_lt_300_200_wh_100_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/ImageContainer_lt_300_200_wh_100_100.gst new file mode 100644 index 000000000..0a079df16 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/ImageContainer_lt_300_200_wh_100_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/LineContainer_lt_200_200_w_400.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/LineContainer_lt_200_200_w_400.gst new file mode 100644 index 000000000..b71ff91f2 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/LineContainer_lt_200_200_w_400.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/MetafileContainer_lt_300_200_wh_120_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/MetafileContainer_lt_300_200_wh_120_100.gst new file mode 100644 index 000000000..86901b8dd Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/MetafileContainer_lt_300_200_wh_120_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.gst new file mode 100644 index 000000000..dfe55594a Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/RectangleContainer_lt_100_200_wh_150_80.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/RectangleContainer_lt_100_200_wh_150_80.gst new file mode 100644 index 000000000..f83f0b4c3 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/RectangleContainer_lt_100_200_wh_150_80.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/SpeechbubbleContainer_lt_200_200_wh_150_80.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/SpeechbubbleContainer_lt_200_200_wh_150_80.gst new file mode 100644 index 000000000..d0db7f535 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/SpeechbubbleContainer_lt_200_200_wh_150_80.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/StepLabelContainer_lt_200_200_lt_500_300.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/StepLabelContainer_lt_200_200_lt_500_300.gst new file mode 100644 index 000000000..28724ce4e Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/StepLabelContainer_lt_200_200_lt_500_300.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/Surface_with_14_different_DrawableContainer.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/Surface_with_14_different_DrawableContainer.gst new file mode 100644 index 000000000..a1a695950 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/Surface_with_14_different_DrawableContainer.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/SvgContainer_lt_300_200_wh_120_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/SvgContainer_lt_300_200_wh_120_100.gst new file mode 100644 index 000000000..39bc1785b Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/SvgContainer_lt_300_200_wh_120_100.gst differ diff --git a/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/TextContainer_lt_300_200_wh_300_100.gst b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/TextContainer_lt_300_200_wh_300_100.gst new file mode 100644 index 000000000..37f4e50d0 Binary files /dev/null and b/src/Greenshot.Test/TestData/GreenshotTemplate/File_Version_2.01/TextContainer_lt_300_200_wh_300_100.gst differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/ArrowContainer_lt_100_200_wh_400_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/ArrowContainer_lt_100_200_wh_400_100.greenshot new file mode 100644 index 000000000..bea8fe010 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/ArrowContainer_lt_100_200_wh_400_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/EllipseContainer_lt_200_200_wh_400_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/EllipseContainer_lt_200_200_wh_400_100.greenshot new file mode 100644 index 000000000..97770ee16 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/EllipseContainer_lt_200_200_wh_400_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/FreehandContainer_with_4_points.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/FreehandContainer_with_4_points.greenshot new file mode 100644 index 000000000..e1cad2c77 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/FreehandContainer_with_4_points.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot new file mode 100644 index 000000000..0ace18aeb Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/IconContainer_lt_400_200_wh_32_32.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/IconContainer_lt_400_200_wh_32_32.greenshot new file mode 100644 index 000000000..122270307 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/IconContainer_lt_400_200_wh_32_32.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/ImageContainer_lt_300_200_wh_100_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/ImageContainer_lt_300_200_wh_100_100.greenshot new file mode 100644 index 000000000..b2e0b7abb Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/ImageContainer_lt_300_200_wh_100_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/LineContainer_lt_200_200_w_400.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/LineContainer_lt_200_200_w_400.greenshot new file mode 100644 index 000000000..a53c076b8 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/LineContainer_lt_200_200_w_400.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot new file mode 100644 index 000000000..eac540fc3 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/RectangleContainer_lt_100_200_wh_150_80.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/RectangleContainer_lt_100_200_wh_150_80.greenshot new file mode 100644 index 000000000..d2df21961 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/RectangleContainer_lt_100_200_wh_150_80.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot new file mode 100644 index 000000000..65b3ade7b Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/StepLabelContainer_lt_200_200_lt_500_300.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/StepLabelContainer_lt_200_200_lt_500_300.greenshot new file mode 100644 index 000000000..b2d3e5e4e Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/StepLabelContainer_lt_200_200_lt_500_300.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/Surface_with_11_different_DrawableContainer.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/Surface_with_11_different_DrawableContainer.greenshot new file mode 100644 index 000000000..e42413f55 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/Surface_with_11_different_DrawableContainer.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/Surface_with_Image_800x400.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/Surface_with_Image_800x400.greenshot new file mode 100644 index 000000000..29b0171da Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/Surface_with_Image_800x400.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/TextContainer_lt_300_200_wh_300_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/TextContainer_lt_300_200_wh_300_100.greenshot new file mode 100644 index 000000000..156842393 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.02/TextContainer_lt_300_200_wh_300_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/ArrowContainer_lt_100_200_wh_400_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/ArrowContainer_lt_100_200_wh_400_100.greenshot new file mode 100644 index 000000000..57d0f2013 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/ArrowContainer_lt_100_200_wh_400_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/EllipseContainer_lt_200_200_wh_400_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/EllipseContainer_lt_200_200_wh_400_100.greenshot new file mode 100644 index 000000000..891c81c11 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/EllipseContainer_lt_200_200_wh_400_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/FreehandContainer_with_4_points.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/FreehandContainer_with_4_points.greenshot new file mode 100644 index 000000000..1ab987eca Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/FreehandContainer_with_4_points.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot new file mode 100644 index 000000000..fcf6bf6d0 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/IconContainer_lt_400_200_wh_32_32.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/IconContainer_lt_400_200_wh_32_32.greenshot new file mode 100644 index 000000000..7a8077021 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/IconContainer_lt_400_200_wh_32_32.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/ImageContainer_lt_300_200_wh_100_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/ImageContainer_lt_300_200_wh_100_100.greenshot new file mode 100644 index 000000000..79d73b012 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/ImageContainer_lt_300_200_wh_100_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/LineContainer_lt_200_200_w_400.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/LineContainer_lt_200_200_w_400.greenshot new file mode 100644 index 000000000..d6d2a34e9 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/LineContainer_lt_200_200_w_400.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/MetafileContainer_lt_300_200_wh_120_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/MetafileContainer_lt_300_200_wh_120_100.greenshot new file mode 100644 index 000000000..81698b07b Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/MetafileContainer_lt_300_200_wh_120_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot new file mode 100644 index 000000000..cb84932a0 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/RectangleContainer_lt_100_200_wh_150_80.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/RectangleContainer_lt_100_200_wh_150_80.greenshot new file mode 100644 index 000000000..221305d71 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/RectangleContainer_lt_100_200_wh_150_80.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot new file mode 100644 index 000000000..f81c966a3 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/StepLabelContainer_lt_200_200_lt_500_300.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/StepLabelContainer_lt_200_200_lt_500_300.greenshot new file mode 100644 index 000000000..9150681a4 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/StepLabelContainer_lt_200_200_lt_500_300.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/Surface_with_14_different_DrawableContainer.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/Surface_with_14_different_DrawableContainer.greenshot new file mode 100644 index 000000000..ba84c39a6 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/Surface_with_14_different_DrawableContainer.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/Surface_with_Image_800x400.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/Surface_with_Image_800x400.greenshot new file mode 100644 index 000000000..1508060e7 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/Surface_with_Image_800x400.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/SvgContainer_lt_300_200_wh_120_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/SvgContainer_lt_300_200_wh_120_100.greenshot new file mode 100644 index 000000000..f13a33492 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/SvgContainer_lt_300_200_wh_120_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/TextContainer_lt_300_200_wh_300_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/TextContainer_lt_300_200_wh_300_100.greenshot new file mode 100644 index 000000000..e8fe6a9a9 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_1.03/TextContainer_lt_300_200_wh_300_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/ArrowContainer_lt_100_200_wh_400_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/ArrowContainer_lt_100_200_wh_400_100.greenshot new file mode 100644 index 000000000..216b94584 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/ArrowContainer_lt_100_200_wh_400_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/EllipseContainer_lt_200_200_wh_400_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/EllipseContainer_lt_200_200_wh_400_100.greenshot new file mode 100644 index 000000000..5e755c23d Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/EllipseContainer_lt_200_200_wh_400_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/FreehandContainer_with_4_points.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/FreehandContainer_with_4_points.greenshot new file mode 100644 index 000000000..67d943467 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/FreehandContainer_with_4_points.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot new file mode 100644 index 000000000..e142bd608 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/HighlightContainer_TextFilter_lt_310_70_wh_195_60.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/IconContainer_lt_400_200_wh_32_32.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/IconContainer_lt_400_200_wh_32_32.greenshot new file mode 100644 index 000000000..05420a483 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/IconContainer_lt_400_200_wh_32_32.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/ImageContainer_lt_300_200_wh_100_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/ImageContainer_lt_300_200_wh_100_100.greenshot new file mode 100644 index 000000000..e9ad02c27 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/ImageContainer_lt_300_200_wh_100_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/LineContainer_lt_200_200_w_400.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/LineContainer_lt_200_200_w_400.greenshot new file mode 100644 index 000000000..ca3b761d5 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/LineContainer_lt_200_200_w_400.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/MetafileContainer_lt_300_200_wh_120_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/MetafileContainer_lt_300_200_wh_120_100.greenshot new file mode 100644 index 000000000..8ba010b5f Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/MetafileContainer_lt_300_200_wh_120_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot new file mode 100644 index 000000000..742246a38 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/ObfuscateContainer_BlurFilter_lt_130_70_wh_180_70.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/RectangleContainer_lt_100_200_wh_150_80.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/RectangleContainer_lt_100_200_wh_150_80.greenshot new file mode 100644 index 000000000..57f630381 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/RectangleContainer_lt_100_200_wh_150_80.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot new file mode 100644 index 000000000..6b2143892 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/SpeechbubbleContainer_lt_200_200_wh_150_80.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/StepLabelContainer_lt_200_200_lt_500_300.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/StepLabelContainer_lt_200_200_lt_500_300.greenshot new file mode 100644 index 000000000..8d6e48d09 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/StepLabelContainer_lt_200_200_lt_500_300.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/Surface_with_14_different_DrawableContainer.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/Surface_with_14_different_DrawableContainer.greenshot new file mode 100644 index 000000000..f8c945765 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/Surface_with_14_different_DrawableContainer.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/Surface_with_Image_800x400.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/Surface_with_Image_800x400.greenshot new file mode 100644 index 000000000..62c111341 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/Surface_with_Image_800x400.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/SvgContainer_lt_300_200_wh_120_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/SvgContainer_lt_300_200_wh_120_100.greenshot new file mode 100644 index 000000000..35ac907e5 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/SvgContainer_lt_300_200_wh_120_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/TextContainer_lt_300_200_wh_300_100.greenshot b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/TextContainer_lt_300_200_wh_300_100.greenshot new file mode 100644 index 000000000..65f714071 Binary files /dev/null and b/src/Greenshot.Test/TestData/Greenshotfile/File_Version_2.01/TextContainer_lt_300_200_wh_300_100.greenshot differ diff --git a/src/Greenshot.Test/TestData/Images/Greenshot.ico b/src/Greenshot.Test/TestData/Images/Greenshot.ico new file mode 100644 index 000000000..fd1312dec Binary files /dev/null and b/src/Greenshot.Test/TestData/Images/Greenshot.ico differ diff --git a/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.emf b/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.emf new file mode 100644 index 000000000..39612f621 Binary files /dev/null and b/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.emf differ diff --git a/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.jpg b/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.jpg new file mode 100644 index 000000000..e250504b2 Binary files /dev/null and b/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.jpg differ diff --git a/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.png b/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.png new file mode 100644 index 000000000..f3b53882c Binary files /dev/null and b/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.png differ diff --git a/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.svg b/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.svg new file mode 100644 index 000000000..ae11b9b6b --- /dev/null +++ b/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.svg @@ -0,0 +1,229 @@ + + + Testfile with greenshot logo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Testfile with greenshot logo + https://getgreenshot.org + + + + diff --git a/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.wmf b/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.wmf new file mode 100644 index 000000000..e5693946d Binary files /dev/null and b/src/Greenshot.Test/TestData/Images/Logo_G_with_Border.wmf differ diff --git a/src/Greenshot.Test/TestData/Images/Logo_G_with_Border_256x256.png b/src/Greenshot.Test/TestData/Images/Logo_G_with_Border_256x256.png new file mode 100644 index 000000000..e9edbddc7 Binary files /dev/null and b/src/Greenshot.Test/TestData/Images/Logo_G_with_Border_256x256.png differ diff --git a/src/Greenshot.Test/TestData/Images/Screenshot_background_800x400.png b/src/Greenshot.Test/TestData/Images/Screenshot_background_800x400.png new file mode 100644 index 000000000..5b8b27e60 Binary files /dev/null and b/src/Greenshot.Test/TestData/Images/Screenshot_background_800x400.png differ diff --git a/src/Greenshot.Test/TestData/Images/Screenshot_background_800x400_template.svg b/src/Greenshot.Test/TestData/Images/Screenshot_background_800x400_template.svg new file mode 100644 index 000000000..6e8289758 --- /dev/null +++ b/src/Greenshot.Test/TestData/Images/Screenshot_background_800x400_template.svg @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + Hello Greenshot! + Testfile 800x400 px + + + 200 + 200 + 400 + 600 + 100 + 300 + 100 + 700 + 300 + 500 + + diff --git a/src/Greenshot.sln b/src/Greenshot.sln index b71e79bd3..c0da2f4c4 100644 --- a/src/Greenshot.sln +++ b/src/Greenshot.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.7.34009.444 +VisualStudioVersion = 17.13.35919.96 d17.13 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot", "Greenshot\Greenshot.csproj", "{CD642BF4-D815-4D67-A0B5-C69F0B8231AF}" ProjectSection(ProjectDependencies) = postProject @@ -53,6 +53,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot.Editor", "Greenshot.Editor\Greenshot.Editor.csproj", "{148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Greenshot.Test", "Greenshot.Test\Greenshot.Test.csproj", "{CD82EAD5-57B8-2FD8-0B6F-B912185F70D1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -173,6 +175,14 @@ Global {148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}.Release|Any CPU.Build.0 = Release|Any CPU {148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}.Release|x86.ActiveCfg = Release|Any CPU {148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}.Release|x86.Build.0 = Release|Any CPU + {CD82EAD5-57B8-2FD8-0B6F-B912185F70D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD82EAD5-57B8-2FD8-0B6F-B912185F70D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD82EAD5-57B8-2FD8-0B6F-B912185F70D1}.Debug|x86.ActiveCfg = Debug|Any CPU + {CD82EAD5-57B8-2FD8-0B6F-B912185F70D1}.Debug|x86.Build.0 = Debug|Any CPU + {CD82EAD5-57B8-2FD8-0B6F-B912185F70D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD82EAD5-57B8-2FD8-0B6F-B912185F70D1}.Release|Any CPU.Build.0 = Release|Any CPU + {CD82EAD5-57B8-2FD8-0B6F-B912185F70D1}.Release|x86.ActiveCfg = Release|Any CPU + {CD82EAD5-57B8-2FD8-0B6F-B912185F70D1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Greenshot/Helpers/CaptureHelper.cs b/src/Greenshot/Helpers/CaptureHelper.cs index e51b51ebe..867736258 100644 --- a/src/Greenshot/Helpers/CaptureHelper.cs +++ b/src/Greenshot/Helpers/CaptureHelper.cs @@ -25,6 +25,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; +using System.Linq; using System.Text; using System.Threading; using System.Windows.Forms; @@ -41,6 +42,7 @@ using Greenshot.Base.Interfaces; using Greenshot.Configuration; using Greenshot.Editor.Destinations; using Greenshot.Editor.Drawing; +using Greenshot.Editor.FileFormatHandlers; using Greenshot.Forms; namespace Greenshot.Helpers @@ -432,37 +434,45 @@ namespace Greenshot.Helpers Image fileImage = null; string filename = _capture.CaptureDetails.Filename; - if (!string.IsNullOrEmpty(filename)) + if (string.IsNullOrEmpty(filename)) { - // TODO: Fix that the Greenshot format needs a separate code path - try - { - if (filename.ToLower().EndsWith("." + OutputFormat.greenshot)) - { - ISurface surface = new Surface(); - surface = ImageIO.LoadGreenshotSurface(filename, surface); - surface.CaptureDetails = _capture.CaptureDetails; - DestinationHelper.GetDestination(EditorDestination.DESIGNATION).ExportCapture(true, surface, _capture.CaptureDetails); - break; - } - } - catch (Exception e) - { - Log.Error(e.Message, e); - MessageBox.Show(Language.GetFormattedString(LangKey.error_openfile, filename)); - } + break; + } - // TODO: Remove Image loading for here - try + // TODO: Fix that the Greenshot format needs a separate code path + try + { + if (filename.ToLower().EndsWith("." + OutputFormat.greenshot)) { - fileImage = ImageIO.LoadImage(filename); - } - catch (Exception e) - { - Log.Error(e.Message, e); - MessageBox.Show(Language.GetFormattedString(LangKey.error_openfile, filename)); + var greenshotFileFormatHandler = SimpleServiceProvider.Current.GetAllInstances().OfType().FirstOrDefault(); + if (greenshotFileFormatHandler is null) + { + throw new Exception($"No instance of {nameof(GreenshotFileFormatHandler)} found in service provider."); + } + + ISurface surface = greenshotFileFormatHandler.LoadGreenshotSurface(filename); + surface.CaptureDetails = _capture.CaptureDetails; + DestinationHelper.GetDestination(EditorDestination.DESIGNATION).ExportCapture(true, surface, _capture.CaptureDetails); + break; } } + catch (Exception e) + { + Log.Error(e.Message, e); + MessageBox.Show(Language.GetFormattedString(LangKey.error_openfile, filename)); + } + + // TODO: Remove Image loading for here + try + { + fileImage = ImageIO.LoadImage(filename); + } + catch (Exception e) + { + Log.Error(e.Message, e); + MessageBox.Show(Language.GetFormattedString(LangKey.error_openfile, filename)); + } + if (fileImage != null) {