Skip to content

Commit 05c6cb5

Browse files
alexkmbkalexkmbk
and
alexkmbk
authored
"writeImage" method support for windows (#145)
* fix js version dependance * writeImage method support for windows --------- Co-authored-by: alexkmbk <[email protected]>
1 parent 22bf4e6 commit 05c6cb5

File tree

4 files changed

+106
-1
lines changed

4 files changed

+106
-1
lines changed

packages/pasteboard/lib/pasteboard.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Pasteboard {
1010
/// available on iOS, desktop and the web.
1111
static Future<Uint8List?> get image => pasteboard.image;
1212

13-
/// only available on Windows
13+
/// only available on Windows and the web.
1414
/// Get "HTML format" from system pasteboard.
1515
/// HTML format: https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa767917(v=vs.85)
1616
static Future<String?> get html => pasteboard.html;

packages/pasteboard/lib/src/pasteboard_platform_io.dart

+24
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import 'dart:io';
22
import 'dart:typed_data';
3+
import 'package:path/path.dart' as Path;
4+
import 'package:uuid/uuid.dart';
35

46
import 'package:flutter/foundation.dart';
57
import 'package:flutter/services.dart';
@@ -63,6 +65,12 @@ class PasteboardPlatformIO implements PasteboardPlatform {
6365
}
6466
if (Platform.isIOS || Platform.isMacOS) {
6567
await _channel.invokeMethod<void>('writeImage', image);
68+
} else if (Platform.isWindows) {
69+
final file = await File(GetTempFileName()).create();
70+
file.writeAsBytesSync(image);
71+
await _channel
72+
.invokeMethod<Object>('writeImage', {'fileName': file.path});
73+
file.delete();
6674
}
6775
}
6876

@@ -77,3 +85,19 @@ class PasteboardPlatformIO implements PasteboardPlatform {
7785
Clipboard.setData(ClipboardData(text: value));
7886
}
7987
}
88+
89+
String GetTempFileName() {
90+
final dir = Directory.systemTemp;
91+
String tempFileName;
92+
93+
var uuid = Uuid();
94+
95+
while (true) {
96+
tempFileName = Path.join(dir.path, uuid.v1().toString());
97+
if (!File(tempFileName).existsSync()) {
98+
break;
99+
}
100+
}
101+
102+
return tempFileName;
103+
}

packages/pasteboard/windows/CMakeLists.txt

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.14)
22
set(PROJECT_NAME "pasteboard")
33
project(${PROJECT_NAME} LANGUAGES CXX)
44

5+
56
# This value is used when generating builds using this plugin, so it must
67
# not be changed
78
set(PLUGIN_NAME "pasteboard_plugin")
@@ -17,6 +18,11 @@ target_include_directories(${PLUGIN_NAME} INTERFACE
1718
"${CMAKE_CURRENT_SOURCE_DIR}/include")
1819
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
1920

21+
# GdiPlus doesn't compile with /W4 warning level
22+
target_compile_options(${PLUGIN_NAME} PRIVATE
23+
$<$<CXX_COMPILER_ID:MSVC>:/W3 /WX>
24+
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic -Werror>
25+
)
2026

2127

2228
# List of absolute paths to libraries that should be bundled with the plugin

packages/pasteboard/windows/pasteboard_plugin.cpp

+75
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@
44
#include <flutter/plugin_registrar_windows.h>
55
#include <flutter/standard_method_codec.h>
66

7+
78
#include <Windows.h>
89
#include <ShlObj.h>
10+
#include <gdiplus.h>
911

1012
#include <map>
1113
#include <memory>
1214

1315
#include "strconv.h"
1416

17+
#pragma comment(lib, "GdiPlus")
18+
1519
namespace {
1620

1721
constexpr STGMEDIUM kNullStorageMedium = {TYMED_NULL, nullptr, nullptr};
@@ -131,6 +135,49 @@ PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp) {
131135
return pbmi;
132136
}
133137

138+
// The code was taken from this answer: https://stackoverflow.com/a/39201008/2134488
139+
//
140+
bool CopyImageToClipboard(const wchar_t* filename)
141+
{
142+
//initialize Gdiplus once:
143+
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
144+
ULONG_PTR gdiplusToken;
145+
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
146+
147+
bool result = false;
148+
Gdiplus::Bitmap *gdibmp = Gdiplus::Bitmap::FromFile(filename);
149+
if (gdibmp)
150+
{
151+
HBITMAP hbitmap;
152+
gdibmp->GetHBITMAP(0, &hbitmap);
153+
if (OpenClipboard(NULL))
154+
{
155+
EmptyClipboard();
156+
DIBSECTION ds;
157+
if (GetObject(hbitmap, sizeof(DIBSECTION), &ds))
158+
{
159+
HDC hdc = GetDC(HWND_DESKTOP);
160+
//create compatible bitmap (get DDB from DIB)
161+
HBITMAP hbitmap_ddb = CreateDIBitmap(hdc, &ds.dsBmih, CBM_INIT,
162+
ds.dsBm.bmBits, (BITMAPINFO*)&ds.dsBmih, DIB_RGB_COLORS);
163+
ReleaseDC(HWND_DESKTOP, hdc);
164+
SetClipboardData(CF_BITMAP, hbitmap_ddb);
165+
DeleteObject(hbitmap_ddb);
166+
result = true;
167+
}
168+
CloseClipboard();
169+
}
170+
171+
//cleanup:
172+
DeleteObject(hbitmap);
173+
delete gdibmp;
174+
}
175+
176+
Gdiplus::GdiplusShutdown(gdiplusToken);
177+
178+
return result;
179+
}
180+
134181
void CreateBMPFile(LPCTSTR pszFile, HBITMAP hBMP) {
135182
HANDLE hf; // file handle
136183
BITMAPFILEHEADER hdr; // bitmap file-header
@@ -431,6 +478,34 @@ void PasteboardPlugin::HandleMethodCall(
431478
SetClipboardData(CF_HDROP, storage.hGlobal);
432479
result->Success();
433480
}
481+
else if (method_call.method_name() == "writeImage") {
482+
483+
484+
const auto* arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
485+
486+
std::string fileName;
487+
if (arguments) {
488+
auto it = arguments->find(flutter::EncodableValue("fileName"));
489+
if (it != arguments->end()) {
490+
if (std::holds_alternative<std::string>(it->second)) {
491+
fileName = std::get<std::string>(it->second);
492+
}
493+
}
494+
}
495+
496+
if (fileName.size() == 0) {
497+
result->Error("0", "File name is empty");
498+
return;
499+
}
500+
std::wstring wsFileName = cp_to_wide(fileName, CP_UTF8);
501+
502+
if (!CopyImageToClipboard(wsFileName.c_str())) {
503+
result->Error("0", "Failed to copy image to clipboard");
504+
return;
505+
}
506+
507+
result->Success();
508+
}
434509
else if (method_call.method_name() == "html") {
435510

436511
UINT CF_HTML = RegisterClipboardFormatA("HTML Format");

0 commit comments

Comments
 (0)