Merge pull request #394 from shotr-io/master

Implement NativeImage support, fixes #14
This commit is contained in:
Gregor Biswanger
2020-05-09 21:09:12 +02:00
committed by GitHub
17 changed files with 896 additions and 91 deletions

View File

@@ -237,6 +237,40 @@ namespace ElectronNET.API
BridgeConnector.Socket.Emit("clipboard-write", JObject.FromObject(data, _jsonSerializer), type);
}
/// <summary>
/// Reads an image from the clipboard.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public Task<NativeImage> ReadImageAsync(string type = "")
{
var taskCompletionSource = new TaskCompletionSource<NativeImage>();
BridgeConnector.Socket.On("clipboard-readImage-Completed", (image) =>
{
BridgeConnector.Socket.Off("clipboard-readImage-Completed");
var nativeImage = ((JObject)image).ToObject<NativeImage>();
taskCompletionSource.SetResult(nativeImage);
});
BridgeConnector.Socket.Emit("clipboard-readImage", type);
return taskCompletionSource.Task;
}
/// <summary>
/// Writes an image to the clipboard.
/// </summary>
/// <param name="image"></param>
/// <param name="type"></param>
public void WriteImage(NativeImage image, string type = "")
{
BridgeConnector.Socket.Emit("clipboard-writeImage", JsonConvert.SerializeObject(image), type);
}
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),

View File

@@ -41,6 +41,7 @@ This package contains the API to access the "native" electron API.</Description>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SocketIoClientDotNet" Version="1.0.5" />
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,33 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class AddRepresentationOptions
{
/// <summary>
/// Gets or sets the width
/// </summary>
public int? Width { get; set; }
/// <summary>
/// Gets or sets the height
/// </summary>
public int? Height { get; set; }
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
/// <summary>
/// Gets or sets the buffer
/// </summary>
public byte[] Buffer { get; set; }
/// <summary>
/// Gets or sets the dataURL
/// </summary>
public string DataUrl { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class BitmapOptions
{
/// <summary>
/// Gets or sets the scale factor
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
}
}

View File

@@ -0,0 +1,23 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class CreateFromBitmapOptions
{
/// <summary>
/// Gets or sets the width
/// </summary>
public int? Width { get; set; }
/// <summary>
/// Gets or sets the height
/// </summary>
public int? Height { get; set; }
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
}
}

View File

@@ -0,0 +1,23 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class CreateFromBufferOptions
{
/// <summary>
/// Gets or sets the width
/// </summary>
public int? Width { get; set; }
/// <summary>
/// Gets or sets the height
/// </summary>
public int? Height { get; set; }
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
}
}

View File

@@ -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 :)
/// <summary>
///
/// Native Image handler for Electron.NET
/// </summary>
[JsonConverter(typeof(NativeImageJsonConverter))]
public class NativeImage
{
// public static NativeImage CreateEmpty()
// {
// throw new NotImplementedException();
// }
private readonly Dictionary<float, Image> _images = new Dictionary<float, Image>();
private bool _isTemplateImage;
// public static NativeImage CreateFromBuffer(byte[] buffer)
// {
// throw new NotImplementedException();
// }
private static readonly Dictionary<string, float> ScaleFactorPairs = new Dictionary<string, float>
{
{"@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}
};
// public static NativeImage CreateFromBuffer(byte[] buffer, CreateFromBufferOptions options)
// {
// throw new NotImplementedException();
// }
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);
}
// public static NativeImage CreateFromDataURL(string dataURL)
// {
// throw new NotImplementedException();
// }
/// <summary>
/// Creates an empty NativeImage
/// </summary>
public static NativeImage CreateEmpty()
{
return new NativeImage();
}
// public static NativeImage CreateFromPath(string path)
// {
// throw new NotImplementedException();
// }
/// <summary>
///
/// </summary>
public static NativeImage CreateFromBitmap(Bitmap bitmap, CreateFromBitmapOptions options = null)
{
if (options is null)
{
options = new CreateFromBitmapOptions();
}
// public void AddRepresentation(AddRepresentationOptions options)
// {
// throw new NotImplementedException();
// }
return new NativeImage(bitmap, options.ScaleFactor);
}
// public NativeImage Crop(Rectangle rect)
// {
// throw new NotImplementedException();
// }
/// <summary>
/// Creates a NativeImage from a byte array.
/// </summary>
public static NativeImage CreateFromBuffer(byte[] buffer, CreateFromBufferOptions options = null)
{
if (options is null)
{
options = new CreateFromBufferOptions();
}
// public int GetAspectRatio()
// {
// throw new NotImplementedException();
// }
var ms = new MemoryStream(buffer);
var image = Image.FromStream(ms);
// public byte[] GetBitmap()
// {
// throw new NotImplementedException();
// }
return new NativeImage(image, options.ScaleFactor);
}
// public byte[] GetBitmap(BitmapOptions options)
// {
// throw new NotImplementedException();
// }
/// <summary>
/// Creates a NativeImage from a base64 encoded data URL.
/// </summary>
/// <param name="dataUrl">A data URL with a base64 encoded image.</param>
public static NativeImage CreateFromDataURL(string dataUrl)
{
var images = new Dictionary<float,Image>();
var parsedDataUrl = Regex.Match(dataUrl, @"data:image/(?<type>.+?),(?<data>.+)");
var actualData = parsedDataUrl.Groups["data"].Value;
var binData = Convert.FromBase64String(actualData);
// public byte[] GetNativeHandle()
// {
// throw new NotImplementedException();
// }
var image = BytesToImage(binData);
// public Size GetSize()
// {
// throw new NotImplementedException();
// }
images.Add(1.0f, image);
// public bool IsEmpty()
// {
// throw new NotImplementedException();
// }
return new NativeImage(images);
}
// public bool IsTemplateImage()
// {
// throw new NotImplementedException();
// }
/// <summary>
/// Creates a NativeImage from an image on the disk.
/// </summary>
/// <param name="path">The path of the image</param>
public static NativeImage CreateFromPath(string path)
{
var images = new Dictionary<float,Image>();
if (Regex.IsMatch(path, "(@.+?x)"))
{
var dpi = ExtractDpiFromFilePath(path);
if (dpi == null)
{
throw new Exception($"Invalid scaling factor for '{path}'.");
}
// public NativeImage Resize(ResizeOptions options)
// {
// throw new NotImplementedException();
// }
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);
// public void SetTemplateImage(bool option)
// {
// throw new NotImplementedException();
// }
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);
}
}
}
}
// public byte[] ToBitmap(ToBitmapOptions options)
// {
// throw new NotImplementedException();
// }
return new NativeImage(images);
}
// public string ToDataURL(ToDataURLOptions options)
// {
// throw new NotImplementedException();
// }
/// <summary>
/// Creates an empty NativeImage
/// </summary>
public NativeImage()
{
}
// public byte[] ToJPEG(int quality)
// {
// throw new NotImplementedException();
// }
/// <summary>
/// Creates a NativeImage from a bitmap and scale factor
/// </summary>
public NativeImage(Image bitmap, float scaleFactor = 1.0f)
{
_images.Add(scaleFactor, bitmap);
}
// public byte[] ToPNG(ToPNGOptions options)
// {
// throw new NotImplementedException();
// }
/// <summary>
/// Creates a NativeImage from a dictionary of scales and images.
/// </summary>
public NativeImage(Dictionary<float, Image> imageDictionary)
{
_images = imageDictionary;
}
/// <summary>
/// Crops the image specified by the input rectangle and computes scale factor
/// </summary>
public NativeImage Crop(Rectangle rect)
{
var images = new Dictionary<float,Image>();
foreach (var image in _images)
{
images.Add(image.Key, Crop(rect.X, rect.Y, rect.Width, rect.Height, image.Key));
}
return new NativeImage(images);
}
/// <summary>
/// Resizes the image and computes scale factor
/// </summary>
public NativeImage Resize(ResizeOptions options)
{
var images = new Dictionary<float, Image>();
foreach (var image in _images)
{
images.Add(image.Key, Resize(options.Width, options.Height, image.Key));
}
return new NativeImage(images);
}
/// <summary>
/// Add an image representation for a specific scale factor.
/// </summary>
/// <param name="options"></param>
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);
}
}
/// <summary>
/// Gets the aspect ratio for the image based on scale factor
/// </summary>
/// <param name="scaleFactor">Optional</param>
public float GetAspectRatio(float scaleFactor = 1.0f)
{
var image = GetScale(scaleFactor);
if (image != null)
{
return image.Width / image.Height;
}
return 0f;
}
/// <summary>
/// Returns a byte array that contains the image's raw bitmap pixel data.
/// </summary>
public byte[] GetBitmap(BitmapOptions options)
{
return ToBitmap(new ToBitmapOptions{ ScaleFactor = options.ScaleFactor });
}
/// <summary>
/// Returns a byte array that contains the image's raw bitmap pixel data.
/// </summary>
public byte[] GetNativeHandle()
{
return ToBitmap(new ToBitmapOptions());
}
/// <summary>
/// Gets the size of the specified image based on scale factor
/// </summary>
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;
}
/// <summary>
/// Checks to see if the NativeImage instance is empty.
/// </summary>
public bool IsEmpty()
{
return _images.Count <= 0;
}
/// <summary>
/// Deprecated. Whether the image is a template image.
/// </summary>
public bool IsTemplateImage => _isTemplateImage;
/// <summary>
/// Deprecated. Marks the image as a template image.
/// </summary>
public void SetTemplateImage(bool option)
{
_isTemplateImage = option;
}
/// <summary>
/// Outputs a bitmap based on the scale factor
/// </summary>
public byte[] ToBitmap(ToBitmapOptions options)
{
return ImageToBytes(ImageFormat.Bmp, options.ScaleFactor);
}
/// <summary>
/// Outputs a data URL based on the scale factor
/// </summary>
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}";
}
/// <summary>
/// Outputs a JPEG for the default scale factor
/// </summary>
public byte[] ToJPEG(int quality)
{
return ImageToBytes(ImageFormat.Jpeg, 1.0f, quality);
}
/// <summary>
/// Outputs a PNG for the specified scale factor
/// </summary>
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<float,string> GetAllScaledImages()
{
var dict = new Dictionary<float,string>();
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;
}
}
}

View File

@@ -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<Dictionary<float, string>>(reader);
var newDictionary = new Dictionary<float, Image>();
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);
}
}

View File

@@ -0,0 +1,23 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class ResizeOptions
{
/// <summary>
/// Gets or sets the width
/// </summary>
public int? Width { get; set; }
/// <summary>
/// Gets or sets the height
/// </summary>
public int? Height { get; set; }
/// <summary>
/// good, better, or best. Default is "best";
/// </summary>
public string Quality { get; set; } = "best";
}
}

View File

@@ -0,0 +1,13 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class ToBitmapOptions
{
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
}
}

View File

@@ -0,0 +1,13 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class ToDataUrlOptions
{
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
}
}

View File

@@ -0,0 +1,13 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class ToPNGOptions
{
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
}
}

View File

@@ -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

View File

@@ -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);
});
};

View File

@@ -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 @@ namespace ElectronNET.WebApp.Controllers
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();

View File

@@ -8,7 +8,7 @@
</h1>
<h3>The <code>Electron.Clipboard</code> provides methods to perform copy and paste operations.</h3>
<p>This module also has methods for copying text as markup (HTML) to the clipboard.</p>
<p>You find the sample source code in <code>Controllers\ClipboardController.cs</code>.</p>
</div>
</header>
@@ -90,24 +90,206 @@ pasteBtn.addEventListener('click', () => {
</div>
</div>
<script>
(function(){
const { ipcRenderer } = require("electron");
<div class="demo">
<div class="demo-wrapper">
<button id="copy-to-demo-toggle" class="js-container-target demo-toggle-button">
Copy Image
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="copy-image-to">Copy</button>
<textarea id="paste-image-div" onpaste="console.log('onpastefromhtml')"></textarea>
<div class="demo-image-box" id="image-paste-div"></div>
</div>
<p>In this example we copy an image to the Clipboard. After clicking 'Copy' use the text area to paste (CMD + V or CTRL + V) the image from the clipboard.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("copy-image-to", async (test) =>
{
var nativeImage = NativeImage.CreateFromDataURL(test.ToString());
Electron.Clipboard.WriteImage(nativeImage);
});</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Electron.js Support in Electron.NET.</strong>
<p>The <code>clipboard</code> module is built into Electron.js (therefore you can use this in the renderer processes).</p>
<pre><code class="language-js">const { nativeImage, clipboard } = require('electron');
const copyBtn = document.getElementById('copy-to-image');
const imageDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACmklEQVR42oVTX0hTURj/zr2b07utMR0st2m30UxZJRHpWNDjRkUPvgUZQdGD4Esq6yWf9EVx9iL4EAiBVNDDHqxs9RBI+ZcorLVSkemcNdmct827Oe92Ot1b270i9MHHOfc73/f7fvd3zofggGU/OD1k8RJ3E2/6Gw4TnyYerDoXei3PR/82uwtOliydlJbtoA1OhtY7AFVZxDOc3YRiegkELsRjPjKKMYxoz3+NlAAyc00sBuhTm9ztavNFoBizhIqVbYrZOOzHp0DYmhkHhHt1reGIePTrfeOQ2tzaranzAqI1cmJlw1IYF3KwF30F+a0Fv8Ed7kHbU40elc4SYOxXGJoxibmDl++D70W/AkeMvewX94VsEvjV57yQjrWhxNuTfsbW0lVlaynxHbw6AL4Jn9QSkdb4QIzQ4TfmIbsxP4zibxpmjE6vS33EfCjzEn+MFHrkuTikQsFZFJt07BxtvWSgVBXwX8MyQYU8xOcmObQ+cWLH6rpAANQy2RU3LKssX0tR2IfY7DsOrQbsM7VnHK5Kg04S69o8+J62HEpAfpbjMvBjcXkWrTw77jeyxq7qeqOUdH0J2u7pwNFsUbyF5cVNCAxkwPe4QfxORVKQXNsZRt+esJ5KvSpQ26hnKrQ0JBI5GLvLSSCntFLxl12x+NYDA5hqNJDnC/Dze4bPpgtt4o+Gxo8NGS10t9lOA6VCkEhViCByE4ur96C4j2BrVYDtWMHvvLHWIwJ8flTPEqp91dZie40Ng0ZbFIVKxCT+JqsEsscjSEYpUkyRpwy9p2+uR0pSfxqrYxFGnYxB6NDXCIzOKEDlHyCSkctQkEmpIJ1U8TxHj5L0kebb0fIwye3jQ5uH9PUiQIpxxoCnSSx49k5UMc6/ARiuBRgyrEC3AAAAAElFTkSuQmCC';
copyBtn.addEventListener('click', () => {
var image = nativeImage.CreateFromDataURL(imageDataUrl);
clipboard.WriteImage(image);
})
</code></pre>
</div>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="copy-to-demo-toggle" class="js-container-target demo-toggle-button">
Paste Image
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="paste-image-from">Paste</button>
<img class="demo-image-box" id="paste-image-from-div"></img>
</div>
<p>In this example we paste an image from the Clipboard. After clicking 'Paste', if your clipboard contained an image, the image will show up next to the paste button.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">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));
});</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Electron.js Support in Electron.NET.</strong>
<p>The <code>clipboard</code> module is built into Electron.js (therefore you can use this in the renderer processes).</p>
<pre><code class="language-js">const { nativeImage, clipboard } = require('electron');
const pasteBtn = document.getElementById('paste-to-image');
copyBtn.addEventListener('click', () => {
const image = clipboard.readImage();
document.getElementById('image-holder').src = image.toDataURL();
})</code></pre>
</div>
</div>
</div>
</div>
<script>
(function () {
const { ipcRenderer, nativeImage } = require("electron");
document.getElementById("copy-to").addEventListener("click", () => {
document.getElementById('copy-to-input').placeholder = 'Copied! Paste here to see.';
ipcRenderer.send("copy-to", "Electron.NET Demo!");
});
document.getElementById("paste-to").addEventListener("click", () => {
ipcRenderer.send("paste-to", "What a demo!");
});
document.getElementById("copy-image-to").addEventListener("click", () => {
ipcRenderer.send("copy-image-to", 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACmklEQVR42oVTX0hTURj/zr2b07utMR0st2m30UxZJRHpWNDjRkUPvgUZQdGD4Esq6yWf9EVx9iL4EAiBVNDDHqxs9RBI+ZcorLVSkemcNdmct827Oe92Ot1b270i9MHHOfc73/f7fvd3zofggGU/OD1k8RJ3E2/6Gw4TnyYerDoXei3PR/82uwtOliydlJbtoA1OhtY7AFVZxDOc3YRiegkELsRjPjKKMYxoz3+NlAAyc00sBuhTm9ztavNFoBizhIqVbYrZOOzHp0DYmhkHhHt1reGIePTrfeOQ2tzaranzAqI1cmJlw1IYF3KwF30F+a0Fv8Ed7kHbU40elc4SYOxXGJoxibmDl++D70W/AkeMvewX94VsEvjV57yQjrWhxNuTfsbW0lVlaynxHbw6AL4Jn9QSkdb4QIzQ4TfmIbsxP4zibxpmjE6vS33EfCjzEn+MFHrkuTikQsFZFJt07BxtvWSgVBXwX8MyQYU8xOcmObQ+cWLH6rpAANQy2RU3LKssX0tR2IfY7DsOrQbsM7VnHK5Kg04S69o8+J62HEpAfpbjMvBjcXkWrTw77jeyxq7qeqOUdH0J2u7pwNFsUbyF5cVNCAxkwPe4QfxORVKQXNsZRt+esJ5KvSpQ26hnKrQ0JBI5GLvLSSCntFLxl12x+NYDA5hqNJDnC/Dze4bPpgtt4o+Gxo8NGS10t9lOA6VCkEhViCByE4ur96C4j2BrVYDtWMHvvLHWIwJ8flTPEqp91dZie40Ng0ZbFIVKxCT+JqsEsscjSEYpUkyRpwy9p2+uR0pSfxqrYxFGnYxB6NDXCIzOKEDlHyCSkctQkEmpIJ1U8TxHj5L0kebb0fIwye3jQ5uH9PUiQIpxxoCnSSx49k5UMc6/ARiuBRgyrEC3AAAAAElFTkSuQmCC');
});
document.getElementById("paste-image-from").addEventListener("click", () => {
ipcRenderer.send("paste-image-to");
});
ipcRenderer.on("paste-from", (sender, text) => {
const message = `Clipboard contents: ${text}`;
document.getElementById("paste-from").innerText = message;
});
ipcRenderer.on("paste-image-from", (sender, data) => {
console.log(data);
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 });
}
document.getElementById("paste-image-from-div").src = ni.toDataURL();
});
var PasteImage = function (el) {
this._el = el;
this._listenForPaste();
};
PasteImage.prototype._getURLObj = function () {
return window.URL || window.webkitURL;
};
PasteImage.prototype._pasteImage = function (image) {
this.emit('paste-image', image);
};
PasteImage.prototype._pasteImageSource = function (src) {
var self = this,
image = new Image();
image.onload = function () {
self._pasteImage(image);
};
image.src = src;
};
PasteImage.prototype._onPaste = function (e) {
// We need to check if event.clipboardData is supported (Chrome & IE)
if (e.clipboardData && e.clipboardData.items) {
// Get the items from the clipboard
var items = e.clipboardData.items;
// Loop through all items, looking for any kind of image
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
// We need to represent the image as a file
var blob = items[i].getAsFile();
// Use a URL or webkitURL (whichever is available to the browser) to create a
// temporary URL to the object
var URLObj = this._getURLObj();
var source = URLObj.createObjectURL(blob);
// The URL can then be used as the source of an image
this._pasteImageSource(source);
// Prevent the image (or URL) from being pasted into the contenteditable element
e.preventDefault();
}
}
}
};
PasteImage.prototype._listenForPaste = function () {
var self = this;
self._origOnPaste = self._el.onpaste;
self._el.addEventListener('paste', function (e) {
self._onPaste(e);
// Preserve an existing onpaste event handler
if (self._origOnPaste) {
self._origOnPaste.apply(this, arguments);
}
});
};
PasteImage.prototype.on = function (event, callback) {
this._callback = callback;
};
PasteImage.prototype.emit = function (event, arg) {
this._callback(arg);
};
var pasteImage = new PasteImage(document.getElementById('paste-image-div'));
pasteImage.on('paste-image', function (image) {
document.getElementById('image-paste-div').appendChild(image);
});
}());
</script>

View File

@@ -203,3 +203,7 @@
font-weight: 600;
}
/* Clipboard paste image ------------------ */
.demo-image-box {
padding-left: 15px;
}