mirror of
https://github.com/greenshot/greenshot
synced 2025-08-20 21:43:24 -07:00
Merge c8453b189d
into c1ad1d4a93
This commit is contained in:
commit
6f69263dc0
261 changed files with 14631 additions and 764 deletions
|
@ -610,6 +610,7 @@ EndSelection:<<<<<<<4
|
|||
}
|
||||
var fileFormatHandlers = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>();
|
||||
var supportedExtensions = fileFormatHandlers.ExtensionsFor(FileFormatHandlerActions.LoadDrawableFromStream).ToList();
|
||||
var foundContainer = false;
|
||||
|
||||
foreach (var (stream, filename) in IterateClipboardContent(dataObject))
|
||||
{
|
||||
|
@ -622,7 +623,8 @@ EndSelection:<<<<<<<4
|
|||
IEnumerable<IDrawableContainer> 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))
|
||||
{
|
||||
|
|
|
@ -264,35 +264,6 @@ namespace Greenshot.Base.Core
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load a Greenshot surface
|
||||
/// </summary>
|
||||
/// <param name="fullPath"></param>
|
||||
/// <param name="returnSurface"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves image to specific path with specified quality
|
||||
/// </summary>
|
||||
|
@ -602,54 +573,5 @@ namespace Greenshot.Base.Core
|
|||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load a Greenshot surface from a stream
|
||||
/// </summary>
|
||||
/// <param name="surfaceFileStream">Stream</param>
|
||||
/// <param name="returnSurface"></param>
|
||||
/// <returns>ISurface</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,8 +12,12 @@ namespace Greenshot.Base.Core
|
|||
{
|
||||
private readonly Dictionary<Type, IList<object>> _services = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current instance of the service locator.
|
||||
/// </summary>
|
||||
public static IServiceLocator Current { get; } = new SimpleServiceProvider();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IReadOnlyList<TService> GetAllInstances<TService>()
|
||||
{
|
||||
var typeOfService = typeof(TService);
|
||||
|
@ -25,11 +29,13 @@ namespace Greenshot.Base.Core
|
|||
return results.Cast<TService>().ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TService GetInstance<TService>()
|
||||
{
|
||||
return GetAllInstances<TService>().SingleOrDefault();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AddService<TService>(IEnumerable<TService> services)
|
||||
{
|
||||
var serviceType = typeof(TService);
|
||||
|
@ -50,9 +56,40 @@ namespace Greenshot.Base.Core
|
|||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AddService<TService>(params TService[] services)
|
||||
{
|
||||
AddService(services.AsEnumerable());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveService<TService>(IEnumerable<TService> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveService<TService>(params TService[] services)
|
||||
{
|
||||
RemoveService(services.AsEnumerable());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,6 +41,11 @@ namespace Greenshot.Base.IniFile
|
|||
/// </summary>
|
||||
private static readonly object IniLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// A lock object for the section map
|
||||
/// </summary>
|
||||
private static readonly object SectionMapLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// As the ini implementation is kept someone generic, for reusing, this holds the name of the application
|
||||
/// </summary>
|
||||
|
@ -290,6 +295,8 @@ namespace Greenshot.Base.IniFile
|
|||
// Load the fixed settings
|
||||
_fixedProperties = Read(CreateIniLocation(_configName + FixedPostfix + IniExtension, true));
|
||||
|
||||
lock (SectionMapLock)
|
||||
{
|
||||
foreach (IniSection section in SectionMap.Values)
|
||||
{
|
||||
try
|
||||
|
@ -310,6 +317,7 @@ namespace Greenshot.Base.IniFile
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This "fixes" the properties of the section, meaning any properties in the fixed file can't be changed.
|
||||
|
@ -389,7 +397,12 @@ namespace Greenshot.Base.IniFile
|
|||
{
|
||||
get
|
||||
{
|
||||
foreach (string sectionName in SectionMap.Keys)
|
||||
List<string> sectionNames;
|
||||
lock (SectionMapLock)
|
||||
{
|
||||
sectionNames = [.. SectionMap.Keys];
|
||||
}
|
||||
foreach (string sectionName in sectionNames)
|
||||
{
|
||||
yield return sectionName;
|
||||
}
|
||||
|
@ -402,10 +415,13 @@ namespace Greenshot.Base.IniFile
|
|||
/// <param name="sectionName"></param>
|
||||
/// <returns></returns>
|
||||
public static IniSection GetIniSection(string sectionName)
|
||||
{
|
||||
lock (SectionMapLock)
|
||||
{
|
||||
SectionMap.TryGetValue(sectionName, out var returnValue);
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A generic method which returns an instance of the supplied type, filled with it's configuration
|
||||
|
@ -429,6 +445,9 @@ namespace Greenshot.Base.IniFile
|
|||
|
||||
Type iniSectionType = typeof(T);
|
||||
string sectionName = IniSection.GetIniSectionAttribute(iniSectionType).Name;
|
||||
|
||||
lock (SectionMapLock)
|
||||
{
|
||||
if (SectionMap.ContainsKey(sectionName))
|
||||
{
|
||||
//LOG.Debug("Returning pre-mapped section " + sectionName);
|
||||
|
@ -444,6 +463,7 @@ namespace Greenshot.Base.IniFile
|
|||
section.Fill(PropertiesForSection(section));
|
||||
FixProperties(section);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowSave && section.IsDirty)
|
||||
{
|
||||
|
|
|
@ -60,7 +60,7 @@ namespace Greenshot.Base.Interfaces
|
|||
/// Try to save the specified bitmap to the stream in the format belonging to the extension
|
||||
/// </summary>
|
||||
/// <param name="bitmap">Bitmap</param>
|
||||
/// <param name="destination">Stream</param>
|
||||
/// <param name="destination">Stream for destination</param>
|
||||
/// <param name="extension">extension</param>
|
||||
/// <param name="surface">ISurface with the elements for those file types which can store a surface (.greenshot)</param>
|
||||
/// <param name="surfaceOutputSettings">SurfaceOutputSettings</param>
|
||||
|
@ -68,21 +68,22 @@ namespace Greenshot.Base.Interfaces
|
|||
public bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Try to load an Image from the stream
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="extension"></param>
|
||||
/// <param name="bitmap"></param>
|
||||
/// <returns>bool true if it was successful</returns>
|
||||
/// <returns><see langword="true"/> if the image was successfully loaded into a <see cref="Bitmap"/>; otherwise, <see
|
||||
/// langword="false"/>.</returns>
|
||||
public bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap);
|
||||
|
||||
/// <summary>
|
||||
/// Try to load a drawable container from the stream
|
||||
/// Try to load drawable container from the stream
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream</param>
|
||||
/// <param name="extension">string</param>
|
||||
/// <param name="parentSurface">ISurface</param>
|
||||
/// <returns>IEnumerable{IDrawableContainer}</returns>
|
||||
/// <param name="parentSurface">Parent <see cref="ISurface"/> to initialize the container</param>
|
||||
/// <returns>All <see cref="IDrawableContainer"/> that could be loaded from the stream</returns>
|
||||
public IEnumerable<IDrawableContainer> LoadDrawablesFromStream(Stream stream, string extension, ISurface parentSurface = null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,5 +54,19 @@ namespace Greenshot.Base.Interfaces
|
|||
/// <typeparam name="TService">Type of the service</typeparam>
|
||||
/// <param name="services">IEnumerable{TService} with services to add</param>
|
||||
void AddService<TService>(IEnumerable<TService> services);
|
||||
|
||||
/// <summary>
|
||||
/// Remove one or more services from the registry
|
||||
/// </summary>
|
||||
/// <typeparam name="TService">Type of the service</typeparam>
|
||||
/// <param name="services">One or more services which need to be removed</param>
|
||||
void RemoveService<TService>(params TService[] services);
|
||||
|
||||
/// <summary>
|
||||
/// Remove multiple services from the registry
|
||||
/// </summary>
|
||||
/// <typeparam name="TService">Type of the service</typeparam>
|
||||
/// <param name="services">IEnumerable{TService} with services to remove</param>
|
||||
void RemoveService<TService>(IEnumerable<TService> services);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
/// <summary>
|
||||
/// Adds all container to the Surface.
|
||||
/// Ensure that all new container are correctly integrated into Surface
|
||||
/// </summary>
|
||||
/// <param name="containerList"></param>
|
||||
void LoadElements(IDrawableContainerList containerList);
|
||||
|
||||
/// <summary>
|
||||
/// Provides the selected elements
|
||||
|
|
|
@ -33,7 +33,6 @@ namespace Greenshot.Editor.Drawing
|
|||
/// <summary>
|
||||
/// Description of LineContainer.
|
||||
/// </summary>
|
||||
[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);
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -19,11 +19,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
|||
{
|
||||
/// <summary>
|
||||
/// Description of CursorContainer.
|
||||
/// This Container is not in use. For a capture with mouse cursor the IconContainer is used cctor Surface(ICapture capture) in <see cref="Surface"/>.
|
||||
/// </summary>
|
||||
[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
|
||||
|
|
|
@ -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.
|
||||
/// </summary>
|
||||
[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<IAdorner>();
|
||||
OnDeserialized(context);
|
||||
InitializeFields();
|
||||
_parent = parent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="streamingContext"></param>
|
||||
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
|
|||
/// <summary>
|
||||
/// List of available Adorners
|
||||
/// </summary>
|
||||
[NonSerialized] private IList<IAdorner> _adorners = new List<IAdorner>();
|
||||
private IList<IAdorner> _adorners = new List<IAdorner>();
|
||||
|
||||
public IList<IAdorner> 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);
|
||||
|
|
|
@ -42,7 +42,6 @@ namespace Greenshot.Editor.Drawing
|
|||
/// <summary>
|
||||
/// Dispatches most of a DrawableContainer's public properties and methods to a list of DrawableContainers.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class DrawableContainerList : List<IDrawableContainer>, IDrawableContainerList
|
||||
{
|
||||
private static readonly ComponentResourceManager EditorFormResources = new(typeof(ImageEditorForm));
|
||||
|
|
|
@ -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
|
|||
/// <summary>
|
||||
/// Description of EllipseContainer.
|
||||
/// </summary>
|
||||
[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();
|
||||
|
|
|
@ -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
|
|||
/// <summary>
|
||||
/// Basic IFieldHolder implementation, providing access to a set of fields
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class AbstractFieldHolder : IFieldHolder
|
||||
{
|
||||
private static readonly ILog LOG = LogManager.GetLogger(typeof(AbstractFieldHolder));
|
||||
private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection<EditorConfiguration>();
|
||||
[NonSerialized] private readonly IDictionary<IField, PropertyChangedEventHandler> _handlers = new Dictionary<IField, PropertyChangedEventHandler>();
|
||||
private readonly IDictionary<IField, PropertyChangedEventHandler> _handlers = new Dictionary<IField, PropertyChangedEventHandler>();
|
||||
|
||||
/// <summary>
|
||||
/// called when a field's value has changed
|
||||
/// </summary>
|
||||
[NonSerialized] private FieldChangedEventHandler _fieldChanged;
|
||||
private FieldChangedEventHandler _fieldChanged;
|
||||
|
||||
/// <inheritdoc cref="_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<IFieldType, IField> _fieldsByType = new Dictionary<IFieldType, IField>();
|
||||
private readonly IList<IField> fields = new List<IField>();
|
||||
|
||||
[OnDeserialized]
|
||||
private void OnDeserialized(StreamingContext context)
|
||||
{
|
||||
_fieldsByType = new Dictionary<IFieldType, IField>();
|
||||
// listen to changing properties
|
||||
foreach (var field in fields)
|
||||
{
|
||||
field.PropertyChanged += delegate { _fieldChanged?.Invoke(this, new FieldChangedEventArgs(field)); };
|
||||
_fieldsByType[field.FieldType] = field;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The dictionary represents the base collection of fields. It allows quick access by <see cref="IFieldType"/>.
|
||||
/// </summary>
|
||||
private readonly IDictionary<IFieldType, IField> _fieldsByType = new Dictionary<IFieldType, IField>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of all fields currently stored in the internal dictionary.
|
||||
/// </summary>
|
||||
public IList<IField> GetFields()
|
||||
{
|
||||
return fields;
|
||||
return _fieldsByType.Select(x => x.Value).ToList();
|
||||
}
|
||||
|
||||
|
||||
public IField GetField(IFieldType fieldType)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -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.
|
||||
/// </summary>
|
||||
[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<IFieldHolder> Children = new List<IFieldHolder>();
|
||||
|
||||
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);
|
||||
|
|
|
@ -29,10 +29,9 @@ namespace Greenshot.Editor.Drawing.Fields
|
|||
/// Represents a single field of a drawable element, i.e.
|
||||
/// line thickness of a rectangle.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class Field : IField
|
||||
{
|
||||
[field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged;
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private object _myValue;
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class FieldAggregator : AbstractFieldHolder, IFieldAggregator
|
||||
{
|
||||
private readonly IDrawableContainerList _boundContainers;
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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)
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class FieldType : IFieldType
|
||||
{
|
||||
public static readonly IFieldType ARROWHEADS = new FieldType(nameof(ARROWHEADS));
|
||||
|
|
|
@ -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
|
|||
/// <summary>
|
||||
/// empty container for filter-only elements
|
||||
/// </summary>
|
||||
[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()
|
||||
|
|
|
@ -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.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class AbstractFilter : AbstractFieldHolder, IFilter
|
||||
{
|
||||
[NonSerialized] private PropertyChangedEventHandler propertyChanged;
|
||||
private PropertyChangedEventHandler propertyChanged;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged
|
||||
{
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
|
|
@ -32,7 +32,6 @@ namespace Greenshot.Editor.Drawing.Filters
|
|||
/// <summary>
|
||||
/// Magnify an area
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class MagnifierFilter : AbstractFilter
|
||||
{
|
||||
public MagnifierFilter(DrawableContainer parent) : base(parent)
|
||||
|
|
|
@ -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
|
|||
/// <summary>
|
||||
/// Description of PathContainer.
|
||||
/// </summary>
|
||||
[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;
|
||||
|
@ -55,13 +52,26 @@ namespace Greenshot.Editor.Drawing
|
|||
/// Constructor
|
||||
/// </summary>
|
||||
public FreehandContainer(ISurface parent) : base(parent)
|
||||
{
|
||||
if (parent?.Image is not null)
|
||||
{
|
||||
Width = parent.Image.Width;
|
||||
Height = parent.Image.Height;
|
||||
}
|
||||
Top = 0;
|
||||
Left = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <remarks>And recalculates path from capturePoints</remarks>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This Dispose is called from the Dispose and the Destructor.
|
||||
/// </summary>
|
||||
|
@ -98,6 +103,12 @@ namespace Greenshot.Editor.Drawing
|
|||
freehandPath = null;
|
||||
}
|
||||
|
||||
public List<Point> CapturePoints
|
||||
{
|
||||
get => capturePoints;
|
||||
set => capturePoints = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from Surface (the parent) when the drawing begins (mouse-down)
|
||||
/// </summary>
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
|||
/// <summary>
|
||||
/// Description of ObfuscateContainer.
|
||||
/// </summary>
|
||||
[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;
|
||||
|
|
|
@ -19,11 +19,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
|||
/// <summary>
|
||||
/// Description of IconContainer.
|
||||
/// </summary>
|
||||
[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();
|
||||
|
|
|
@ -19,11 +19,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
|||
/// <summary>
|
||||
/// Description of BitmapContainer.
|
||||
/// </summary>
|
||||
[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
|
||||
/// </summary>
|
||||
[NonSerialized] private Image _shadowBitmap;
|
||||
private Image _shadowBitmap;
|
||||
|
||||
/// <summary>
|
||||
/// This is the offset for the shadow version of the bitmap
|
||||
/// Do not serialize, as the offset is recreated
|
||||
/// </summary>
|
||||
[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();
|
||||
|
|
|
@ -19,10 +19,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
|||
/// <summary>
|
||||
/// Description of LineContainer.
|
||||
/// </summary>
|
||||
[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));
|
||||
|
|
|
@ -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,15 +33,42 @@ namespace Greenshot.Editor.Drawing
|
|||
/// <summary>
|
||||
/// This provides a resizable SVG container, redrawing the SVG in the size the container takes.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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()
|
||||
|
@ -74,6 +102,37 @@ namespace Greenshot.Editor.Drawing
|
|||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Metafile"/> from the specified <see cref="Image"/>.
|
||||
/// </summary>
|
||||
/// <param name="image">The source <see cref="Image"/> to be converted into a <see cref="Metafile"/>. Cannot be <see
|
||||
/// langword="null"/>.</param>
|
||||
/// <returns>A <see cref="Metafile"/> object that contains the graphical content of the specified <see cref="Image"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="image"/> is <see langword="null"/>.</exception>
|
||||
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);
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
|||
/// <summary>
|
||||
/// This is a FilterContainer for the obfuscator filters like blur and pixelate.
|
||||
/// </summary>
|
||||
[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;
|
||||
|
|
|
@ -35,7 +35,6 @@ namespace Greenshot.Editor.Drawing
|
|||
/// <summary>
|
||||
/// Represents a rectangular shape on the Surface
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class RectangleContainer : DrawableContainer
|
||||
{
|
||||
public RectangleContainer(ISurface parent) : base(parent)
|
||||
|
@ -43,16 +42,6 @@ namespace Greenshot.Editor.Drawing
|
|||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do some logic to make sure all field are initiated correctly
|
||||
/// </summary>
|
||||
/// <param name="streamingContext">StreamingContext</param>
|
||||
protected override void OnDeserialized(StreamingContext streamingContext)
|
||||
{
|
||||
base.OnDeserialized(streamingContext);
|
||||
Init();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
CreateDefaultAdorners();
|
||||
|
|
|
@ -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
|
|||
/// <summary>
|
||||
/// Description of SpeechbubbleContainer.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class SpeechbubbleContainer : TextContainer
|
||||
{
|
||||
private Point _initialGripperPoint;
|
||||
|
||||
// Only used for serializing the TargetGripper location
|
||||
/// <summary>
|
||||
/// Location of target gripper, used to draw the tail of the speech bubble.
|
||||
/// </summary>
|
||||
private Point _storedTargetGripperLocation;
|
||||
|
||||
/// <summary>
|
||||
/// Store the current location of the target gripper
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
[OnSerializing]
|
||||
private void SetValuesOnSerializing(StreamingContext context)
|
||||
/// <inheritdoc cref="_storedTargetGripperLocation"/>
|
||||
public Point StoredTargetGripperLocation
|
||||
{
|
||||
if (TargetAdorner != null)
|
||||
{
|
||||
_storedTargetGripperLocation = TargetAdorner.Location;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore the target gripper
|
||||
/// </summary>
|
||||
/// <param name="streamingContext">StreamingContext</param>
|
||||
protected override void OnDeserialized(StreamingContext streamingContext)
|
||||
{
|
||||
base.OnDeserialized(streamingContext);
|
||||
InitTargetAdorner(_storedTargetGripperLocation);
|
||||
get => _storedTargetGripperLocation;
|
||||
set => _storedTargetGripperLocation = value;
|
||||
}
|
||||
|
||||
public SpeechbubbleContainer(ISurface parent) : base(parent)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <remarks>And initialize target adorner using the stored gripper location.</remarks>
|
||||
public override void OnDeserialized()
|
||||
{
|
||||
base.OnDeserialized();
|
||||
InitTargetAdorner(_storedTargetGripperLocation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We set our own field values
|
||||
/// </summary>
|
||||
|
|
|
@ -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.
|
||||
/// </summary>
|
||||
[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,43 +55,22 @@ 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.
|
||||
/// <summary>
|
||||
/// Used to store the counter start of the Surface on serialization / deserialization.
|
||||
/// </summary>
|
||||
/// <remarks>The Surface itself is not stored. All StepLabelContainer will store the same start value. It's a bit hacky. </remarks>
|
||||
private int _counterStart = 1;
|
||||
|
||||
public int Number
|
||||
{
|
||||
get { return _number; }
|
||||
set { _number = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the counter before serializing
|
||||
/// Used to store the number of this label on serialization / deserialization.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
[OnSerializing]
|
||||
private void SetValuesOnSerializing(StreamingContext context)
|
||||
{
|
||||
if (InternalParent == null) return;
|
||||
/// <remarks> 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 <see cref="Draw"/>. </remarks>
|
||||
public int Number { get;set; }
|
||||
|
||||
Number = InternalParent.CountStepLabels(this);
|
||||
_counterStart = InternalParent.CounterStart;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore values that don't serialize
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
protected override void OnDeserialized(StreamingContext context)
|
||||
{
|
||||
Init();
|
||||
_stringFormat = new StringFormat
|
||||
{
|
||||
Alignment = StringAlignment.Center,
|
||||
LineAlignment = StringAlignment.Center
|
||||
};
|
||||
/// <inheritdoc cref="_counterStart"/>
|
||||
public int CounterStart {
|
||||
get { return _counterStart; }
|
||||
set { _counterStart = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -691,57 +689,16 @@ namespace Greenshot.Editor.Drawing
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This saves the elements of this surface to a stream.
|
||||
/// Is used to save a template of the complete surface
|
||||
/// </summary>
|
||||
/// <param name="streamWrite"></param>
|
||||
/// <returns></returns>
|
||||
public long SaveElementsToStream(Stream streamWrite)
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This loads elements from a stream, among others this is used to load a surface.
|
||||
/// </summary>
|
||||
/// <param name="streamRead"></param>
|
||||
public void LoadElementsFromStream(Stream streamRead)
|
||||
{
|
||||
try
|
||||
{
|
||||
BinaryFormatter binaryRead = new BinaryFormatter();
|
||||
binaryRead.Binder = new BinaryFormatterHelper();
|
||||
IDrawableContainerList loadedElements = (IDrawableContainerList) binaryRead.Deserialize(streamRead);
|
||||
loadedElements.Parent = this;
|
||||
containerList.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);
|
||||
}
|
||||
AddElements(containerList);
|
||||
SelectElements(containerList);
|
||||
FieldAggregator.BindElements(containerList);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -2083,7 +2040,10 @@ namespace Greenshot.Editor.Drawing
|
|||
public void CutSelectedElements()
|
||||
{
|
||||
if (!HasSelectedElements) return;
|
||||
ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements);
|
||||
|
||||
var serializedData = DtoHelper.SerializeDrawableContainerList((DrawableContainerList)selectedElements);
|
||||
ClipboardHelper.SetClipboardData(typeof(DrawableContainerListDto), serializedData);
|
||||
|
||||
RemoveSelectedElements();
|
||||
}
|
||||
|
||||
|
@ -2093,7 +2053,9 @@ namespace Greenshot.Editor.Drawing
|
|||
public void CopySelectedElements()
|
||||
{
|
||||
if (!HasSelectedElements) return;
|
||||
ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements);
|
||||
|
||||
var serializedData = DtoHelper.SerializeDrawableContainerList((DrawableContainerList)selectedElements);
|
||||
ClipboardHelper.SetClipboardData(typeof(DrawableContainerListDto), serializedData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -2200,9 +2162,12 @@ namespace Greenshot.Editor.Drawing
|
|||
}
|
||||
}
|
||||
|
||||
if (formats.Contains(typeof(IDrawableContainerList).FullName))
|
||||
if (formats.Contains(typeof(DrawableContainerListDto).FullName))
|
||||
{
|
||||
IDrawableContainerList dcs = (IDrawableContainerList) ClipboardHelper.GetFromDataObject(clipboard, typeof(IDrawableContainerList));
|
||||
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
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using Dapplo.Windows.Common.Structs;
|
||||
|
@ -32,33 +31,30 @@ namespace Greenshot.Editor.Drawing
|
|||
/// <summary>
|
||||
/// This provides a resizable SVG container, redrawing the SVG in the size the container takes.
|
||||
/// </summary>
|
||||
[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()
|
||||
private void Init()
|
||||
{
|
||||
base.Init();
|
||||
// Do nothing when there is no content
|
||||
if (_svgContent == null)
|
||||
if (SvgContent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_svgContent.Position = 0;
|
||||
SvgContent.Position = 0;
|
||||
|
||||
_svgDocument = SvgDocument.Open<SvgDocument>(_svgContent);
|
||||
_svgDocument = SvgDocument.Open<SvgDocument>(SvgContent);
|
||||
}
|
||||
|
||||
protected override Image ComputeBitmap()
|
||||
|
|
|
@ -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
|
|||
/// <summary>
|
||||
/// Represents a textbox (extends RectangleContainer for border/background support
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
/// <summary>
|
||||
/// The StringFormat object is not serializable!!
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do some logic to make sure all field are initiated correctly
|
||||
/// </summary>
|
||||
/// <param name="streamingContext">StreamingContext</param>
|
||||
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)
|
||||
|
|
|
@ -19,13 +19,12 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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)
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class VectorGraphicsContainer : DrawableContainer
|
||||
{
|
||||
private static readonly ILog LOG = LogManager.GetLogger(typeof(VectorGraphicsContainer));
|
||||
|
||||
/// <inheritdoc cref="RotationAngle"/>
|
||||
private int _rotationAngle;
|
||||
protected int RotationAngle
|
||||
|
||||
/// /// <summary>
|
||||
/// This is the rotation angle of the vector graphics. It is used to rotate the graphics when rendering in <see cref="ComputeBitmap"/>.
|
||||
/// </summary>
|
||||
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.
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
private Image _cachedImage;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor takes care of calling Init
|
||||
/// Constructor takes care of creating adorners
|
||||
/// </summary>
|
||||
/// <param name="parent">ISurface</param>
|
||||
public VectorGraphicsContainer(ISurface parent) : base(parent)
|
||||
protected VectorGraphicsContainer(ISurface parent) : base(parent)
|
||||
{
|
||||
Init();
|
||||
InitAdorners();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make sure Init is called after deserializing
|
||||
/// For vector graphics the <see cref="DrawableContainer.CreateDefaultAdorners"/> are not used. so we need to initialize the adorners here.
|
||||
/// </summary>
|
||||
/// <param name="streamingContext">StreamingContext</param>
|
||||
protected override void OnDeserialized(StreamingContext streamingContext)
|
||||
{
|
||||
base.OnDeserialized(streamingContext);
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Init is called after creating the instance, and from OnDeserialized
|
||||
/// This is the place to generate your adorners
|
||||
/// </summary>
|
||||
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!
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
|
|
@ -32,8 +32,10 @@ namespace Greenshot.Editor
|
|||
SimpleServiceProvider.Current.AddService<IFileFormatHandler>(
|
||||
// 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
|
||||
|
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.Drawing;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="ArrowContainer"/> objects.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class ArrowContainerDto : DrawableContainerDto
|
||||
{
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.Drawing;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="CursorContainer"/> objects.
|
||||
/// The <see cref="CursorContainer"/> is not really in use. For a capture with mouse cursor the IconContainer is used. See: cctor Surface(ICapture capture) in <see cref="Surface"/>.
|
||||
/// </summary>
|
||||
[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; }
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="DrawableContainer"/> objects.
|
||||
/// Simplified Version that supports Properties from <see cref="AbstractFieldHolderWithChildren"/> and <see cref="AbstractFieldHolder"/> as well.
|
||||
/// Ignore <see cref="AbstractFieldHolderWithChildren.Children"/> because it is only used for filters at the moment and all field values from the filters are already in <see cref="Fields"/>.
|
||||
/// </summary>
|
||||
[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<FieldDto> Fields { get; set; } = [];
|
||||
}
|
||||
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using System.Collections.Generic;
|
||||
using Greenshot.Editor.Drawing;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="DrawableContainerList"/> objects.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class DrawableContainerListDto
|
||||
{
|
||||
[Key(10)]
|
||||
public List<DrawableContainerDto> ContainerList { get; set; } = [];
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="Drawing.EllipseContainer"/> objects.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class EllipseContainerDto : DrawableContainerDto
|
||||
{
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using System.Collections.Generic;
|
||||
using Greenshot.Editor.Drawing;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="FreehandContainer"/> objects.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class FreehandContainerDto : DrawableContainerDto
|
||||
{
|
||||
[Key(100)]
|
||||
public List<PointDto> CapturePoints { get; set; } = [];
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.Drawing;
|
||||
using Greenshot.Editor.Drawing.Fields;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="HighlightContainer"/> objects.
|
||||
/// Ignore <see cref="DrawableContainer.Filters"/> because they would be recreated on deserialization based on field values of <see cref="FieldType.PREPARED_FILTER_HIGHLIGHT"/>.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class HighlightContainerDto : DrawableContainerDto
|
||||
{
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="Drawing.IconContainer"/> objects.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class IconContainerDto : DrawableContainerDto
|
||||
{
|
||||
[Key(100)]
|
||||
public byte[] Icon { get; set; }
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.Drawing;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="ImageContainer"/> objects.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class ImageContainerDto : DrawableContainerDto
|
||||
{
|
||||
[Key(100)]
|
||||
public byte[] Image { get; set; } // Store image as byte array
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.Drawing;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="LineContainer"/> objects.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class LineContainerDto : DrawableContainerDto
|
||||
{
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.Drawing;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="MetafileContainer"/> objects.
|
||||
/// Simplified version that supports properties from <see cref="VectorGraphicsContainer"/> as well.
|
||||
/// </summary>
|
||||
[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
|
||||
}
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.Drawing;
|
||||
using Greenshot.Editor.Drawing.Fields;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="ObfuscateContainer"/> objects.
|
||||
/// Ignore <see cref="DrawableContainer.Filters"/> because they would be recreated on deserialization based on field values of <see cref="FieldType.PREPARED_FILTER_OBFUSCATE"/>.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class ObfuscateContainerDto : DrawableContainerDto
|
||||
{
|
||||
}
|
33
src/Greenshot.Editor/FileFormat/Dto/Container/PointDto.cs
Normal file
33
src/Greenshot.Editor/FileFormat/Dto/Container/PointDto.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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; }
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.Drawing;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="RectangleContainer"/> objects.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class RectangleContainerDto : DrawableContainerDto
|
||||
{
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.Drawing;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="SpeechbubbleContainer"/> objects.
|
||||
/// </summary>
|
||||
[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 };
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="Drawing.StepLabelContainer"/> objects.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class StepLabelContainerDto : DrawableContainerDto
|
||||
{
|
||||
[Key(100)]
|
||||
public int Number { get; set; } = 1;
|
||||
|
||||
[Key(101)]
|
||||
public int CounterStart { get; set; } = 1;
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.Drawing;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
/// <summary>
|
||||
/// Data transfer object to serialize <see cref="SvgContainer"/> objects.
|
||||
/// Simplified version that supports properties from <see cref="VectorGraphicsContainer"/> as well.
|
||||
/// </summary>
|
||||
[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
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Container;
|
||||
|
||||
[MessagePackObject]
|
||||
public sealed class TextContainerDto : DrawableContainerDto
|
||||
{
|
||||
[Key(100)]
|
||||
public string Text { get; set; } = string.Empty;
|
||||
}
|
406
src/Greenshot.Editor/FileFormat/Dto/ConvertDomainToDto.cs
Normal file
406
src/Greenshot.Editor/FileFormat/Dto/ConvertDomainToDto.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods to convert domain objects, mainly drawable container and fields, into their corresponding Data Transfer Object (DTO) representations.
|
||||
/// </summary>
|
||||
/// <remarks>This class contains a collection of static ToDto() methods that handle the transformation</remarks>
|
||||
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)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a given value to its corresponding <see cref="FieldValueDto"/> representation.
|
||||
/// </summary>
|
||||
/// <remarks>The method is public mainly because of testing.</remarks>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
/// <returns>A specific subclass of <see cref="FieldValueDto"/> instance representing the provided value.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if the type of <paramref name="value"/> is not supported.</exception>
|
||||
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()}"),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified <see cref="Image"/> to a byte array in PNG format.
|
||||
/// </summary>
|
||||
private static byte[] ImageToByteArray(Image image)
|
||||
{
|
||||
if (image == null) return null;
|
||||
|
||||
using var memoryStream = new MemoryStream();
|
||||
image.Save(memoryStream, ImageFormat.Png);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified <see cref="Icon"/> to a byte array representation.
|
||||
/// </summary>
|
||||
private static byte[] IconToByteArray(Icon icon)
|
||||
{
|
||||
if (icon == null) return null;
|
||||
|
||||
using var memoryStream = new MemoryStream();
|
||||
icon.Save(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
}
|
399
src/Greenshot.Editor/FileFormat/Dto/ConvertDtoToDomain.cs
Normal file
399
src/Greenshot.Editor/FileFormat/Dto/ConvertDtoToDomain.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods to convert various Data Transfer Object (DTO) types into their corresponding domain types, mainly drawable container and fields.
|
||||
/// </summary>
|
||||
/// <remarks>This class contains a collection of static ToDomain() methods that handle the transformation</remarks>
|
||||
public static class ConvertDtoToDomain
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(ConvertDtoToDomain));
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the provided parent surface is null and creates a new one if it is.
|
||||
/// </summary>
|
||||
private static ISurface CheckOrCreateParentSurface( ISurface parentSurface) => parentSurface ?? SimpleServiceProvider.Current.GetInstance<Func<ISurface>>().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)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="FieldValueDto"/> instance to its corresponding value.
|
||||
/// </summary>
|
||||
/// <remarks>The method is public mainly because of testing.</remarks>
|
||||
/// <returns>The value extracted from the <paramref name="dto"/> using its <c>GetValue</c> method, or <see langword="null"/>
|
||||
/// if <paramref name="dto"/> is <see langword="null"/>.</returns>
|
||||
public static object ConvertDtoToValue(FieldValueDto dto)
|
||||
{
|
||||
return dto?.GetValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a drawable container.
|
||||
/// </summary>
|
||||
/// <remarks>This method sets the position, dimensions, and field values of the container based on the
|
||||
/// provided DTO. It also invokes the <see cref="DrawableContainer.OnDeserialized"/> method to finalize the
|
||||
/// initialization process.</remarks>
|
||||
/// <typeparam name="T">The type of the drawable container, which must inherit from <see cref="DrawableContainer"/>.</typeparam>
|
||||
/// <param name="container">The drawable container instance to initialize. Must not be <see langword="null"/>.</param>
|
||||
/// <param name="dto">The data transfer object containing the properties and field values to apply. Must not be <see
|
||||
/// langword="null"/>.</param>
|
||||
/// <returns>The initialized drawable container of type <typeparamref name="T"/>.</returns>
|
||||
private static T InitDrawableContainer<T>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transfers field values to a <see cref="DrawableContainer"/>.
|
||||
/// </summary>
|
||||
/// <remarks>This method uses <see cref="AbstractFieldHolderWithChildren.SetFieldValue"/> to add or update the field value.</remarks>
|
||||
private static void TranferFieldValues(List<FieldDto> dtoFields, DrawableContainer domain)
|
||||
{
|
||||
foreach (var field in dtoFields.Select(ToDomain))
|
||||
{
|
||||
domain.SetFieldValue(field.FieldType, field.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a byte array into an <see cref="Image"/> object.
|
||||
/// </summary>
|
||||
private static Image ByteArrayToImage(byte[] byteArrayIn)
|
||||
{
|
||||
if (byteArrayIn == null) return null;
|
||||
using var ms = new MemoryStream(byteArrayIn);
|
||||
return Image.FromStream(ms);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a byte array into an <see cref="Icon"/> object.
|
||||
/// </summary>
|
||||
private static Icon ByteArrayToIcon(byte[] byteArrayIn)
|
||||
{
|
||||
if (byteArrayIn == null) return null;
|
||||
using var ms = new MemoryStream(byteArrayIn);
|
||||
return new Icon(ms);
|
||||
}
|
||||
|
||||
}
|
||||
|
96
src/Greenshot.Editor/FileFormat/Dto/DtoHelper.cs
Normal file
96
src/Greenshot.Editor/FileFormat/Dto/DtoHelper.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// All DTO classes should not contain any business logic. This applies to helper methods as well
|
||||
/// So this is the place for them.
|
||||
/// </summary>
|
||||
public static class DtoHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the value of a field from a <see cref="DrawableContainerDto"/> object. Null if not found.
|
||||
/// </summary>
|
||||
/// <param name="drawableContainer"></param>
|
||||
/// <param name="fieldType"></param>
|
||||
/// <returns></returns>
|
||||
public static object GetFieldValue(DrawableContainerDto drawableContainer, IFieldType fieldType)
|
||||
{
|
||||
return drawableContainer.Fields?
|
||||
.FirstOrDefault(x => x.FieldTypeName == fieldType.Name)?
|
||||
.Value.GetValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We store Color as an ARGB integer, so we have to compare two colors by their ARGB values
|
||||
/// </summary>
|
||||
/// <param name="leftColor"></param>
|
||||
/// <param name="rightColor"></param>
|
||||
/// <returns></returns>
|
||||
public static bool CompareColorValue(Color leftColor, Color rightColor)
|
||||
{
|
||||
return leftColor.ToArgb() == rightColor.ToArgb();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method.
|
||||
/// <see cref="Color.ToString()"/> hides the ARGB value for KnownColor, so we have to use this method to print ARGB value every time.
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static string ArgbString(Color color) => $"ARGB({color.A}, {color.R}, {color.G}, {color.B})";
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a <see cref="DrawableContainerList"/> into a byte array using MessagePack serialization.
|
||||
/// </summary>
|
||||
/// <remarks>This method converts the DrawableContainerList into a DTO before serializing it.
|
||||
/// <br/> It is mainly used for copying the DrawableContainerList to Clipboard with "<c>Greenshot.Editor.FileFormat.Dto.Container.DrawableContainerListDto</c>" as identifier.
|
||||
/// See also: <seealso cref="ImageEditorForm.SupportedClipboardFormats"/> </remarks>
|
||||
/// <param name="drawableContainerList">The <see cref="DrawableContainerList"/> instance to serialize. Must not be <see langword="null"/>.</param>
|
||||
/// <returns>A byte array containing the serialized representation of the <see cref="DrawableContainerListDto"/>.</returns>
|
||||
public static byte[] SerializeDrawableContainerList(DrawableContainerList drawableContainerList)
|
||||
{
|
||||
var dto = ConvertDomainToDto.ToDto(drawableContainerList);
|
||||
return MessagePackSerializer.Serialize(dto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a byte array into a <see cref="DrawableContainerList"/> using MessagePack serialization.
|
||||
/// </summary>
|
||||
/// <remarks>It deserializes the byte array into a <see cref="DrawableContainerListDto"/> and then converts it to a <see cref="DrawableContainerList"/>.
|
||||
/// <br/> It is mainly used for reading the DrawableContainerList from Clipboard with "<c>Greenshot.Editor.FileFormat.Dto.Container.DrawableContainerListDto</c>" as identifier.
|
||||
/// See also: <seealso cref="ImageEditorForm.SupportedClipboardFormats"/>
|
||||
/// </remarks>
|
||||
/// <param name="data">A byte array containing the serialized representation of the <see cref="DrawableContainerListDto"/>.</param>
|
||||
public static DrawableContainerList DeserializeDrawableContainerList(byte[] data)
|
||||
{
|
||||
var dto = MessagePackSerializer.Deserialize<DrawableContainerListDto>(data);
|
||||
return ConvertDtoToDomain.ToDomain(dto);
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using System.Drawing;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Fields;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a field value that stores color information.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
36
src/Greenshot.Editor/FileFormat/Dto/Fields/FieldDto.cs
Normal file
36
src/Greenshot.Editor/FileFormat/Dto/Fields/FieldDto.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
45
src/Greenshot.Editor/FileFormat/Dto/Fields/FieldValueDto.cs
Normal file
45
src/Greenshot.Editor/FileFormat/Dto/Fields/FieldValueDto.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.Drawing.Fields;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto.Fields;
|
||||
|
||||
/// <summary>
|
||||
/// This is a specific Dto to support serialization for the possible types in <see cref="Field.Value"/>
|
||||
/// </summary>
|
||||
[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();
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
62
src/Greenshot.Editor/FileFormat/Dto/GreenshotFileDto.cs
Normal file
62
src/Greenshot.Editor/FileFormat/Dto/GreenshotFileDto.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.FileFormat.Dto.Container;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto;
|
||||
|
||||
/// <summary>
|
||||
/// Is Data Transfer Object (DTO) for <see cref="GreenshotFile"/>
|
||||
/// This represents the main class for a .greenshot file.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class GreenshotFileDto
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="GreenshotFile.SchemaVersion"/>
|
||||
/// </summary>
|
||||
[Key(0)]
|
||||
public int SchemaVersion { get; set; } = GreenshotFileVersionHandler.CurrentSchemaVersion;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="GreenshotFile.FormatVersion"/>
|
||||
/// </summary>
|
||||
[Key(1)]
|
||||
public GreenshotFileVersionHandler.GreenshotFileFormatVersion FormatVersion { get; set; } = GreenshotFileVersionHandler.GreenshotFileFormatVersion.Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="GreenshotFile.Image"/>
|
||||
/// </summary>
|
||||
[Key(11)]
|
||||
public byte[] Image { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="GreenshotFile.ContainerList"/>
|
||||
/// </summary>
|
||||
[Key(12)]
|
||||
public DrawableContainerListDto ContainerList { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="GreenshotFile.RenderedImage"/>
|
||||
/// </summary>
|
||||
[Key(13)]
|
||||
public byte[] RenderedImage { get; set; }
|
||||
}
|
50
src/Greenshot.Editor/FileFormat/Dto/GreenshotTemplateDto.cs
Normal file
50
src/Greenshot.Editor/FileFormat/Dto/GreenshotTemplateDto.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.FileFormat.Dto.Container;
|
||||
using MessagePack;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.Dto;
|
||||
|
||||
/// <summary>
|
||||
/// Is Data Transfer Object (DTO) for <see cref="GreenshotTemplate"/>
|
||||
/// This represents the main class for a .gst file.
|
||||
/// </summary>
|
||||
[MessagePackObject]
|
||||
public sealed class GreenshotTemplateDto
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="GreenshotTemplate.SchemaVersion"/>
|
||||
/// </summary>
|
||||
[Key(0)]
|
||||
public int SchemaVersion { get; set; } = GreenshotFileVersionHandler.CurrentSchemaVersion;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="GreenshotTemplate.FormatVersion"/>
|
||||
/// </summary>
|
||||
[Key(1)]
|
||||
public GreenshotFileVersionHandler.GreenshotFileFormatVersion FormatVersion { get; set; } = GreenshotFileVersionHandler.GreenshotFileFormatVersion.Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="GreenshotTemplate.ContainerList"/>
|
||||
/// </summary>
|
||||
[Key(11)]
|
||||
public DrawableContainerListDto ContainerList { get; set; } = new();
|
||||
}
|
59
src/Greenshot.Editor/FileFormat/GreenshotFile.cs
Normal file
59
src/Greenshot.Editor/FileFormat/GreenshotFile.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Drawing;
|
||||
using Greenshot.Editor.Drawing;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a .greenshot file as domain object.
|
||||
/// </summary>
|
||||
public sealed class GreenshotFile
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public DrawableContainerList ContainerList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The main image of the surface. It is captured from the screen or opend from a file.
|
||||
/// </summary>
|
||||
public Image Image { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The image rendered from the surface, which includes all drawable containers.
|
||||
/// </summary>
|
||||
public Image RenderedImage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public GreenshotFileVersionHandler.GreenshotFileFormatVersion FormatVersion { get; set; } = GreenshotFileVersionHandler.GreenshotFileFormatVersion.Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// Version of the file schema
|
||||
/// </summary>
|
||||
public int SchemaVersion { get; set; }
|
||||
}
|
221
src/Greenshot.Editor/FileFormat/GreenshotFileVersionHandler.cs
Normal file
221
src/Greenshot.Editor/FileFormat/GreenshotFileVersionHandler.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides functionality for handling all supported Greenshot file format versions.
|
||||
/// <remarks>It also provides methods to create a <see cref="GreenshotFile"/> from a surface and to create a <see cref="ISurface"/> from a Greenshot file.</remarks>
|
||||
/// </summary>
|
||||
public sealed class GreenshotFileVersionHandler
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileVersionHandler));
|
||||
|
||||
/// <summary>
|
||||
/// Represents the file format version for greenshot files. This includes greenshot templates as well.
|
||||
/// </summary>
|
||||
/// <remarks> The file versions are now independent of the app version.<br/>
|
||||
/// 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.
|
||||
/// <code> {serializer version}.{schema version} </code>
|
||||
/// The first part is <see cref="GreenshotFileFormatVersion"/>. This decides wich serializer/ binary data structure is used.<br/>
|
||||
/// The second part is <see cref="GreenshotFileDto.SchemaVersion"/>. This schema version only needs to be changed if certain actions are necessary for backward compatibility.<br/>
|
||||
///
|
||||
/// The old versions still fit this pattern.
|
||||
/// </remarks>
|
||||
public enum GreenshotFileFormatVersion
|
||||
{
|
||||
Unknown = 0,
|
||||
/// <summary>
|
||||
/// This format uses BinaryFormat serialization, supporting Greenshot file versions 01.02 and 01.03
|
||||
/// </summary>
|
||||
V1 = 1,
|
||||
/// <summary>
|
||||
/// This format uses MessagePack serialization
|
||||
/// </summary>
|
||||
V2 = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Version of the current file schema. More precisely, the version of the Dto structure. This includes greenshot templates as well.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Increase this version if you change the Dto structure in a way that breaks backward compatibility.
|
||||
/// After incrementing this version, you need to extend <see cref="GreenshotFileV2.MigrateToCurrentVersion"/>.
|
||||
/// <para>
|
||||
/// ./FileFormat/readme.md for more information about the file format and versioning.
|
||||
/// </para></remarks>
|
||||
public const int CurrentSchemaVersion = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Loads a <see cref="GreenshotFile"/> from the stream.
|
||||
/// </summary>
|
||||
/// <remarks>This method detects the file format version of the Greenshot file and loads the GreenshotFile accordingly.</remarks>
|
||||
/// <param name="greenshotFileStream">A <see cref="Stream"/> containing the Greenshot file data.</param>
|
||||
/// <returns>The loaded <see cref="GreenshotFile"/> .</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if the stream does not contain a valid Greenshot file.</exception>
|
||||
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!")
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="CreateGreenshotFile"/>
|
||||
/// <inheritdoc cref="SaveToStream"/>
|
||||
/// </summary>
|
||||
public static bool SaveToStreamInCurrentVersion( ISurface surface, Stream stream) =>
|
||||
SaveToStream(CreateGreenshotFile(surface), stream);
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="GreenshotFileV2.SaveToStream"/>
|
||||
/// </summary>
|
||||
private static bool SaveToStream(GreenshotFile greenshotFile, Stream stream) => GreenshotFileV2.SaveToStream(greenshotFile, stream);
|
||||
|
||||
/// <summary>
|
||||
/// Detects the Greenshot file version from the provided stream.
|
||||
/// </summary>
|
||||
/// <param name="greenshotFileStream">A <see cref="Stream"/> containing the Greenshot file data.</param>
|
||||
/// <returns><see cref="GreenshotFileFormatVersion"/></returns>
|
||||
/// <exception cref="ArgumentException">Thrown if the file format version cannot be determined.</exception>
|
||||
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!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="GreenshotFile"/> from surface.
|
||||
/// <inheritdoc cref="CreateGreenshotFileInCurrentVersion"/>
|
||||
/// </summary>
|
||||
/// <param name="surface">Cannot be <see langword="null"/>.</param>
|
||||
/// <returns>A <see cref="GreenshotFile"/> with surface's image, rendered image, and elements.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="surface"/> is <see langword="null"/> or if its <see cref="ISurface.Image"/> is <see langword="null"/>.</exception>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="GreenshotFile"/> with the specified image and elements and also
|
||||
/// configured with the current format and schema versions.
|
||||
/// </summary>
|
||||
/// <param name="image">Background image of the surface. </param>
|
||||
/// <param name="renderedImage">rendered image of the surface, which includes all drawable containers. </param>
|
||||
/// <param name="elements">Elements that are positioned on the surface, like text, shapes, and images. </param>
|
||||
/// <returns></returns>
|
||||
private static GreenshotFile CreateGreenshotFileInCurrentVersion(Bitmap image, Bitmap renderedImage, DrawableContainerList elements)
|
||||
{
|
||||
return new GreenshotFile
|
||||
{
|
||||
Image = image,
|
||||
RenderedImage = renderedImage,
|
||||
ContainerList = elements,
|
||||
FormatVersion = GreenshotFileFormatVersion.V2,
|
||||
SchemaVersion = CurrentSchemaVersion,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ISurface"/> instance and initializes it with the data from the specified <see
|
||||
/// cref="GreenshotFile"/>. It validates the data and creates always a new surface, or throw an exception.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="greenshotFile"/> is <see langword="null"/>
|
||||
/// or if its <see cref="GreenshotFile.Image"/> is <see langword="null"/>.</exception>
|
||||
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<Func<ISurface>>().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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="LoadFromStream"/>
|
||||
/// <inheritdoc cref="CreateSurface"/>
|
||||
/// </summary>
|
||||
public static ISurface CreateSurfaceFromStream(Stream stream) =>
|
||||
CreateSurface(LoadFromStream(stream));
|
||||
}
|
47
src/Greenshot.Editor/FileFormat/GreenshotTemplate.cs
Normal file
47
src/Greenshot.Editor/FileFormat/GreenshotTemplate.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using Greenshot.Editor.Drawing;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a .gst file as domain object.
|
||||
/// </summary>
|
||||
public sealed class GreenshotTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public DrawableContainerList ContainerList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public GreenshotFileVersionHandler.GreenshotFileFormatVersion FormatVersion { get; set; } = GreenshotFileVersionHandler.GreenshotFileFormatVersion.Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// Version of the file schema
|
||||
/// </summary>
|
||||
public int SchemaVersion { get; set; }
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides functionality for handling different Greenshot template file format versions.
|
||||
/// </summary>
|
||||
public class GreenshotTemplateVersionHandler
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileVersionHandler));
|
||||
|
||||
/// <summary>
|
||||
/// Loads a <see cref="GreenshotTemplate"/> from the stream.
|
||||
/// </summary>
|
||||
/// <remarks>This method detects the file format version of the Greenshot template and loads the GreenshotTemplate accordingly.</remarks>
|
||||
/// <param name="greenshotTemplateStream">A <see cref="Stream"/> containing the Greenshot template data.</param>
|
||||
/// <returns>The loaded <see cref="GreenshotTemplate"/> .</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if the stream does not contain a valid Greenshot template.</exception>
|
||||
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!")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="CreateGreenshotFileInCurrentVersion"/>
|
||||
/// <inheritdoc cref="SaveToStream"/>
|
||||
/// </summary>
|
||||
public static bool SaveToStreamInCurrentVersion(DrawableContainerList elements, Stream stream) =>
|
||||
SaveToStream(CreateGreenshotTemplateInCurrentVersion(elements), stream);
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="GreenshotTemplateV2.SaveToStream"/>
|
||||
/// </summary>
|
||||
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!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="GreenshotTemplate"/> with the specified elements and also
|
||||
/// configured with the current format and schema versions.
|
||||
/// </summary>
|
||||
/// <param name="elements"></param>
|
||||
/// <returns></returns>
|
||||
private static GreenshotTemplate CreateGreenshotTemplateInCurrentVersion(DrawableContainerList elements)
|
||||
{
|
||||
return new GreenshotTemplate
|
||||
{
|
||||
ContainerList = elements,
|
||||
FormatVersion = GreenshotFileFormatVersion.V2,
|
||||
SchemaVersion = CurrentSchemaVersion,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
163
src/Greenshot.Editor/FileFormat/V1/GreenshotFileV1.cs
Normal file
163
src/Greenshot.Editor/FileFormat/V1/GreenshotFileV1.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods for loading Greenshot file format version V1.
|
||||
/// </summary>
|
||||
/// <remarks>Greenshot file format version V1 supports Greenshot file versions 01.02 and 01.03.
|
||||
/// Saving to this format is not supported anymore.</remarks>
|
||||
internal static class GreenshotFileV1
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileV1));
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the stream matches the Greenshot file format version V1.
|
||||
/// </summary>
|
||||
/// <remarks>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.</remarks>
|
||||
/// <param name="greenshotFileStream">The stream containing the file to check. The stream must support seeking.</param>
|
||||
/// <returns><see langword="true"/> if the file format matches the Greenshot version V1 and the file contains surface
|
||||
/// data; otherwise, <see langword="false"/>.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This load function support Greenshot file version 01.02 and 01.03.
|
||||
/// </summary>
|
||||
/// <param name="greenshotFileStream">A <see cref="Stream"/> containing the Greenshot file data.</param>
|
||||
/// <returns>The loaded Greenshot file.</returns>
|
||||
/// <exception cref="ArgumentException">If schema version is not found in the stream.</exception>
|
||||
/// <exception cref="SecurityAccessDeniedException">If legacy container list cannot be read due to security restrictions.</exception>
|
||||
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;
|
||||
}
|
||||
}
|
93
src/Greenshot.Editor/FileFormat/V1/GreenshotTemplateV1.cs
Normal file
93
src/Greenshot.Editor/FileFormat/V1/GreenshotTemplateV1.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.ServiceModel.Security;
|
||||
using Greenshot.Editor.FileFormat.V1.Legacy;
|
||||
using log4net;
|
||||
|
||||
namespace Greenshot.Editor.FileFormat.V1;
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods for loading Greenshot template in file format version V1.
|
||||
/// </summary>
|
||||
/// <remarks>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.</remarks>
|
||||
internal static class GreenshotTemplateV1
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotTemplateV1));
|
||||
|
||||
/// <summary>Determines whether the stream matches the Greenshot template format version V1.
|
||||
/// </summary>
|
||||
/// <remarks>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.</remarks>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This load function support Greenshot template from app version 1.2 and 1.3.
|
||||
/// </summary>
|
||||
/// <remarks>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.</remarks>
|
||||
/// <param name="greenshotTemplateFileStream">A <see cref="Stream"/> containing the Greenshot template data.</param>
|
||||
/// <returns>The loaded Greenshot template.</returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
303
src/Greenshot.Editor/FileFormat/V1/Legacy/ConvertLegacyToDto.cs
Normal file
303
src/Greenshot.Editor/FileFormat/V1/Legacy/ConvertLegacyToDto.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods for converting legacy drawable container objects into their corresponding Data Transfer Object
|
||||
/// (DTO) representations.
|
||||
/// </summary>
|
||||
/// <remarks>This class contains a collection of static ToDto() methods that handle the transformation</remarks>
|
||||
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<DrawableContainerDto>() };
|
||||
foreach (var w in containerList)
|
||||
{
|
||||
dto.ContainerList.Add(ToDto(w));
|
||||
}
|
||||
return dto;
|
||||
}
|
||||
|
||||
private static List<FieldDto> ToFieldDtos(IList<LegacyField> fields)
|
||||
{
|
||||
var list = new List<FieldDto>();
|
||||
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<PointDto>();
|
||||
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()}")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
358
src/Greenshot.Editor/FileFormat/V1/Legacy/LegacyClasses.cs
Normal file
358
src/Greenshot.Editor/FileFormat/V1/Legacy/LegacyClasses.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<LegacyField> Fields;
|
||||
|
||||
protected LegacyFieldHolder(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
Fields = (IList<LegacyField>)info.GetValue("AbstractFieldHolder+fields", typeof(IList<LegacyField>));
|
||||
}
|
||||
|
||||
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<LegacyFieldHolder> Children;
|
||||
|
||||
protected LegacyFieldHolderWithChildren(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
Children = (IList<LegacyFieldHolder>)info.GetValue("Children", typeof(IList<LegacyFieldHolder>));
|
||||
}
|
||||
}
|
||||
#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<LegacyDrawableContainer>
|
||||
{
|
||||
}
|
||||
|
||||
[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<Point> CapturePoints;
|
||||
|
||||
protected LegacyFreehandContainer(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
CapturePoints = (List<Point>)info.GetValue("capturePoints", typeof(List<Point>));
|
||||
}
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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+.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
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
|
||||
|
||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Deserializes a legacy Greenshot file stream and converts it into a <see cref="DrawableContainerList"/> object.
|
||||
/// </summary>
|
||||
/// <remarks>This method processes a legacy Greenshot file by deserializing its contents to <see cref="LegacyDrawableContainerList"/>,
|
||||
/// convert this to <see cref="DrawableContainerListDto"/> and then to <see cref="DrawableContainerList"/>. The input stream must contain data in the expected legacy format.</remarks>
|
||||
/// <param name="stream">The input stream containing the serialized legacy Greenshot file data. Must not be <see langword="null"/>.</param>
|
||||
/// <returns>A <see cref="DrawableContainerList"/> representing the deserialized and converted data from the legacy file.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if the stream does not contain a valid legacy Greenshot file.</exception>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 <see cref="GreenshotFileVersionHandler"/>.
|
||||
/// It extracts the container list sub stream from the Greenshot file stream and call <see cref="GetContainerListFromLegacyContainerListStream(Stream)"/>.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
internal sealed class LegacySerializationBinder : SerializationBinder
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(LegacySerializationBinder));
|
||||
private static readonly IDictionary<string, Type> TypeMapper = new Dictionary<string, Type>
|
||||
{
|
||||
// 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<System.Drawing.Point>)},
|
||||
|
||||
// legacy types
|
||||
{"System.Collections.Generic.List`1[[Greenshot.Base.Interfaces.Drawing.IFieldHolder", typeof(List<LegacyFieldHolder>)},
|
||||
{"System.Collections.Generic.List`1[[Greenshot.Base.Interfaces.Drawing.IField", typeof(List<LegacyField>)},
|
||||
{"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) },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Do the type mapping
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">Assembly for the type that was serialized</param>
|
||||
/// <param name="typeName">Type that was serialized</param>
|
||||
/// <returns>Type which was mapped</returns>
|
||||
/// <exception cref="SecurityAccessDeniedException">If something smells fishy</exception>
|
||||
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}");
|
||||
}
|
||||
}
|
266
src/Greenshot.Editor/FileFormat/V2/GreenshotFileV2.cs
Normal file
266
src/Greenshot.Editor/FileFormat/V2/GreenshotFileV2.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods for loading and saving Greenshot file format version V2.
|
||||
/// </summary>
|
||||
internal static class GreenshotFileV2
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileV2));
|
||||
|
||||
/// <summary>
|
||||
/// Represents the marker string used to identify the file format as specific to Greenshot file.
|
||||
/// </summary>
|
||||
private const string FileFormatMarker = "Greenshot";
|
||||
|
||||
/// <summary>
|
||||
/// Represents the file format version used for Greenshot files, formatted as a two-digit string.
|
||||
/// </summary>
|
||||
/// <remarks> Thie ist fixed to <see cref="GreenshotFileVersionHandler.GreenshotFileFormatVersion.V2"/> ("02") .</remarks>
|
||||
private static readonly string FileFormatVersion = ((int)GreenshotFileVersionHandler.GreenshotFileFormatVersion.V2).ToString("D2");
|
||||
|
||||
/// <summary>
|
||||
/// Represents the current schema version of the Greenshot file in a two-digit string format.
|
||||
/// </summary>
|
||||
/// <remarks> It is derived from the <see cref="GreenshotFileVersionHandler.CurrentSchemaVersion"/> property.</remarks>
|
||||
private static readonly string SchemaVersion = GreenshotFileVersionHandler.CurrentSchemaVersion.ToString("D2");
|
||||
|
||||
/// <summary>
|
||||
/// Represents the complete version string, combining the file format version and schema version.
|
||||
/// </summary>
|
||||
/// <remarks>This value is a concatenation of <see cref="FileFormatVersion"/> and <see
|
||||
/// cref="SchemaVersion"/>, separated by a period ("."), e.g. "02.01" for version 2.1.</remarks>
|
||||
private static readonly string CompleteVersion = FileFormatVersion + Dot + SchemaVersion;
|
||||
|
||||
private const string Dot = ".";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the stream matches the Greenshot file format version V2.
|
||||
/// </summary>
|
||||
/// <remarks>It checks for specific markers <see cref="FileFormatMarker"/> + <see cref="FileFormatVersion"/> 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.</remarks>
|
||||
/// <param name="greenshotFileStream">The stream containing the file to check. The stream must support seeking.</param>
|
||||
/// <returns><see langword="true"/> if the file format matches the Greenshot version V2; otherwise, <see langword="false"/>.</returns>
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the specified <see cref="GreenshotFile"/> to the provided stream in the Greenshot file format version V2.
|
||||
/// </summary>
|
||||
/// <remarks>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.<br/>
|
||||
/// The file parts are:<br/>
|
||||
/// 1. The PNG image data<br/>
|
||||
/// 2. The binary data of the <see cref="GreenshotFileDto"/>. (serialized with MessagePack).<br/>
|
||||
/// 3. 8 bytes for the binary data size(Int64).<br/>
|
||||
/// 4. 11 bytes for the Greenshot marker string <see cref="FileFormatMarker"/> + <see cref="FileFormatVersion"/> (i.e. `"Greenshot02"`).<br/>
|
||||
/// 5. 3 bytes for the Greenshot file schema version <see cref="GreenshotFileVersionHandler.CurrentSchemaVersion"/> (i.e. `".01"`).<br/>
|
||||
/// </remarks>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This load function supports file version 02
|
||||
/// </summary>
|
||||
/// <param name="greenshotFileStream">A <see cref="Stream"/> containing the Greenshot file data.</param>
|
||||
/// <returns>The loaded Greenshot file.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified <see cref="GreenshotFile"/> instance into a byte array by using MessagePackSerializer.
|
||||
/// </summary>
|
||||
/// <param name="data">The <see cref="GreenshotFile"/> instance to serialize. Cannot be <see langword="null"/>.</param>
|
||||
/// <returns>A byte array representing the serialized form of the <see cref="GreenshotFile"/> instance.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a byte array into an <see cref="GreenshotFile"/> object by using MessagePackSerializer.
|
||||
/// </summary>
|
||||
/// <remarks> This also migrates the loaded <see cref="GreenshotFileDto"/> to the current version if necessary.
|
||||
/// </remarks>
|
||||
/// <param name="bytes">The byte array containing the serialized data of an <see cref="GreenshotFile"/>.</param>
|
||||
/// <returns>An <see cref="GreenshotFile"/> object deserialized from the provided byte array.</returns>
|
||||
private static GreenshotFile Deserialize(byte[] bytes)
|
||||
{
|
||||
var dto = MessagePackSerializer.Deserialize<GreenshotFileDto>(bytes);
|
||||
var currentVersionDto = MigrateToCurrentVersion(dto);
|
||||
return ConvertDtoToDomain.ToDomain(currentVersionDto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main method for migrating an <see cref="GreenshotFileDto"/> to the current version.
|
||||
/// </summary>
|
||||
/// <remarks>Does nothing if the version is already current.</remarks>
|
||||
/// <param name="dto"></param>
|
||||
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;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
212
src/Greenshot.Editor/FileFormat/V2/GreenshotTemplateV2.cs
Normal file
212
src/Greenshot.Editor/FileFormat/V2/GreenshotTemplateV2.cs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods for loading and saving Greenshot template file format version V2.
|
||||
/// </summary>
|
||||
internal static class GreenshotTemplateV2
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotTemplateV2));
|
||||
|
||||
/// <summary>
|
||||
/// Represents the marker string used to identify the file format as specific to Greenshot templates.
|
||||
/// </summary>
|
||||
private const string FileFormatMarker = "GreenshotTemplate";
|
||||
|
||||
/// <summary>
|
||||
/// Represents the file format version used for Greenshot files, formatted as a two-digit string.
|
||||
/// </summary>
|
||||
/// <remarks> Thie ist fixed to <see cref="GreenshotFileFormatVersion.V2"/> ("02") .</remarks>
|
||||
private static readonly string FileFormatVersion = ((int)GreenshotFileFormatVersion.V2).ToString("D2");
|
||||
|
||||
/// <summary>
|
||||
/// Represents the current schema version of the Greenshot file in a two-digit string format.
|
||||
/// </summary>
|
||||
/// <remarks> It is derived from the <see cref="GreenshotFileVersionHandler.CurrentSchemaVersion"/> property.</remarks>
|
||||
private static readonly string SchemaVersion = CurrentSchemaVersion.ToString("D2");
|
||||
|
||||
/// <summary>
|
||||
/// Represents the complete version string, combining the file format version and schema version.
|
||||
/// </summary>
|
||||
/// <remarks>This value is a concatenation of <see cref="FileFormatVersion"/> and <see
|
||||
/// cref="SchemaVersion"/>, separated by a period ("."), e.g. "02.01" for version 2.1.</remarks>
|
||||
private static readonly string CompleteVersion = FileFormatVersion + "." + SchemaVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the stream matches the Greenshot file format version V2.
|
||||
/// </summary>
|
||||
/// <remarks>The stream's position will be modified during the operation but will remain open after the method completes.</remarks>
|
||||
/// <param name="greenshotTemplateFileStream">The stream containing the file to check. The stream must support seeking.</param>
|
||||
/// <returns><see langword="true"/> if the file format matches the Greenshot version V2; otherwise, <see langword="false"/>.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This load function supports file format version V2.
|
||||
/// </summary>
|
||||
/// <param name="greenshotTemplateFileStream">A <see cref="Stream"/> containing the Greenshot template data.</param>
|
||||
/// <returns>The loaded Greenshot template.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the <see cref="GreenshotTemplate"/> to the provided stream in the Greenshot template format.
|
||||
/// </summary>
|
||||
/// <remarks>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.<br/>
|
||||
/// The file parts are:<br/>
|
||||
/// 1. 19 bytes for the Greenshot marker string <see cref="FileFormatMarker"/> + <see cref="FileFormatVersion"/> (i.e. `"GreenshotTemplate02"`).<br/>
|
||||
/// 2. 3 bytes for the Greenshot file schema version <see cref="GreenshotFileVersionHandler.CurrentSchemaVersion"/> (i.e. `".01"`).<br/>
|
||||
/// 3. The binary data of the <see cref="GreenshotTemplateDto"/>. (serialized with MessagePack).<br/>
|
||||
/// </remarks>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified <see cref="GreenshotTemplate"/> instance into a byte array by using MessagePackSerializer.
|
||||
/// </summary>
|
||||
/// <param name="data">The <see cref="GreenshotTemplate"/> instance to serialize. Cannot be <see langword="null"/>.</param>
|
||||
/// <returns>A byte array representing the serialized form of the <see cref="GreenshotTemplate"/> instance.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a byte array into an <see cref="GreenshotTemplate"/> object by using MessagePackSerializer.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The byte array containing the serialized data of an <see cref="GreenshotTemplate"/>.</param>
|
||||
/// <returns>An <see cref="GreenshotTemplate"/> object deserialized from the provided byte array.</returns>
|
||||
private static GreenshotTemplate Deserialize(byte[] bytes)
|
||||
{
|
||||
var dto = MessagePackSerializer.Deserialize<GreenshotTemplateDto>(bytes);
|
||||
var currentVersionDto = MigrateToCurrentVersion(dto);
|
||||
return ConvertDtoToDomain.ToDomain(currentVersionDto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main method for migrating an <see cref="GreenshotTemplateDto"/> to the current version.
|
||||
/// </summary>
|
||||
/// <remarks>Does nothing if the version is already current.</remarks>
|
||||
/// <param name="dto"></param>
|
||||
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;
|
||||
}
|
||||
*/
|
||||
}
|
216
src/Greenshot.Editor/FileFormat/readme.md
Normal file
216
src/Greenshot.Editor/FileFormat/readme.md
Normal file
|
@ -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<GreenshotFileDto>(dto);
|
||||
var dto = MessagePackSerializer.Deserialize<GreenshotFileDto>(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.
|
|
@ -40,17 +40,20 @@ namespace Greenshot.Editor.FileFormatHandlers
|
|||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap);
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation taking the TryLoadFromStream image and placing it in an ImageContainer
|
||||
/// <inheritdoc />
|
||||
/// </summary>
|
||||
/// <remarks>Default implementation taking the TryLoadFromStream image and placing it in an <see cref="ImageContainer"/> </remarks>
|
||||
/// <param name="stream">Stream</param>
|
||||
/// <param name="extension">string</param>
|
||||
/// <param name="parent">ISurface</param>
|
||||
/// <returns>IEnumerable{IDrawableContainer}</returns>
|
||||
/// <returns>One <see cref="ImageContainer"/> if it was possible to load an image from the stream</returns>
|
||||
public virtual IEnumerable<IDrawableContainer> LoadDrawablesFromStream(Stream stream, string extension, ISurface parent = null)
|
||||
{
|
||||
if (TryLoadFromStream(stream, extension, out var bitmap))
|
||||
|
|
|
@ -115,7 +115,11 @@ namespace Greenshot.Editor.FileFormatHandlers
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc />
|
||||
/// </summary>
|
||||
/// <remarks>This default implementation uses <see cref="Image.FromStream(System.IO.Stream)"/></remarks>
|
||||
/// <returns><see langword="true"/> if the image was successfully loaded into a <see cref="Bitmap"/>; otherwise, <see langword="false"/>.</returns>
|
||||
public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -22,18 +22,16 @@
|
|||
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
|
||||
{
|
||||
public class GreenshotFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler
|
||||
namespace Greenshot.Editor.FileFormatHandlers;
|
||||
|
||||
public sealed class GreenshotFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotFileFormatHandler));
|
||||
private readonly IReadOnlyCollection<string> _ourExtensions = new[] { ".greenshot" };
|
||||
|
@ -44,6 +42,11 @@ namespace Greenshot.Editor.FileFormatHandlers
|
|||
SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the surface to the specified stream in the current .greenshot file format.
|
||||
/// </summary>
|
||||
/// <remarks>Ignores the given bitmap, as the .greenshot file always uses the original surface image.</remarks>
|
||||
/// <returns><see langword="true"/> if the surface was successfully saved to the stream; otherwise, <see langword="false"/>.</returns>
|
||||
public override bool TrySaveToStream(Bitmap bitmap, Stream stream, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null)
|
||||
{
|
||||
if (surface == null)
|
||||
|
@ -53,16 +56,8 @@ namespace Greenshot.Editor.FileFormatHandlers
|
|||
|
||||
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;
|
||||
//ignore the given bitmap, in .greenshot file we always use the original surface image
|
||||
return GreenshotFileVersionHandler.SaveToStreamInCurrentVersion(surface, stream);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -72,11 +67,18 @@ namespace Greenshot.Editor.FileFormatHandlers
|
|||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc />
|
||||
/// </summary>
|
||||
/// <remarks>This implementation loads the <see cref="GreenshotFile"/> from stream. Use this to creates a <see cref="ISurface"/> and uses <see cref="Surface.GetImageForExport"/> wich renders all contained elements into the image.</remarks>
|
||||
/// <returns><see langword="true"/> if the bitmap was successfully loaded from the stream; otherwise, <see
|
||||
/// langword="false"/>.</returns>
|
||||
public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap)
|
||||
{
|
||||
try
|
||||
{
|
||||
var surface = LoadSurface(stream);
|
||||
var surface = GreenshotFileVersionHandler.CreateSurfaceFromStream(stream);
|
||||
|
||||
bitmap = (Bitmap)surface.GetImageForExport();
|
||||
return true;
|
||||
}
|
||||
|
@ -88,46 +90,28 @@ namespace Greenshot.Editor.FileFormatHandlers
|
|||
return false;
|
||||
}
|
||||
|
||||
private ISurface LoadSurface(Stream surfaceFileStream)
|
||||
/// <summary>
|
||||
/// Load a <see cref="ISurface"/> from file path
|
||||
/// </summary>
|
||||
/// <remarks>This implementation loads the <see cref="GreenshotFile"/> from file. Use this to creates a <see cref="ISurface"/>.</remarks>
|
||||
/// <param name="fullPath"></param>
|
||||
/// <returns></returns>
|
||||
public ISurface LoadGreenshotSurface(string fullPath)
|
||||
{
|
||||
var returnSurface = SimpleServiceProvider.Current.GetInstance<Func<ISurface>>().Invoke();
|
||||
Bitmap captureBitmap;
|
||||
|
||||
// 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))
|
||||
if (string.IsNullOrEmpty(fullPath))
|
||||
{
|
||||
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;
|
||||
Log.Warn("No file path provided for loading Greenshot surface.");
|
||||
return null;
|
||||
}
|
||||
Log.InfoFormat("Loading surface data from file {0}", fullPath);
|
||||
|
||||
// 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!");
|
||||
}
|
||||
using Stream greenshotFileStream = File.OpenRead(fullPath);
|
||||
ISurface returnSurface = GreenshotFileVersionHandler.CreateSurfaceFromStream(greenshotFileStream);
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the .gst (Greenshot Template) file format, similar to GreenshotFileFormatHandler but for templates.
|
||||
/// </summary>
|
||||
public sealed class GreenshotTemplateFormatHandler : AbstractFileFormatHandler, IFileFormatHandler
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(GreenshotTemplateFormatHandler));
|
||||
private readonly IReadOnlyCollection<string> _ourExtensions = [".gst"];
|
||||
public GreenshotTemplateFormatHandler()
|
||||
{
|
||||
SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions;
|
||||
SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = [];
|
||||
SupportedExtensions[FileFormatHandlerActions.SaveToStream] = _ourExtensions;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the surface data to a file in the Greenshot template format (.gst).
|
||||
/// </summary>
|
||||
/// <param name="fullPath">The full file path where the template will be saved. Cannot be null or empty.</param>
|
||||
/// <param name="surface">The surface to save as a template. Cannot be null.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="fullPath"/> is null or empty, or if <paramref name="surface"/> is null.</exception>
|
||||
/// <exception cref="Exception">Thrown if the template fails to save to the specified file.</exception>
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>Currently not supported</remarks>
|
||||
public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap)
|
||||
{
|
||||
throw new NotImplementedException("Greenshot template (.gst) does not support save as image.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a Greenshot template from the specified file and applies its elements to the surface.
|
||||
/// </summary>
|
||||
/// <param name="fullPath">The full path to the file containing the Greenshot template. Cannot be null or empty.</param>
|
||||
/// <param name="surface">The surface to which the template elements will be applied. Cannot be null.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="fullPath"/> is null or empty, or if <paramref name="surface"/> is null.</exception>
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
|||
/// </summary>
|
||||
public class MetaFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(MetaFileFormatHandler));
|
||||
private readonly IReadOnlyCollection<string> _ourExtensions = new[] { ".wmf", ".emf" };
|
||||
|
||||
public MetaFileFormatHandler()
|
||||
|
@ -72,9 +75,18 @@ namespace Greenshot.Editor.FileFormatHandlers
|
|||
/// <inheritdoc />
|
||||
public override IEnumerable<IDrawableContainer> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,35 +1515,49 @@ namespace Greenshot.Editor.Forms
|
|||
|
||||
private void SaveElementsToolStripMenuItemClick(object sender, EventArgs e)
|
||||
{
|
||||
var greenshotTemplateFormatHandler = SimpleServiceProvider.Current.GetAllInstances<IFileFormatHandler>().OfType<GreenshotTemplateFormatHandler>().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<IFileFormatHandler>().OfType<GreenshotTemplateFormatHandler>().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);
|
||||
return;
|
||||
}
|
||||
|
||||
greenshotTemplateFormatHandler.LoadTemplateFromFile(openFileDialog.FileName, _surface);
|
||||
_surface.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
private void DestinationToolStripMenuItemClick(object sender, EventArgs e)
|
||||
{
|
||||
|
|
|
@ -88,4 +88,8 @@
|
|||
<DependentUpon>ImageEditorForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MessagePack" Version="3.1.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// This helps to map the serialization of the old .greenshot file to the newer.
|
||||
/// It also prevents misuse.
|
||||
/// </summary>
|
||||
internal class BinaryFormatterHelper : SerializationBinder
|
||||
{
|
||||
private static readonly ILog LOG = LogManager.GetLogger(typeof(BinaryFormatterHelper));
|
||||
private static readonly IDictionary<string, Type> TypeMapper = new Dictionary<string, Type>
|
||||
{
|
||||
{"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<IFieldHolder>)},
|
||||
{"System.Collections.Generic.List`1[[Greenshot.Base.Interfaces.Drawing.IField", typeof(List<IField>)},
|
||||
{"System.Collections.Generic.List`1[[System.Drawing.Point", typeof(List<System.Drawing.Point>)},
|
||||
{"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) },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Do the type mapping
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">Assembly for the type that was serialized</param>
|
||||
/// <param name="typeName">Type that was serialized</param>
|
||||
/// <returns>Type which was mapped</returns>
|
||||
/// <exception cref="SecurityAccessDeniedException">If something smells fishy</exception>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<Func<ISurface>>().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<LineContainerDto>(dto.ContainerList.ContainerList[0]);
|
||||
Assert.IsType<RectangleContainerDto>(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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trivial test to ensure that null ApplicationFile returns null DTO.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ToDto_NullApplicationFile_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
GreenshotFile domain = null;
|
||||
|
||||
// Act
|
||||
// ReSharper disable once ExpressionIsAlwaysNull
|
||||
var result = ConvertDomainToDto.ToDto(domain);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<Func<ISurface>>().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<bool>(resultShadow);
|
||||
Assert.Equal(defaultShadow, (bool)resultShadow);
|
||||
|
||||
Assert.NotNull(resultLineThickness);
|
||||
Assert.IsType<int>(resultLineThickness);
|
||||
Assert.Equal(defaultLineThickness, resultLineThickness);
|
||||
|
||||
Assert.NotNull(resultLineColor);
|
||||
Assert.IsType<Color>(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<Color>(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<ArrowHeadCombination>(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<Func<ISurface>>().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<int>(resultLineThickness);
|
||||
Assert.Equal(3, resultLineThickness);
|
||||
|
||||
Assert.NotNull(resultLineColor);
|
||||
Assert.IsType<Color>(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<Color>(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<bool>(resultShadow);
|
||||
Assert.False((bool)resultShadow);
|
||||
|
||||
Assert.NotNull(resultArrowHeads);
|
||||
Assert.IsType<ArrowHeadCombination>(resultArrowHeads);
|
||||
Assert.Equal(ArrowHeadCombination.BOTH, resultArrowHeads);
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<Func<ISurface>>().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);
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<Func<ISurface>>().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<LineContainerDto>(dto.ContainerList[0]);
|
||||
Assert.IsType<RectangleContainerDto>(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);
|
||||
}
|
||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<Func<ISurface>>().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<bool>(resultShadow);
|
||||
Assert.Equal(defaultShadow,(bool)resultShadow);
|
||||
|
||||
Assert.NotNull(resultLineThickness);
|
||||
Assert.IsType<int>(resultLineThickness);
|
||||
Assert.Equal(defaultLineThickness, resultLineThickness);
|
||||
|
||||
Assert.NotNull(resultLineColor);
|
||||
Assert.IsType<Color>(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<Color>(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<Func<ISurface>>().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<int>(resultLineThickness);
|
||||
Assert.Equal(3,resultLineThickness);
|
||||
|
||||
Assert.NotNull(resultLineColor);
|
||||
Assert.IsType<Color>(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<Color>(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<bool>(resultShadow);
|
||||
Assert.False((bool)resultShadow);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue