diff --git a/ElectronNET.API/Clipboard.cs b/ElectronNET.API/Clipboard.cs index 82815947..e7535378 100644 --- a/ElectronNET.API/Clipboard.cs +++ b/ElectronNET.API/Clipboard.cs @@ -237,6 +237,40 @@ public void Write(Data data, string type = "") BridgeConnector.Socket.Emit("clipboard-write", JObject.FromObject(data, _jsonSerializer), type); } + /// + /// Reads an image from the clipboard. + /// + /// + /// + public Task ReadImageAsync(string type = "") + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("clipboard-readImage-Completed", (image) => + { + BridgeConnector.Socket.Off("clipboard-readImage-Completed"); + + var nativeImage = ((JObject)image).ToObject(); + + taskCompletionSource.SetResult(nativeImage); + + }); + + BridgeConnector.Socket.Emit("clipboard-readImage", type); + + return taskCompletionSource.Task; + } + + /// + /// Writes an image to the clipboard. + /// + /// + /// + public void WriteImage(NativeImage image, string type = "") + { + BridgeConnector.Socket.Emit("clipboard-writeImage", JsonConvert.SerializeObject(image), type); + } + private JsonSerializer _jsonSerializer = new JsonSerializer() { ContractResolver = new CamelCasePropertyNamesContractResolver(), diff --git a/ElectronNET.API/ElectronNET.API.csproj b/ElectronNET.API/ElectronNET.API.csproj index 03bc805f..21a5ae13 100644 --- a/ElectronNET.API/ElectronNET.API.csproj +++ b/ElectronNET.API/ElectronNET.API.csproj @@ -41,6 +41,7 @@ This package contains the API to access the "native" electron API. runtime; build; native; contentfiles; analyzers + diff --git a/ElectronNET.API/Entities/AddRepresentationOptions.cs b/ElectronNET.API/Entities/AddRepresentationOptions.cs new file mode 100644 index 00000000..476fcbba --- /dev/null +++ b/ElectronNET.API/Entities/AddRepresentationOptions.cs @@ -0,0 +1,33 @@ +namespace ElectronNET.API.Entities +{ + /// + /// + /// + public class AddRepresentationOptions + { + /// + /// Gets or sets the width + /// + public int? Width { get; set; } + + /// + /// Gets or sets the height + /// + public int? Height { get; set; } + + /// + /// Gets or sets the scalefactor + /// + public float ScaleFactor { get; set; } = 1.0f; + + /// + /// Gets or sets the buffer + /// + public byte[] Buffer { get; set; } + + /// + /// Gets or sets the dataURL + /// + public string DataUrl { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/BitmapOptions.cs b/ElectronNET.API/Entities/BitmapOptions.cs new file mode 100644 index 00000000..d3aaebba --- /dev/null +++ b/ElectronNET.API/Entities/BitmapOptions.cs @@ -0,0 +1,13 @@ +namespace ElectronNET.API.Entities +{ + /// + /// + /// + public class BitmapOptions + { + /// + /// Gets or sets the scale factor + /// + public float ScaleFactor { get; set; } = 1.0f; + } +} diff --git a/ElectronNET.API/Entities/CreateFromBitmapOptions.cs b/ElectronNET.API/Entities/CreateFromBitmapOptions.cs new file mode 100644 index 00000000..6b80c219 --- /dev/null +++ b/ElectronNET.API/Entities/CreateFromBitmapOptions.cs @@ -0,0 +1,23 @@ +namespace ElectronNET.API.Entities +{ + /// + /// + /// + public class CreateFromBitmapOptions + { + /// + /// Gets or sets the width + /// + public int? Width { get; set; } + + /// + /// Gets or sets the height + /// + public int? Height { get; set; } + + /// + /// Gets or sets the scalefactor + /// + public float ScaleFactor { get; set; } = 1.0f; + } +} diff --git a/ElectronNET.API/Entities/CreateFromBufferOptions.cs b/ElectronNET.API/Entities/CreateFromBufferOptions.cs new file mode 100644 index 00000000..206f5acd --- /dev/null +++ b/ElectronNET.API/Entities/CreateFromBufferOptions.cs @@ -0,0 +1,23 @@ +namespace ElectronNET.API.Entities +{ + /// + /// + /// + public class CreateFromBufferOptions + { + /// + /// Gets or sets the width + /// + public int? Width { get; set; } + + /// + /// Gets or sets the height + /// + public int? Height { get; set; } + + /// + /// Gets or sets the scalefactor + /// + public float ScaleFactor { get; set; } = 1.0f; + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/NativeImage.cs b/ElectronNET.API/Entities/NativeImage.cs index 2e408d19..e1066bed 100644 --- a/ElectronNET.API/Entities/NativeImage.cs +++ b/ElectronNET.API/Entities/NativeImage.cs @@ -1,109 +1,453 @@ -namespace ElectronNET.API.Entities +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Newtonsoft.Json; + +namespace ElectronNET.API.Entities { - // TODO: Need some of real code :) /// - /// + /// Native Image handler for Electron.NET /// + [JsonConverter(typeof(NativeImageJsonConverter))] public class NativeImage { - // public static NativeImage CreateEmpty() - // { - // throw new NotImplementedException(); - // } - - // public static NativeImage CreateFromBuffer(byte[] buffer) - // { - // throw new NotImplementedException(); - // } - - // public static NativeImage CreateFromBuffer(byte[] buffer, CreateFromBufferOptions options) - // { - // throw new NotImplementedException(); - // } - - // public static NativeImage CreateFromDataURL(string dataURL) - // { - // throw new NotImplementedException(); - // } - - // public static NativeImage CreateFromPath(string path) - // { - // throw new NotImplementedException(); - // } - - // public void AddRepresentation(AddRepresentationOptions options) - // { - // throw new NotImplementedException(); - // } - - // public NativeImage Crop(Rectangle rect) - // { - // throw new NotImplementedException(); - // } - - // public int GetAspectRatio() - // { - // throw new NotImplementedException(); - // } - - // public byte[] GetBitmap() - // { - // throw new NotImplementedException(); - // } - - // public byte[] GetBitmap(BitmapOptions options) - // { - // throw new NotImplementedException(); - // } - - // public byte[] GetNativeHandle() - // { - // throw new NotImplementedException(); - // } - - // public Size GetSize() - // { - // throw new NotImplementedException(); - // } - - // public bool IsEmpty() - // { - // throw new NotImplementedException(); - // } - - // public bool IsTemplateImage() - // { - // throw new NotImplementedException(); - // } - - // public NativeImage Resize(ResizeOptions options) - // { - // throw new NotImplementedException(); - // } - - // public void SetTemplateImage(bool option) - // { - // throw new NotImplementedException(); - // } - - // public byte[] ToBitmap(ToBitmapOptions options) - // { - // throw new NotImplementedException(); - // } - - // public string ToDataURL(ToDataURLOptions options) - // { - // throw new NotImplementedException(); - // } - - // public byte[] ToJPEG(int quality) - // { - // throw new NotImplementedException(); - // } - - // public byte[] ToPNG(ToPNGOptions options) - // { - // throw new NotImplementedException(); - // } + private readonly Dictionary _images = new Dictionary(); + private bool _isTemplateImage; + + private static readonly Dictionary ScaleFactorPairs = new Dictionary + { + {"@2x", 2.0f}, {"@3x", 3.0f}, {"@1x", 1.0f}, {"@4x", 4.0f}, + {"@5x", 5.0f}, {"@1.25x", 1.25f}, {"@1.33x", 1.33f}, {"@1.4x", 1.4f}, + {"@1.5x", 1.5f}, {"@1.8x", 1.8f}, {"@2.5x", 2.5f} + }; + + private static float? ExtractDpiFromFilePath(string filePath) + { + var withoutExtension = Path.GetFileNameWithoutExtension(filePath); + return ScaleFactorPairs + .Where(p => withoutExtension.EndsWith(p.Key)) + .Select(p => p.Value) + .FirstOrDefault(); + } + private static Image BytesToImage(byte[] bytes) + { + var ms = new MemoryStream(bytes); + return Image.FromStream(ms); + } + + /// + /// Creates an empty NativeImage + /// + public static NativeImage CreateEmpty() + { + return new NativeImage(); + } + + /// + /// + /// + public static NativeImage CreateFromBitmap(Bitmap bitmap, CreateFromBitmapOptions options = null) + { + if (options is null) + { + options = new CreateFromBitmapOptions(); + } + + return new NativeImage(bitmap, options.ScaleFactor); + } + + /// + /// Creates a NativeImage from a byte array. + /// + public static NativeImage CreateFromBuffer(byte[] buffer, CreateFromBufferOptions options = null) + { + if (options is null) + { + options = new CreateFromBufferOptions(); + } + + var ms = new MemoryStream(buffer); + var image = Image.FromStream(ms); + + return new NativeImage(image, options.ScaleFactor); + } + + /// + /// Creates a NativeImage from a base64 encoded data URL. + /// + /// A data URL with a base64 encoded image. + public static NativeImage CreateFromDataURL(string dataUrl) + { + var images = new Dictionary(); + var parsedDataUrl = Regex.Match(dataUrl, @"data:image/(?.+?),(?.+)"); + var actualData = parsedDataUrl.Groups["data"].Value; + var binData = Convert.FromBase64String(actualData); + + var image = BytesToImage(binData); + + images.Add(1.0f, image); + + return new NativeImage(images); + } + + /// + /// Creates a NativeImage from an image on the disk. + /// + /// The path of the image + public static NativeImage CreateFromPath(string path) + { + var images = new Dictionary(); + if (Regex.IsMatch(path, "(@.+?x)")) + { + var dpi = ExtractDpiFromFilePath(path); + if (dpi == null) + { + throw new Exception($"Invalid scaling factor for '{path}'."); + } + + images[dpi.Value] = Image.FromFile(path); + } + else + { + var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); + var extension = Path.GetExtension(path); + // Load as 1x dpi + images[1.0f] = Image.FromFile(path); + + foreach (var scale in ScaleFactorPairs) + { + var fileName = $"{fileNameWithoutExtension}{scale}.{extension}"; + if (File.Exists(fileName)) + { + var dpi = ExtractDpiFromFilePath(fileName); + if (dpi != null) + { + images[dpi.Value] = Image.FromFile(fileName); + } + } + } + } + + return new NativeImage(images); + } + + /// + /// Creates an empty NativeImage + /// + public NativeImage() + { + } + + /// + /// Creates a NativeImage from a bitmap and scale factor + /// + public NativeImage(Image bitmap, float scaleFactor = 1.0f) + { + _images.Add(scaleFactor, bitmap); + } + + /// + /// Creates a NativeImage from a dictionary of scales and images. + /// + public NativeImage(Dictionary imageDictionary) + { + _images = imageDictionary; + } + + /// + /// Crops the image specified by the input rectangle and computes scale factor + /// + public NativeImage Crop(Rectangle rect) + { + var images = new Dictionary(); + foreach (var image in _images) + { + images.Add(image.Key, Crop(rect.X, rect.Y, rect.Width, rect.Height, image.Key)); + } + + return new NativeImage(images); + } + + /// + /// Resizes the image and computes scale factor + /// + public NativeImage Resize(ResizeOptions options) + { + var images = new Dictionary(); + foreach (var image in _images) + { + images.Add(image.Key, Resize(options.Width, options.Height, image.Key)); + } + + return new NativeImage(images); + } + + /// + /// Add an image representation for a specific scale factor. + /// + /// + public void AddRepresentation(AddRepresentationOptions options) + { + if (options.Buffer.Length > 0) + { + _images[options.ScaleFactor] = + CreateFromBuffer(options.Buffer, new CreateFromBufferOptions {ScaleFactor = options.ScaleFactor}) + .GetScale(options.ScaleFactor); + } + else if (!string.IsNullOrEmpty(options.DataUrl)) + { + _images[options.ScaleFactor] = CreateFromDataURL(options.DataUrl).GetScale(options.ScaleFactor); + } + } + + /// + /// Gets the aspect ratio for the image based on scale factor + /// + /// Optional + public float GetAspectRatio(float scaleFactor = 1.0f) + { + var image = GetScale(scaleFactor); + if (image != null) + { + return image.Width / image.Height; + } + + return 0f; + } + + /// + /// Returns a byte array that contains the image's raw bitmap pixel data. + /// + public byte[] GetBitmap(BitmapOptions options) + { + return ToBitmap(new ToBitmapOptions{ ScaleFactor = options.ScaleFactor }); + } + + /// + /// Returns a byte array that contains the image's raw bitmap pixel data. + /// + public byte[] GetNativeHandle() + { + return ToBitmap(new ToBitmapOptions()); + } + + /// + /// Gets the size of the specified image based on scale factor + /// + public Size GetSize(float scaleFactor = 1.0f) + { + if (_images.ContainsKey(scaleFactor)) + { + var image = _images[scaleFactor]; + return new Size + { + Width = image.Width, + Height = image.Height + }; + } + + return null; + } + + /// + /// Checks to see if the NativeImage instance is empty. + /// + public bool IsEmpty() + { + return _images.Count <= 0; + } + + /// + /// Deprecated. Whether the image is a template image. + /// + public bool IsTemplateImage => _isTemplateImage; + + /// + /// Deprecated. Marks the image as a template image. + /// + public void SetTemplateImage(bool option) + { + _isTemplateImage = option; + } + + /// + /// Outputs a bitmap based on the scale factor + /// + public byte[] ToBitmap(ToBitmapOptions options) + { + return ImageToBytes(ImageFormat.Bmp, options.ScaleFactor); + } + + /// + /// Outputs a data URL based on the scale factor + /// + public string ToDataURL(ToDataUrlOptions options) + { + if (!_images.ContainsKey(options.ScaleFactor)) + { + return null; + } + + var image = _images[options.ScaleFactor]; + var mimeType = ImageCodecInfo.GetImageEncoders().FirstOrDefault(x => x.FormatID == image.RawFormat.Guid)?.MimeType; + if (mimeType is null) + { + mimeType = "image/png"; + } + + var bytes = ImageToBytes(image.RawFormat, options.ScaleFactor); + var base64 = Convert.ToBase64String(bytes); + + return $"data:{mimeType};base64,{base64}"; + } + + /// + /// Outputs a JPEG for the default scale factor + /// + public byte[] ToJPEG(int quality) + { + return ImageToBytes(ImageFormat.Jpeg, 1.0f, quality); + } + + /// + /// Outputs a PNG for the specified scale factor + /// + public byte[] ToPNG(ToPNGOptions options) + { + return ImageToBytes(ImageFormat.Png, options.ScaleFactor); + } + + private byte[] ImageToBytes(ImageFormat imageFormat = null, float scaleFactor = 1.0f, int quality = 100) + { + using var ms = new MemoryStream(); + + if (_images.ContainsKey(scaleFactor)) + { + var image = _images[scaleFactor]; + var encoderCodecInfo = GetEncoder(imageFormat ?? image.RawFormat); + var encoder = Encoder.Quality; + + var encoderParameters = new EncoderParameters(1) + { + Param = new[] + { + new EncoderParameter(encoder, quality) + } + }; + + image.Save(ms, encoderCodecInfo, encoderParameters); + + return ms.ToArray(); + } + + return null; + } + + private Image Resize(int? width, int? height, float scaleFactor = 1.0f) + { + if (!_images.ContainsKey(scaleFactor) || (width is null && height is null)) + { + return null; + } + + var image = _images[scaleFactor]; + using (var g = Graphics.FromImage(image)) + { + g.CompositingQuality = CompositingQuality.HighQuality; + + var aspect = GetAspectRatio(scaleFactor); + + width ??= Convert.ToInt32(image.Width * aspect); + height ??= Convert.ToInt32(image.Height * aspect); + + width = Convert.ToInt32(width * scaleFactor); + height = Convert.ToInt32(height * scaleFactor); + + var bmp = new Bitmap(width.Value, height.Value); + g.DrawImage(bmp, + new System.Drawing.Rectangle(0, 0, image.Width, image.Height), + new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), + GraphicsUnit.Pixel); + + return bmp; + } + } + + private Image Crop(int? x, int? y, int? width, int? height, float scaleFactor = 1.0f) + { + if (!_images.ContainsKey(scaleFactor)) + { + return null; + } + + var image = _images[scaleFactor]; + using (var g = Graphics.FromImage(image)) + { + g.CompositingQuality = CompositingQuality.HighQuality; + + x ??= 0; + y ??= 0; + + x = Convert.ToInt32(x * scaleFactor); + y = Convert.ToInt32(y * scaleFactor); + + width ??= image.Width; + height ??= image.Height; + + width = Convert.ToInt32(width * scaleFactor); + height = Convert.ToInt32(height * scaleFactor); + + var bmp = new Bitmap(width.Value, height.Value); + g.DrawImage(bmp, new System.Drawing.Rectangle(0, 0, image.Width, image.Height), new System.Drawing.Rectangle(x.Value, y.Value, width.Value, height.Value), GraphicsUnit.Pixel); + + return bmp; + } + } + + private ImageCodecInfo GetEncoder(ImageFormat format) + { + var codecs = ImageCodecInfo.GetImageDecoders(); + foreach (ImageCodecInfo codec in codecs) + { + if (codec.FormatID == format.Guid) + { + return codec; + } + } + return null; + } + + internal Dictionary GetAllScaledImages() + { + var dict = new Dictionary(); + try + { + foreach (var (scale, image) in _images) + { + dict.Add(scale, Convert.ToBase64String(ImageToBytes(null, scale))); + } + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + + return dict; + } + + internal Image GetScale(float scaleFactor) + { + if (_images.ContainsKey(scaleFactor)) + { + return _images[scaleFactor]; + } + + return null; + } } } diff --git a/ElectronNET.API/Entities/NativeImageJsonConverter.cs b/ElectronNET.API/Entities/NativeImageJsonConverter.cs new file mode 100644 index 00000000..a82c38ec --- /dev/null +++ b/ElectronNET.API/Entities/NativeImageJsonConverter.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using Newtonsoft.Json; + +namespace ElectronNET.API.Entities +{ + internal class NativeImageJsonConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value is NativeImage nativeImage) + { + var scaledImages = nativeImage.GetAllScaledImages(); + serializer.Serialize(writer, scaledImages); + } + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var dict = serializer.Deserialize>(reader); + var newDictionary = new Dictionary(); + foreach (var item in dict) + { + var bytes = Convert.FromBase64String(item.Value); + newDictionary.Add(item.Key, Image.FromStream(new MemoryStream(bytes))); + } + return new NativeImage(newDictionary); + } + + public override bool CanConvert(Type objectType) => objectType == typeof(NativeImage); + } +} diff --git a/ElectronNET.API/Entities/ResizeOptions.cs b/ElectronNET.API/Entities/ResizeOptions.cs new file mode 100644 index 00000000..1490ccdb --- /dev/null +++ b/ElectronNET.API/Entities/ResizeOptions.cs @@ -0,0 +1,23 @@ +namespace ElectronNET.API.Entities +{ + /// + /// + /// + public class ResizeOptions + { + /// + /// Gets or sets the width + /// + public int? Width { get; set; } + + /// + /// Gets or sets the height + /// + public int? Height { get; set; } + + /// + /// good, better, or best. Default is "best"; + /// + public string Quality { get; set; } = "best"; + } +} diff --git a/ElectronNET.API/Entities/ToBitmapOptions.cs b/ElectronNET.API/Entities/ToBitmapOptions.cs new file mode 100644 index 00000000..1a08c3bf --- /dev/null +++ b/ElectronNET.API/Entities/ToBitmapOptions.cs @@ -0,0 +1,13 @@ +namespace ElectronNET.API.Entities +{ + /// + /// + /// + public class ToBitmapOptions + { + /// + /// Gets or sets the scalefactor + /// + public float ScaleFactor { get; set; } = 1.0f; + } +} diff --git a/ElectronNET.API/Entities/ToDataUrlOptions.cs b/ElectronNET.API/Entities/ToDataUrlOptions.cs new file mode 100644 index 00000000..0df4aa99 --- /dev/null +++ b/ElectronNET.API/Entities/ToDataUrlOptions.cs @@ -0,0 +1,13 @@ +namespace ElectronNET.API.Entities +{ + /// + /// + /// + public class ToDataUrlOptions + { + /// + /// Gets or sets the scalefactor + /// + public float ScaleFactor { get; set; } = 1.0f; + } +} diff --git a/ElectronNET.API/Entities/ToPNGOptions.cs b/ElectronNET.API/Entities/ToPNGOptions.cs new file mode 100644 index 00000000..f4e36b10 --- /dev/null +++ b/ElectronNET.API/Entities/ToPNGOptions.cs @@ -0,0 +1,13 @@ +namespace ElectronNET.API.Entities +{ + /// + /// + /// + public class ToPNGOptions + { + /// + /// Gets or sets the scalefactor + /// + public float ScaleFactor { get; set; } = 1.0f; + } +} diff --git a/ElectronNET.Host/api/clipboard.js b/ElectronNET.Host/api/clipboard.js index 610e072e..f32c6c7d 100644 --- a/ElectronNET.Host/api/clipboard.js +++ b/ElectronNET.Host/api/clipboard.js @@ -48,5 +48,21 @@ module.exports = (socket) => { socket.on('clipboard-write', (data, type) => { electron_1.clipboard.write(data, type); }); + socket.on('clipboard-readImage', (type) => { + const image = electron_1.clipboard.readImage(type); + electronSocket.emit('clipboard-readImage-Completed', { 1: image.toPNG().toString('base64') }); + }); + socket.on('clipboard-writeImage', (data, type) => { + var data = JSON.parse(data); + const ni = electron_1.nativeImage.createEmpty(); + for (var i in data) { + var scaleFactor = i; + var bytes = data[i]; + var buff = Buffer.from(bytes, 'base64'); + ni.addRepresentation({ scaleFactor: scaleFactor, buffer: buff }); + } + + electron_1.clipboard.writeImage(ni, type); + }); }; //# sourceMappingURL=clipboard.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/clipboard.ts b/ElectronNET.Host/api/clipboard.ts index f0f92c10..0cbca39f 100644 --- a/ElectronNET.Host/api/clipboard.ts +++ b/ElectronNET.Host/api/clipboard.ts @@ -1,4 +1,4 @@ -import { clipboard } from 'electron'; +import { clipboard, nativeImage } from 'electron'; let electronSocket; export = (socket: SocketIO.Socket) => { @@ -60,4 +60,22 @@ export = (socket: SocketIO.Socket) => { socket.on('clipboard-write', (data, type) => { clipboard.write(data, type); }); + + socket.on('clipboard-readImage', (type) => { + var image = clipboard.readImage(type); + electronSocket.emit('clipboard-readImage-Completed', { 1: image.toPNG().toString('base64') }); + }); + + socket.on('clipboard-writeImage', (data, type) => { + var data = JSON.parse(data); + const ni = nativeImage.createEmpty(); + for (var i in data) { + var scaleFactor = i; + var bytes = data[i]; + var buff = Buffer.from(bytes, 'base64'); + ni.addRepresentation({ scaleFactor: +scaleFactor, buffer: buff }); + } + + clipboard.writeImage(ni, type); + }); }; diff --git a/ElectronNET.WebApp/Controllers/ClipboardController.cs b/ElectronNET.WebApp/Controllers/ClipboardController.cs index 27f8d9e1..a290236a 100644 --- a/ElectronNET.WebApp/Controllers/ClipboardController.cs +++ b/ElectronNET.WebApp/Controllers/ClipboardController.cs @@ -1,6 +1,11 @@ -using Microsoft.AspNetCore.Mvc; +using System; +using System.Drawing; +using System.IO; +using Microsoft.AspNetCore.Mvc; using ElectronNET.API; using System.Linq; +using ElectronNET.API.Entities; +using Newtonsoft.Json; namespace ElectronNET.WebApp.Controllers { @@ -23,6 +28,19 @@ public IActionResult Index() var mainWindow = Electron.WindowManager.BrowserWindows.First(); Electron.IpcMain.Send(mainWindow, "paste-from", pasteText); }); + + Electron.IpcMain.On("copy-image-to", (test) => + { + var nativeImage = NativeImage.CreateFromDataURL(test.ToString()); + Electron.Clipboard.WriteImage(nativeImage); + }); + + Electron.IpcMain.On("paste-image-to", async test => + { + var nativeImage = await Electron.Clipboard.ReadImageAsync(); + var mainWindow = Electron.WindowManager.BrowserWindows.First(); + Electron.IpcMain.Send(mainWindow, "paste-image-from", JsonConvert.SerializeObject(nativeImage)); + }); } return View(); diff --git a/ElectronNET.WebApp/Views/Clipboard/Index.cshtml b/ElectronNET.WebApp/Views/Clipboard/Index.cshtml index 77e3098d..99264763 100644 --- a/ElectronNET.WebApp/Views/Clipboard/Index.cshtml +++ b/ElectronNET.WebApp/Views/Clipboard/Index.cshtml @@ -8,7 +8,7 @@

The Electron.Clipboard provides methods to perform copy and paste operations.

This module also has methods for copying text as markup (HTML) to the clipboard.

- +

You find the sample source code in Controllers\ClipboardController.cs.

@@ -90,24 +90,206 @@ pasteBtn.addEventListener('click', () => { - diff --git a/ElectronNET.WebApp/wwwroot/assets/css/demo.css b/ElectronNET.WebApp/wwwroot/assets/css/demo.css index 96afc4bd..4ffa4a1a 100644 --- a/ElectronNET.WebApp/wwwroot/assets/css/demo.css +++ b/ElectronNET.WebApp/wwwroot/assets/css/demo.css @@ -203,3 +203,7 @@ font-weight: 600; } +/* Clipboard paste image ------------------ */ +.demo-image-box { + padding-left: 15px; +} \ No newline at end of file