Fixing drag and drop of files, the container was not added to the surface. Also fixed opening things from the clipboard.

This commit is contained in:
Robin Krom 2022-02-09 23:37:38 +01:00
commit 2997671698
No known key found for this signature in database
GPG key ID: BCC01364F1371490
5 changed files with 295 additions and 40 deletions

View file

@ -494,7 +494,7 @@ EndSelection:<<<<<<<4
{ {
IDataObject clipboardData = GetDataObject(); IDataObject clipboardData = GetDataObject();
// Return the first image // Return the first image
foreach (Image clipboardImage in GetImages(clipboardData)) foreach (var clipboardImage in GetImages(clipboardData))
{ {
return clipboardImage; return clipboardImage;
} }
@ -507,11 +507,89 @@ EndSelection:<<<<<<<4
/// Returned images must be disposed by the calling code! /// Returned images must be disposed by the calling code!
/// </summary> /// </summary>
/// <param name="dataObject"></param> /// <param name="dataObject"></param>
/// <returns>IEnumerable of Image</returns> /// <returns>IEnumerable of Bitmap</returns>
public static IEnumerable<IDrawableContainer> GetImages(IDataObject dataObject) public static IEnumerable<Bitmap> GetImages(IDataObject dataObject)
{ {
// Get single image, this takes the "best" match // Get single image, this takes the "best" match
IDrawableContainer singleImage = GetImage(dataObject); Bitmap singleImage = GetImage(dataObject);
if (singleImage != null)
{
Log.InfoFormat($"Got {singleImage.GetType()} from clipboard with size {singleImage.Size}");
yield return singleImage;
yield break;
}
var supportedExtensions = FileFormatHandlerRegistry.ExtensionsFor(FileFormatHandlerActions.LoadDrawableFromStream).ToList();
foreach (var fileData in IterateClipboardContent(dataObject))
{
var extension = Path.GetExtension(fileData.filename)?.ToLowerInvariant();
if (!supportedExtensions.Contains(extension))
{
continue;
}
Bitmap bitmap = null;
try
{
if (!FileFormatHandlerRegistry.TryLoadFromStream(fileData.stream, extension, out bitmap))
{
continue;
}
}
catch (Exception ex)
{
Log.Error("Couldn't read file contents", ex);
continue;
}
finally
{
fileData.stream?.Dispose();
}
// If we get here, there is an image
yield return bitmap;
}
// check if files are supplied
foreach (string imageFile in GetImageFilenames(dataObject))
{
var extension = Path.GetExtension(imageFile)?.ToLowerInvariant();
if (!supportedExtensions.Contains(extension))
{
continue;
}
Bitmap bitmap = null;
using FileStream fileStream = new FileStream(imageFile, FileMode.Open, FileAccess.Read, FileShare.Read);
try
{
if (!FileFormatHandlerRegistry.TryLoadFromStream(fileStream, extension, out bitmap))
{
continue;
}
}
catch (Exception ex)
{
Log.Error("Couldn't read file contents", ex);
continue;
}
// If we get here, there is an image
yield return bitmap;
}
}
/// <summary>
/// Get all images (multiple if file names are available) from the dataObject
/// Returned images must be disposed by the calling code!
/// </summary>
/// <param name="dataObject"></param>
/// <returns>IEnumerable of IDrawableContainer</returns>
public static IEnumerable<IDrawableContainer> GetDrawables(IDataObject dataObject)
{
// Get single image, this takes the "best" match
IDrawableContainer singleImage = GetDrawable(dataObject);
if (singleImage != null) if (singleImage != null)
{ {
Log.InfoFormat($"Got {singleImage.GetType()} from clipboard with size {singleImage.Size}"); Log.InfoFormat($"Got {singleImage.GetType()} from clipboard with size {singleImage.Size}");
@ -585,11 +663,11 @@ EndSelection:<<<<<<<4
/// </summary> /// </summary>
/// <param name="dataObject"></param> /// <param name="dataObject"></param>
/// <returns>Image or null</returns> /// <returns>Image or null</returns>
private static IDrawableContainer GetImage(IDataObject dataObject) private static Bitmap GetImage(IDataObject dataObject)
{ {
if (dataObject == null) return null; if (dataObject == null) return null;
IDrawableContainer returnImage = null; Bitmap returnImage = null;
IList<string> formats = GetFormats(dataObject); IList<string> formats = GetFormats(dataObject);
string[] retrieveFormats; string[] retrieveFormats;
@ -635,6 +713,113 @@ EndSelection:<<<<<<<4
return null; return null;
} }
/// <summary>
/// Get an IDrawableContainer from the IDataObject, don't check for FileDrop
/// </summary>
/// <param name="dataObject"></param>
/// <returns>Image or null</returns>
private static IDrawableContainer GetDrawable(IDataObject dataObject)
{
if (dataObject == null) return null;
IDrawableContainer returnImage = null;
IList<string> formats = GetFormats(dataObject);
string[] retrieveFormats;
// Found a weird bug, where PNG's from Outlook 2010 are clipped
// So I build some special logic to get the best format:
if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib))
{
// Outlook ??
Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front...");
retrieveFormats = new[]
{
DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF,
DataFormats.Tiff, FORMAT_GIF, FORMAT_HTML
};
}
else
{
retrieveFormats = new[]
{
FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP,
FORMAT_FILECONTENTS, FORMAT_GIF, FORMAT_HTML
};
}
foreach (string currentFormat in retrieveFormats)
{
if (formats != null && formats.Contains(currentFormat))
{
Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat);
returnImage = GetDrawableForFormat(currentFormat, dataObject);
}
else
{
Log.DebugFormat("Couldn't find format {0}.", currentFormat);
}
if (returnImage != null)
{
return returnImage;
}
}
return null;
}
/// <summary>
/// Helper method to try to get an Bitmap in the specified format from the dataObject
/// the DIB reader should solve some issues
/// It also supports Format17/DibV5, by using the following information: https://stackoverflow.com/a/14335591
/// </summary>
/// <param name="format">string with the format</param>
/// <param name="dataObject">IDataObject</param>
/// <returns>Bitmap or null</returns>
private static Bitmap GetImageForFormat(string format, IDataObject dataObject)
{
Bitmap bitmap = null;
if (format == FORMAT_HTML)
{
var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8);
if (textObject != null)
{
var doc = new HtmlDocument();
doc.LoadHtml(textObject);
var imgNodes = doc.DocumentNode.SelectNodes("//img");
if (imgNodes != null)
{
foreach (var imgNode in imgNodes)
{
var srcAttribute = imgNode.Attributes["src"];
var imageUrl = srcAttribute.Value;
Log.Debug(imageUrl);
bitmap = NetworkHelper.DownloadImage(imageUrl);
if (bitmap != null)
{
return bitmap;
}
}
}
}
}
object clipboardObject = GetFromDataObject(dataObject, format);
var imageStream = clipboardObject as MemoryStream;
if (!IsValidStream(imageStream))
{
return clipboardObject as Bitmap;
}
// From here, imageStream is a valid stream
if (!FileFormatHandlerRegistry.TryLoadFromStream(imageStream, format, out bitmap))
{
return bitmap;
}
return null;
}
/// <summary> /// <summary>
/// Helper method to try to get an IDrawableContainer in the specified format from the dataObject /// Helper method to try to get an IDrawableContainer in the specified format from the dataObject
/// the DIB reader should solve some issues /// the DIB reader should solve some issues
@ -643,7 +828,7 @@ EndSelection:<<<<<<<4
/// <param name="format">string with the format</param> /// <param name="format">string with the format</param>
/// <param name="dataObject">IDataObject</param> /// <param name="dataObject">IDataObject</param>
/// <returns>IDrawableContainer or null</returns> /// <returns>IDrawableContainer or null</returns>
private static IDrawableContainer GetImageForFormat(string format, IDataObject dataObject) private static IDrawableContainer GetDrawableForFormat(string format, IDataObject dataObject)
{ {
IDrawableContainer drawableContainer = null; IDrawableContainer drawableContainer = null;

View file

@ -84,5 +84,28 @@ namespace Greenshot.Base.Core.FileFormatHandlers
return fileFormatHandler.TryLoadDrawableFromStream(stream, extension, out drawableContainer, parentSurface); return fileFormatHandler.TryLoadDrawableFromStream(stream, extension, out drawableContainer, parentSurface);
} }
/// <summary>
/// Try to load a Bitmap from the stream
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="extension">string</param>
/// <param name="bitmap">Bitmap out</param>
/// <returns>bool true if it was successful</returns>
public static bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap)
{
var fileFormatHandler = FileFormatHandlers
.Where(ffh => ffh.Supports(FileFormatHandlerActions.LoadFromStream, extension))
.OrderBy(ffh => ffh.PriorityFor(FileFormatHandlerActions.LoadFromStream, extension))
.FirstOrDefault();
if (fileFormatHandler == null)
{
bitmap = null;
return false;
}
return fileFormatHandler.TryLoadFromStream(stream, extension, out bitmap);
}
} }
} }

View file

@ -24,7 +24,6 @@ using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -145,6 +144,62 @@ namespace Greenshot.Base.Core
return null; return null;
} }
/// <summary>
/// Download the uri to create a Bitmap
/// </summary>
/// <param name="url">Of an image</param>
/// <returns>Bitmap</returns>
public static Bitmap DownloadImage(string url)
{
var extensions = string.Join("|", FileFormatHandlerRegistry.ExtensionsFor(FileFormatHandlerActions.LoadFromStream));
var imageUrlRegex = new Regex($@"(http|https)://.*(?<extension>{extensions})");
var match = imageUrlRegex.Match(url);
try
{
using var memoryStream = GetAsMemoryStream(url);
try
{
if (FileFormatHandlerRegistry.TryLoadFromStream(memoryStream, match.Success ? match.Groups["extension"]?.Value : null, out var bitmap))
{
return bitmap;
}
}
catch (Exception)
{
// If we arrive here, the image loading didn't work, try to see if the response has a http(s) URL to an image and just take this instead.
string content;
using (var streamReader = new StreamReader(memoryStream, Encoding.UTF8, true))
{
content = streamReader.ReadLine();
}
if (string.IsNullOrEmpty(content))
{
throw;
}
match = imageUrlRegex.Match(content);
if (!match.Success)
{
throw;
}
using var memoryStream2 = GetAsMemoryStream(match.Value);
if (FileFormatHandlerRegistry.TryLoadFromStream(memoryStream2, match.Success ? match.Groups["extension"]?.Value : null, out var bitmap))
{
return bitmap;
}
}
}
catch (Exception e)
{
Log.Error("Problem downloading the image from: " + url, e);
}
return null;
}
/// <summary> /// <summary>
/// Helper method to create a web request with a lot of default settings /// Helper method to create a web request with a lot of default settings
/// </summary> /// </summary>

View file

@ -935,7 +935,7 @@ namespace Greenshot.Editor.Drawing
} }
} }
foreach (var drawableContainer in ClipboardHelper.GetImages(e.Data)) foreach (var drawableContainer in ClipboardHelper.GetDrawables(e.Data))
{ {
drawableContainer.Left = mouse.X; drawableContainer.Left = mouse.X;
drawableContainer.Top = mouse.Y; drawableContainer.Top = mouse.Y;
@ -987,13 +987,11 @@ namespace Greenshot.Editor.Drawing
{ {
//create a blank bitmap the same size as original //create a blank bitmap the same size as original
Bitmap newBitmap = ImageHelper.CreateEmptyLike(Image, Color.Empty); Bitmap newBitmap = ImageHelper.CreateEmptyLike(Image, Color.Empty);
if (newBitmap != null) if (newBitmap == null) return;
{ // Make undoable
// Make undoable MakeUndoable(new SurfaceBackgroundChangeMemento(this, null), false);
MakeUndoable(new SurfaceBackgroundChangeMemento(this, null), false); SetImage(newBitmap, false);
SetImage(newBitmap, false); Invalidate();
Invalidate();
}
} }
/// <summary> /// <summary>
@ -2057,12 +2055,13 @@ namespace Greenshot.Editor.Drawing
{ {
Point pasteLocation = GetPasteLocation(0.1f, 0.1f); Point pasteLocation = GetPasteLocation(0.1f, 0.1f);
foreach (var drawableContainer in ClipboardHelper.GetImages(clipboard)) foreach (var drawableContainer in ClipboardHelper.GetDrawables(clipboard))
{ {
if (drawableContainer == null) continue; if (drawableContainer == null) continue;
DeselectAllElements(); DeselectAllElements();
drawableContainer.Left = pasteLocation.X; drawableContainer.Left = pasteLocation.X;
drawableContainer.Top = pasteLocation.Y; drawableContainer.Top = pasteLocation.Y;
AddElement(drawableContainer);
SelectElement(drawableContainer); SelectElement(drawableContainer);
pasteLocation.X += 10; pasteLocation.X += 10;
pasteLocation.Y += 10; pasteLocation.Y += 10;
@ -2206,24 +2205,23 @@ namespace Greenshot.Editor.Drawing
/// <param name="generateEvents">false to skip event generation</param> /// <param name="generateEvents">false to skip event generation</param>
public void SelectElement(IDrawableContainer container, bool invalidate = true, bool generateEvents = true) public void SelectElement(IDrawableContainer container, bool invalidate = true, bool generateEvents = true)
{ {
if (!selectedElements.Contains(container)) if (selectedElements.Contains(container)) return;
{
selectedElements.Add(container);
container.Selected = true;
FieldAggregator.BindElement(container);
if (generateEvents && _movingElementChanged != null)
{
SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs
{
Elements = selectedElements
};
_movingElementChanged(this, eventArgs);
}
if (invalidate) selectedElements.Add(container);
container.Selected = true;
FieldAggregator.BindElement(container);
if (generateEvents && _movingElementChanged != null)
{
SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs
{ {
container.Invalidate(); Elements = selectedElements
} };
_movingElementChanged(this, eventArgs);
}
if (invalidate)
{
container.Invalidate();
} }
} }

View file

@ -22,10 +22,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Greenshot.Base.Core;
using Greenshot.Base.Core.FileFormatHandlers; using Greenshot.Base.Core.FileFormatHandlers;
using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing; using Greenshot.Base.Interfaces.Drawing;
@ -73,20 +71,16 @@ namespace Greenshot.Editor.FileFormatHandlers
public bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap) public bool TryLoadFromStream(Stream stream, string extension, out Bitmap bitmap)
{ {
var svgDocument = SvgDocument.Open<SvgDocument>(stream); var svgDocument = SvgDocument.Open<SvgDocument>(stream);
int width = (int)svgDocument.ViewBox.Width;
int height = (int)svgDocument.ViewBox.Height;
try try
{ {
bitmap = ImageHelper.CreateEmpty(width, height, PixelFormat.Format32bppArgb, Color.Transparent); bitmap = svgDocument.Draw();
svgDocument.Draw(bitmap);
return true; return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error("Can't load SVG", ex); Log.Error("Can't load SVG", ex);
} }
bitmap = null; bitmap = null;
return false; return false;
} }