diff --git a/installer/innosetup/setup.iss b/installer/innosetup/setup.iss index 31a0035f2..482c26d85 100644 --- a/installer/innosetup/setup.iss +++ b/installer/innosetup/setup.iss @@ -190,6 +190,8 @@ Root: HKCU; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: " Root: HKCU; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser Root: HKCU; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE,0"""; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser Root: HKCU; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser +; Disable the default PRTSCR Snipping Tool in Windows 11 +Root: HKCU; Subkey: Control Panel\Keyboard; ValueType: dword; ValueName: "PrintScreenKeyForSnippingEnabled"; ValueData: "0"; Flags: uninsdeletevalue; Check: ShouldDisableSnippingTool ; HKEY_LOCAL_MACHINE - for all users when admin Root: HKLM; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser Root: HKLM; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser @@ -278,6 +280,7 @@ en.win10=Windows 10 plug-in en.UninstallIconDescription=Uninstall en.ShowLicense=Show license en.ShowReadme=Show Readme +en.disablewin11snippingtool=Disable Win11 default PrtScr snipping tool de.confluence=Confluence Plug-in de.default=Standard installation @@ -290,6 +293,7 @@ de.optimize=Optimierung der Leistung, kann etwas dauern. de.startgreenshot={#ExeName} starten de.startup={#ExeName} starten wenn Windows hochfährt de.win10=Windows 10 Plug-in +de.disablewin11snippingtool=Deaktiviere das Standard Windows 11 Snipping Tool auf "Druck" es.confluence=Extensión para Confluence es.default=${default} @@ -491,6 +495,7 @@ Name: "compact"; Description: "{code:CompactInstall}" Name: "custom"; Description: "{code:CustomInstall}"; Flags: iscustom [Components] +Name: "disablesnippingtool"; Description: {cm:disablewin11snippingtool}; Flags: disablenouninstallwarning; Types: default full custom; Check: IsWindows11OrNewer() Name: "greenshot"; Description: "Greenshot"; Types: default full compact custom; Flags: fixed ;Name: "plugins\networkimport"; Description: "Network Import Plugin"; Types: full Name: "plugins\box"; Description: {cm:box}; Types: full custom; Flags: disablenouninstallwarning @@ -540,6 +545,7 @@ Name: "languages\ukUA"; Description: {cm:ukUA}; Types: full custom; Flags: disab Name: "languages\viVN"; Description: {cm:viVN}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('e') Name: "languages\zhCN"; Description: {cm:zhCN}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('a') Name: "languages\zhTW"; Description: {cm:zhTW}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('9') + [Code] // Do we have a regular user trying to install this? function IsRegularUser(): Boolean; @@ -754,6 +760,19 @@ begin Result := IsWindowsVersionOrNewer(10, 0); end; +function IsWindows11OrNewer: Boolean; +var + WindowsVersion: TWindowsVersion; +begin + GetWindowsVersionEx(WindowsVersion); + Result := (WindowsVersion.Major >= 10) and (WindowsVersion.Build >= 22000); +end; + +function ShouldDisableSnippingTool: Boolean; +begin + Result := IsComponentSelected('disablesnippingtool'); +end; + [Run] Filename: "{app}\{#ExeName}.exe"; Description: "{cm:startgreenshot}"; Parameters: "{code:GetParamsForGS}"; WorkingDir: "{app}"; Flags: nowait postinstall runasoriginaluser Filename: "https://getgreenshot.org/thank-you/?language={language}&version={#Version}"; Flags: shellexec runasoriginaluser diff --git a/src/Greenshot.Editor/Drawing/FreehandContainer.cs b/src/Greenshot.Editor/Drawing/FreehandContainer.cs index b8a66be4e..9c66e1a8e 100644 --- a/src/Greenshot.Editor/Drawing/FreehandContainer.cs +++ b/src/Greenshot.Editor/Drawing/FreehandContainer.cs @@ -1,323 +1,325 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Runtime.Serialization; -using Dapplo.Windows.Common.Structs; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Editor.Drawing.Fields; -using Greenshot.Editor.Helpers; - -namespace Greenshot.Editor.Drawing -{ - /// - /// Description of PathContainer. - /// - [Serializable] - public class FreehandContainer : DrawableContainer - { - private static readonly float[] PointOffset = - { - 0.5f, 0.25f, 0.75f - }; - - [NonSerialized] private GraphicsPath freehandPath = new GraphicsPath(); - private NativeRect myBounds = NativeRect.Empty; - private NativePoint lastMouse = NativePoint.Empty; - private readonly List capturePoints = new List(); - private bool isRecalculated; - - /// - /// Constructor - /// - public FreehandContainer(ISurface parent) : base(parent) - { - Width = parent.Image.Width; - Height = parent.Image.Height; - Top = 0; - Left = 0; - } - - protected override void InitializeFields() - { - AddField(GetType(), FieldType.LINE_THICKNESS, 3); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - } - - public override void Transform(Matrix matrix) - { - Point[] points = capturePoints.ToArray(); - - matrix.TransformPoints(points); - capturePoints.Clear(); - capturePoints.AddRange(points); - RecalculatePath(); - } - - protected override void OnDeserialized(StreamingContext context) - { - RecalculatePath(); - } - - /// - /// This Dispose is called from the Dispose and the Destructor. - /// - /// When disposing==true all non-managed resources should be freed too! - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - freehandPath?.Dispose(); - } - - freehandPath = null; - } - - /// - /// Called from Surface (the parent) when the drawing begins (mouse-down) - /// - /// true if the surface doesn't need to handle the event - public override bool HandleMouseDown(int mouseX, int mouseY) - { - lastMouse = new Point(mouseX, mouseY); - capturePoints.Add(lastMouse); - return true; - } - - /// - /// Called from Surface (the parent) if a mouse move is made while drawing - /// - /// true if the surface doesn't need to handle the event - public override bool HandleMouseMove(int mouseX, int mouseY) - { - Point previousPoint = capturePoints[capturePoints.Count - 1]; - - if (GeometryHelper.Distance2D(previousPoint.X, previousPoint.Y, mouseX, mouseY) >= 2 * EditorConfig.FreehandSensitivity) - { - capturePoints.Add(new Point(mouseX, mouseY)); - } - - if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) < EditorConfig.FreehandSensitivity) - { - return true; - } - - //path.AddCurve(new Point[]{lastMouse, new Point(mouseX, mouseY)}); - lastMouse = new Point(mouseX, mouseY); - freehandPath.AddLine(lastMouse, new Point(mouseX, mouseY)); - // Only re-calculate the bounds & redraw when we added something to the path - myBounds = Rectangle.Round(freehandPath.GetBounds()); - - Invalidate(); - return true; - } - - /// - /// Called when the surface finishes drawing the element - /// - public override void HandleMouseUp(int mouseX, int mouseY) - { - // Make sure we don't loose the ending point - if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) >= EditorConfig.FreehandSensitivity) - { - capturePoints.Add(new Point(mouseX, mouseY)); - } - - RecalculatePath(); - } - - /// - /// Here we recalculate the freehand path by smoothing out the lines with Beziers. - /// - private void RecalculatePath() - { - // Store the previous path, to dispose it later when we are finished - var previousFreehandPath = freehandPath; - var newFreehandPath = new GraphicsPath(); - - // Here we can put some cleanup... like losing all the uninteresting points. - if (capturePoints.Count >= 3) - { - int index = 0; - while ((capturePoints.Count - 1) % 3 != 0) - { - // duplicate points, first at 50% than 25% than 75% - capturePoints.Insert((int) (capturePoints.Count * PointOffset[index]), capturePoints[(int) (capturePoints.Count * PointOffset[index++])]); - } - - newFreehandPath.AddBeziers(capturePoints.ToArray()); - } - else if (capturePoints.Count == 2) - { - newFreehandPath.AddLine(capturePoints[0], capturePoints[1]); - } - - // Recalculate the bounds - myBounds = Rectangle.Round(newFreehandPath.GetBounds()); - - // assign - isRecalculated = true; - freehandPath = newFreehandPath; - - // dispose previous - previousFreehandPath?.Dispose(); - } - - /// - /// Do the drawing of the freehand "stroke" - /// - /// - /// - public override void Draw(Graphics graphics, RenderMode renderMode) - { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - using var pen = new Pen(lineColor) - { - Width = lineThickness - }; - if (!(pen.Width > 0)) - { - return; - } - - // Make sure the lines are nicely rounded - pen.EndCap = LineCap.Round; - pen.StartCap = LineCap.Round; - pen.LineJoin = LineJoin.Round; - // Move to where we need to draw - graphics.TranslateTransform(Left, Top); - var currentFreehandPath = freehandPath; - if (currentFreehandPath != null) - { - if (isRecalculated && Selected && renderMode == RenderMode.EDIT) - { - isRecalculated = false; - DrawSelectionBorder(graphics, pen, currentFreehandPath); - } - - graphics.DrawPath(pen, currentFreehandPath); - } - - // Move back, otherwise everything is shifted - graphics.TranslateTransform(-Left, -Top); - } - - /// - /// Draw a selectionborder around the freehand path - /// - /// Graphics - /// Pen - /// GraphicsPath - protected static void DrawSelectionBorder(Graphics graphics, Pen linePen, GraphicsPath path) - { - using var selectionPen = (Pen) linePen.Clone(); - using var selectionPath = (GraphicsPath) path.Clone(); - selectionPen.Width += 5; - selectionPen.Color = Color.FromArgb(120, Color.LightSeaGreen); - graphics.DrawPath(selectionPen, selectionPath); - selectionPath.Widen(selectionPen); - selectionPen.DashPattern = new float[] - { - 2, 2 - }; - selectionPen.Color = Color.LightSeaGreen; - selectionPen.Width = 1; - graphics.DrawPath(selectionPen, selectionPath); - } - - /// - /// Get the bounds in which we have something drawn, plus safety margin, these are not the normal bounds... - /// - public override NativeRect DrawingBounds - { - get - { - if (!myBounds.IsEmpty) - { - int lineThickness = Math.Max(10, GetFieldValueAsInt(FieldType.LINE_THICKNESS)); - int safetyMargin = 10; - return new NativeRect(myBounds.Left + Left - (safetyMargin + lineThickness), myBounds.Top + Top - (safetyMargin + lineThickness), - myBounds.Width + 2 * (lineThickness + safetyMargin), myBounds.Height + 2 * (lineThickness + safetyMargin)); - } - - if (_parent?.Image is Image image) - { - return new NativeRect(0, 0, image.Width, image.Height); - } - - return NativeRect.Empty; - } - } - - /// - /// FreehandContainer are regarded equal if they are of the same type and their paths are equal. - /// - /// object - /// bool - public override bool Equals(object obj) - { - bool ret = false; - if (obj == null || GetType() != obj.GetType()) - { - return false; - } - - if (obj is FreehandContainer other && Equals(freehandPath, other.freehandPath)) - { - ret = true; - } - - return ret; - } - - public override int GetHashCode() - { - return freehandPath?.GetHashCode() ?? 0; - } - - public override bool ClickableAt(int x, int y) - { - bool returnValue = base.ClickableAt(x, y); - if (returnValue) - { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - using var pen = new Pen(Color.White) - { - Width = lineThickness + 10 - }; - returnValue = freehandPath.IsOutlineVisible(x - Left, y - Top, pen); - } - - return returnValue; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Runtime.Serialization; +using Dapplo.Windows.Common.Structs; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of PathContainer. + /// + [Serializable] + public class FreehandContainer : DrawableContainer + { + private static readonly float[] PointOffset = + { + 0.5f, 0.25f, 0.75f + }; + + [NonSerialized] + private GraphicsPath freehandPath = new GraphicsPath(); + + private Rectangle myBounds = NativeRect.Empty; + private Point lastMouse = NativePoint.Empty; + private List capturePoints = new List(); + private bool isRecalculated; + + /// + /// Constructor + /// + public FreehandContainer(ISurface parent) : base(parent) + { + Width = parent.Image.Width; + Height = parent.Image.Height; + Top = 0; + Left = 0; + } + + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 3); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + } + + public override void Transform(Matrix matrix) + { + Point[] points = capturePoints.ToArray(); + + matrix.TransformPoints(points); + capturePoints.Clear(); + capturePoints.AddRange(points); + RecalculatePath(); + } + + protected override void OnDeserialized(StreamingContext context) + { + RecalculatePath(); + } + + /// + /// This Dispose is called from the Dispose and the Destructor. + /// + /// When disposing==true all non-managed resources should be freed too! + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + freehandPath?.Dispose(); + } + + freehandPath = null; + } + + /// + /// Called from Surface (the parent) when the drawing begins (mouse-down) + /// + /// true if the surface doesn't need to handle the event + public override bool HandleMouseDown(int mouseX, int mouseY) + { + lastMouse = new Point(mouseX, mouseY); + capturePoints.Add(lastMouse); + return true; + } + + /// + /// Called from Surface (the parent) if a mouse move is made while drawing + /// + /// true if the surface doesn't need to handle the event + public override bool HandleMouseMove(int mouseX, int mouseY) + { + Point previousPoint = capturePoints[capturePoints.Count - 1]; + + if (GeometryHelper.Distance2D(previousPoint.X, previousPoint.Y, mouseX, mouseY) >= 2 * EditorConfig.FreehandSensitivity) + { + capturePoints.Add(new Point(mouseX, mouseY)); + } + + if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) < EditorConfig.FreehandSensitivity) + { + return true; + } + + //path.AddCurve(new Point[]{lastMouse, new Point(mouseX, mouseY)}); + lastMouse = new Point(mouseX, mouseY); + freehandPath.AddLine(lastMouse, new Point(mouseX, mouseY)); + // Only re-calculate the bounds & redraw when we added something to the path + myBounds = Rectangle.Round(freehandPath.GetBounds()); + + Invalidate(); + return true; + } + + /// + /// Called when the surface finishes drawing the element + /// + public override void HandleMouseUp(int mouseX, int mouseY) + { + // Make sure we don't loose the ending point + if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) >= EditorConfig.FreehandSensitivity) + { + capturePoints.Add(new Point(mouseX, mouseY)); + } + + RecalculatePath(); + } + + /// + /// Here we recalculate the freehand path by smoothing out the lines with Beziers. + /// + private void RecalculatePath() + { + // Store the previous path, to dispose it later when we are finished + var previousFreehandPath = freehandPath; + var newFreehandPath = new GraphicsPath(); + + // Here we can put some cleanup... like losing all the uninteresting points. + if (capturePoints.Count >= 3) + { + int index = 0; + while ((capturePoints.Count - 1) % 3 != 0) + { + // duplicate points, first at 50% than 25% than 75% + capturePoints.Insert((int) (capturePoints.Count * PointOffset[index]), capturePoints[(int) (capturePoints.Count * PointOffset[index++])]); + } + + newFreehandPath.AddBeziers(capturePoints.ToArray()); + } + else if (capturePoints.Count == 2) + { + newFreehandPath.AddLine(capturePoints[0], capturePoints[1]); + } + + // Recalculate the bounds + myBounds = Rectangle.Round(newFreehandPath.GetBounds()); + + // assign + isRecalculated = true; + freehandPath = newFreehandPath; + + // dispose previous + previousFreehandPath?.Dispose(); + } + + /// + /// Do the drawing of the freehand "stroke" + /// + /// + /// + public override void Draw(Graphics graphics, RenderMode renderMode) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + using var pen = new Pen(lineColor) + { + Width = lineThickness + }; + if (!(pen.Width > 0)) + { + return; + } + + // Make sure the lines are nicely rounded + pen.EndCap = LineCap.Round; + pen.StartCap = LineCap.Round; + pen.LineJoin = LineJoin.Round; + // Move to where we need to draw + graphics.TranslateTransform(Left, Top); + var currentFreehandPath = freehandPath; + if (currentFreehandPath != null) + { + if (isRecalculated && Selected && renderMode == RenderMode.EDIT) + { + isRecalculated = false; + DrawSelectionBorder(graphics, pen, currentFreehandPath); + } + + graphics.DrawPath(pen, currentFreehandPath); + } + + // Move back, otherwise everything is shifted + graphics.TranslateTransform(-Left, -Top); + } + + /// + /// Draw a selectionborder around the freehand path + /// + /// Graphics + /// Pen + /// GraphicsPath + protected static void DrawSelectionBorder(Graphics graphics, Pen linePen, GraphicsPath path) + { + using var selectionPen = (Pen) linePen.Clone(); + using var selectionPath = (GraphicsPath) path.Clone(); + selectionPen.Width += 5; + selectionPen.Color = Color.FromArgb(120, Color.LightSeaGreen); + graphics.DrawPath(selectionPen, selectionPath); + selectionPath.Widen(selectionPen); + selectionPen.DashPattern = new float[] + { + 2, 2 + }; + selectionPen.Color = Color.LightSeaGreen; + selectionPen.Width = 1; + graphics.DrawPath(selectionPen, selectionPath); + } + + /// + /// Get the bounds in which we have something drawn, plus safety margin, these are not the normal bounds... + /// + public override NativeRect DrawingBounds + { + get + { + if (!myBounds.IsEmpty) + { + int lineThickness = Math.Max(10, GetFieldValueAsInt(FieldType.LINE_THICKNESS)); + int safetyMargin = 10; + return new NativeRect(myBounds.Left + Left - (safetyMargin + lineThickness), myBounds.Top + Top - (safetyMargin + lineThickness), + myBounds.Width + 2 * (lineThickness + safetyMargin), myBounds.Height + 2 * (lineThickness + safetyMargin)); + } + + if (_parent?.Image is Image image) + { + return new NativeRect(0, 0, image.Width, image.Height); + } + + return NativeRect.Empty; + } + } + + /// + /// FreehandContainer are regarded equal if they are of the same type and their paths are equal. + /// + /// object + /// bool + public override bool Equals(object obj) + { + bool ret = false; + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + if (obj is FreehandContainer other && Equals(freehandPath, other.freehandPath)) + { + ret = true; + } + + return ret; + } + + public override int GetHashCode() + { + return freehandPath?.GetHashCode() ?? 0; + } + + public override bool ClickableAt(int x, int y) + { + bool returnValue = base.ClickableAt(x, y); + if (returnValue) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + using var pen = new Pen(Color.White) + { + Width = lineThickness + 10 + }; + returnValue = freehandPath.IsOutlineVisible(x - Left, y - Top, pen); + } + + return returnValue; + } + } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs b/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs index 22dd13379..e1f37544b 100644 --- a/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs +++ b/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs @@ -39,10 +39,10 @@ namespace Greenshot.Editor.Drawing [Serializable] public class SpeechbubbleContainer : TextContainer { - private NativePoint _initialGripperPoint; + private Point _initialGripperPoint; // Only used for serializing the TargetGripper location - private NativePoint _storedTargetGripperLocation; + private Point _storedTargetGripperLocation; /// /// Store the current location of the target gripper @@ -120,7 +120,8 @@ namespace Greenshot.Editor.Drawing int xOffset = leftAligned ? -20 : 20; int yOffset = topAligned ? -20 : 20; - NativePoint newGripperLocation = _initialGripperPoint.Offset(xOffset, yOffset); + NativePoint initialGripperPoint = _initialGripperPoint; + NativePoint newGripperLocation = initialGripperPoint.Offset(xOffset, yOffset); if (TargetAdorner.Location != newGripperLocation) { diff --git a/src/Greenshot.Editor/Drawing/Surface.cs b/src/Greenshot.Editor/Drawing/Surface.cs index ccf979661..32fdb938f 100644 --- a/src/Greenshot.Editor/Drawing/Surface.cs +++ b/src/Greenshot.Editor/Drawing/Surface.cs @@ -28,6 +28,7 @@ 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,6 +42,7 @@ using Greenshot.Base.Interfaces.Drawing.Adorners; using Greenshot.Editor.Configuration; using Greenshot.Editor.Drawing.Emoji; using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; using Greenshot.Editor.Memento; using log4net; @@ -723,6 +725,7 @@ namespace Greenshot.Editor.Drawing try { BinaryFormatter binaryRead = new BinaryFormatter(); + binaryRead.Binder = new BinaryFormatterHelper(); IDrawableContainerList loadedElements = (IDrawableContainerList) binaryRead.Deserialize(streamRead); loadedElements.Parent = this; // Make sure the steplabels are sorted according to their number @@ -732,6 +735,10 @@ namespace Greenshot.Editor.Drawing SelectElements(loadedElements); FieldAggregator.BindElements(loadedElements); } + catch (SecurityAccessDeniedException) + { + throw; + } catch (Exception e) { LOG.Error("Error serializing elements from stream.", e); diff --git a/src/Greenshot.Editor/Drawing/SvgContainer.cs b/src/Greenshot.Editor/Drawing/SvgContainer.cs index 283f755e8..85b8cb43d 100644 --- a/src/Greenshot.Editor/Drawing/SvgContainer.cs +++ b/src/Greenshot.Editor/Drawing/SvgContainer.cs @@ -21,6 +21,7 @@ using System; using System.Drawing; +using System.IO; using Dapplo.Windows.Common.Structs; using Greenshot.Base.Core; using Greenshot.Base.Interfaces; @@ -34,14 +35,32 @@ namespace Greenshot.Editor.Drawing [Serializable] public class SvgContainer : VectorGraphicsContainer { - private readonly SvgDocument _svgDocument; + private MemoryStream _svgContent; - public SvgContainer(SvgDocument svgDocument, ISurface parent) : base(parent) + [NonSerialized] + private SvgDocument _svgDocument; + + public SvgContainer(Stream stream, ISurface parent) : base(parent) { - _svgDocument = svgDocument; - Size = new Size((int)svgDocument.Width, (int)svgDocument.Height); + _svgContent = new MemoryStream(); + stream.CopyTo(_svgContent); + Init(); + Size = new Size((int)_svgDocument.Width, (int)_svgDocument.Height); } - + + protected override void Init() + { + base.Init(); + // Do nothing when there is no content + if (_svgContent == null) + { + return; + } + _svgContent.Position = 0; + + _svgDocument = SvgDocument.Open(_svgContent); + } + protected override Image ComputeBitmap() { //var image = ImageHelper.CreateEmpty(Width, Height, PixelFormat.Format32bppArgb, Color.Transparent); diff --git a/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs b/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs index 45238caaf..43da94c7d 100644 --- a/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs +++ b/src/Greenshot.Editor/Drawing/VectorGraphicsContainer.cs @@ -47,7 +47,8 @@ namespace Greenshot.Editor.Drawing /// This is the cached version of the bitmap, pre-rendered to save performance /// Do not serialized, it can be rebuild with other information. /// - [NonSerialized] private Image _cachedImage; + [NonSerialized] + private Image _cachedImage; /// /// Constructor takes care of calling Init diff --git a/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs b/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs index 14a33246e..76c5e85b9 100644 --- a/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs +++ b/src/Greenshot.Editor/FileFormatHandlers/SvgFileFormatHandler.cs @@ -1,89 +1,89 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Base.Interfaces.Plugin; -using Greenshot.Editor.Drawing; -using log4net; -using Svg; - -namespace Greenshot.Editor.FileFormatHandlers -{ - /// - /// This handled the loading of SVG images to the editor - /// - public class SvgFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler - { - private static readonly ILog Log = LogManager.GetLogger(typeof(SvgFileFormatHandler)); - private readonly IReadOnlyCollection _ourExtensions = new[] { ".svg" }; - - public SvgFileFormatHandler() - { - SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; - SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; - } - - public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) - { - var svgDocument = SvgDocument.Open(stream); - - try - { - bitmap = svgDocument.Draw(); - return true; - } - catch (Exception ex) - { - Log.Error("Can't load SVG", ex); - } - bitmap = null; - return false; - } - - public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) - { - // TODO: Implement this - return false; - } - - public override IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface parent = null) - { - SvgDocument svgDocument = null; - try - { - svgDocument = SvgDocument.Open(stream); - } - catch (Exception ex) - { - Log.Error("Can't load SVG", ex); - } - if (svgDocument != null) - { - yield return new SvgContainer(svgDocument, parent); - } - } - } -} +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Base.Interfaces.Plugin; +using Greenshot.Editor.Drawing; +using log4net; +using Svg; + +namespace Greenshot.Editor.FileFormatHandlers +{ + /// + /// This handled the loading of SVG images to the editor + /// + public class SvgFileFormatHandler : AbstractFileFormatHandler, IFileFormatHandler + { + private static readonly ILog Log = LogManager.GetLogger(typeof(SvgFileFormatHandler)); + private readonly IReadOnlyCollection _ourExtensions = new[] { ".svg" }; + + public SvgFileFormatHandler() + { + SupportedExtensions[FileFormatHandlerActions.LoadDrawableFromStream] = _ourExtensions; + SupportedExtensions[FileFormatHandlerActions.LoadFromStream] = _ourExtensions; + } + + public override bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) + { + var svgDocument = SvgDocument.Open(stream); + + try + { + bitmap = svgDocument.Draw(); + return true; + } + catch (Exception ex) + { + Log.Error("Can't load SVG", ex); + } + bitmap = null; + return false; + } + + public override bool TrySaveToStream(Bitmap bitmap, Stream destination, string extension, ISurface surface = null, SurfaceOutputSettings surfaceOutputSettings = null) + { + // TODO: Implement this + return false; + } + + public override IEnumerable LoadDrawablesFromStream(Stream stream, string extension, ISurface parent = null) + { + SvgContainer svgContainer = null; + try + { + svgContainer = new SvgContainer(stream, parent); + } + catch (Exception ex) + { + Log.Error("Can't load SVG", ex); + } + if (svgContainer != null) + { + yield return svgContainer; + } + } + } +} diff --git a/src/Greenshot.Editor/Helpers/BinaryFormatterHelper.cs b/src/Greenshot.Editor/Helpers/BinaryFormatterHelper.cs new file mode 100644 index 000000000..279e8b1a5 --- /dev/null +++ b/src/Greenshot.Editor/Helpers/BinaryFormatterHelper.cs @@ -0,0 +1,123 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.ServiceModel.Security; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Drawing.Filters; +using log4net; +using static Greenshot.Editor.Drawing.FilterContainer; + +namespace Greenshot.Editor.Helpers +{ + /// + /// This helps to map the serialization of the old .greenshot file to the newer. + /// It also prevents misuse. + /// + internal class BinaryFormatterHelper : SerializationBinder + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(BinaryFormatterHelper)); + private static readonly IDictionary TypeMapper = new Dictionary + { + {"System.Guid",typeof(Guid) }, + {"System.Drawing.Rectangle",typeof(System.Drawing.Rectangle) }, + {"System.Drawing.Point",typeof(System.Drawing.Point) }, + {"System.Drawing.Color",typeof(System.Drawing.Color) }, + {"System.Drawing.Bitmap",typeof(System.Drawing.Bitmap) }, + {"System.Drawing.Icon",typeof(System.Drawing.Icon) }, + {"System.Drawing.Size",typeof(System.Drawing.Size) }, + {"System.IO.MemoryStream",typeof(System.IO.MemoryStream) }, + {"System.Drawing.StringAlignment",typeof(System.Drawing.StringAlignment) }, + {"System.Collections.Generic.List`1[[Greenshot.Base.Interfaces.Drawing.IFieldHolder", typeof(List)}, + {"System.Collections.Generic.List`1[[Greenshot.Base.Interfaces.Drawing.IField", typeof(List)}, + {"System.Collections.Generic.List`1[[System.Drawing.Point", typeof(List)}, + {"Greenshot.Editor.Drawing.ArrowContainer", typeof(ArrowContainer) }, + {"Greenshot.Editor.Drawing.LineContainer", typeof(LineContainer) }, + {"Greenshot.Editor.Drawing.TextContainer", typeof(TextContainer) }, + {"Greenshot.Editor.Drawing.SpeechbubbleContainer", typeof(SpeechbubbleContainer) }, + {"Greenshot.Editor.Drawing.RectangleContainer", typeof(RectangleContainer) }, + {"Greenshot.Editor.Drawing.EllipseContainer", typeof(EllipseContainer) }, + {"Greenshot.Editor.Drawing.FreehandContainer", typeof(FreehandContainer) }, + {"Greenshot.Editor.Drawing.HighlightContainer", typeof(HighlightContainer) }, + {"Greenshot.Editor.Drawing.IconContainer", typeof(IconContainer) }, + {"Greenshot.Editor.Drawing.ObfuscateContainer", typeof(ObfuscateContainer) }, + {"Greenshot.Editor.Drawing.StepLabelContainer", typeof(StepLabelContainer) }, + {"Greenshot.Editor.Drawing.SvgContainer", typeof(SvgContainer) }, + {"Greenshot.Editor.Drawing.VectorGraphicsContainer", typeof(VectorGraphicsContainer) }, + {"Greenshot.Editor.Drawing.MetafileContainer", typeof(MetafileContainer) }, + {"Greenshot.Editor.Drawing.ImageContainer", typeof(ImageContainer) }, + {"Greenshot.Editor.Drawing.FilterContainer", typeof(FilterContainer) }, + {"Greenshot.Editor.Drawing.DrawableContainer", typeof(DrawableContainer) }, + {"Greenshot.Editor.Drawing.DrawableContainerList", typeof(DrawableContainerList) }, + {"Greenshot.Editor.Drawing.CursorContainer", typeof(CursorContainer) }, + {"Greenshot.Editor.Drawing.Filters.HighlightFilter", typeof(HighlightFilter) }, + {"Greenshot.Editor.Drawing.Filters.GrayscaleFilter", typeof(GrayscaleFilter) }, + {"Greenshot.Editor.Drawing.Filters.MagnifierFilter", typeof(MagnifierFilter) }, + {"Greenshot.Editor.Drawing.Filters.BrightnessFilter", typeof(BrightnessFilter) }, + {"Greenshot.Editor.Drawing.Filters.BlurFilter", typeof(BlurFilter) }, + {"Greenshot.Editor.Drawing.Filters.PixelizationFilter", typeof(PixelizationFilter) }, + {"Greenshot.Base.Interfaces.Drawing.IDrawableContainer", typeof(IDrawableContainer) }, + {"Greenshot.Base.Interfaces.Drawing.EditStatus", typeof(EditStatus) }, + {"Greenshot.Base.Interfaces.Drawing.IFieldHolder", typeof(IFieldHolder) }, + {"Greenshot.Base.Interfaces.Drawing.IField", typeof(IField) }, + {"Greenshot.Base.Interfaces.Drawing.FieldFlag", typeof(FieldFlag) }, + {"Greenshot.Editor.Drawing.Fields.Field", typeof(Field) }, + {"Greenshot.Editor.Drawing.Fields.FieldType", typeof(FieldType) }, + {"Greenshot.Editor.Drawing.FilterContainer+PreparedFilter", typeof(PreparedFilter) }, + }; + + /// + /// Do the type mapping + /// + /// Assembly for the type that was serialized + /// Type that was serialized + /// Type which was mapped + /// If something smells fishy + public override Type BindToType(string assemblyName, string typeName) + { + if (string.IsNullOrEmpty(typeName)) + { + return null; + } + var typeNameCommaLocation = typeName.IndexOf(","); + var comparingTypeName = typeName.Substring(0, typeNameCommaLocation > 0 ? typeNameCommaLocation : typeName.Length); + + // Correct wrong types + comparingTypeName = comparingTypeName.Replace("Greenshot.Drawing", "Greenshot.Editor.Drawing"); + comparingTypeName = comparingTypeName.Replace("Greenshot.Plugin.Drawing", "Greenshot.Base.Interfaces.Drawing"); + comparingTypeName = comparingTypeName.Replace("GreenshotPlugin.Interfaces.Drawing", "Greenshot.Base.Interfaces.Drawing"); + comparingTypeName = comparingTypeName.Replace("Greenshot.Drawing.Fields", "Greenshot.Editor.Drawing.Fields"); + comparingTypeName = comparingTypeName.Replace("Greenshot.Drawing.Filters", "Greenshot.Editor.Drawing.Filters"); + + if (TypeMapper.TryGetValue(comparingTypeName, out var returnType)) + { + LOG.Info($"Mapped {assemblyName} - {typeName} to {returnType.FullName}"); + return returnType; + } + LOG.Warn($"Unexpected Greenshot type in .greenshot file detected, maybe vulnerability attack created with ysoserial? Suspicious type: {assemblyName} - {typeName}"); + throw new SecurityAccessDeniedException($"Suspicious type in .greenshot file: {assemblyName} - {typeName}"); + } + } +}