add support for CF_DIBV5, CF_HDROP, and 'PNG' in ImageGrab.grabclipboard() on win32

This commit is contained in:
nulano 2020-05-09 09:29:40 +02:00
parent d23df7227c
commit 5728662c7f
3 changed files with 90 additions and 21 deletions

View File

@ -4,7 +4,7 @@ import sys
import pytest
from PIL import Image, ImageGrab
from .helper import assert_image
from .helper import assert_image, assert_image_equal_tofile
class TestImageGrab:
@ -71,3 +71,26 @@ $bmp = New-Object Drawing.Bitmap 200, 200
im = ImageGrab.grabclipboard()
assert_image(im, im.mode, im.size)
@pytest.mark.skipif(sys.platform != "win32", reason="Windows only")
def test_grabclipboard_file(self):
p = subprocess.Popen(["powershell", "-command", "-"], stdin=subprocess.PIPE)
p.stdin.write(rb'Set-Clipboard -Path "Tests\images\hopper.gif"')
p.communicate()
im = ImageGrab.grabclipboard()
assert_image_equal_tofile(im, "Tests/images/hopper.gif")
@pytest.mark.skipif(sys.platform != "win32", reason="Windows only")
def test_grabclipboard_png(self):
p = subprocess.Popen(["powershell", "-command", "-"], stdin=subprocess.PIPE)
p.stdin.write(
rb"""$bytes = [System.IO.File]::ReadAllBytes("Tests\images\hopper.png")
$ms = new-object System.IO.MemoryStream(, $bytes)
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[Windows.Forms.Clipboard]::SetData("PNG", $ms)"""
)
p.communicate()
im = ImageGrab.grabclipboard()
assert_image_equal_tofile(im, "Tests/images/hopper.png")

View File

@ -93,12 +93,24 @@ def grabclipboard():
os.unlink(filepath)
return im
elif sys.platform == "win32":
data = Image.core.grabclipboard_win32()
if isinstance(data, bytes):
from . import BmpImagePlugin
import io
import io
return BmpImagePlugin.DibImageFile(io.BytesIO(data))
return data
fmt, data = Image.core.grabclipboard_win32()
if isinstance(data, str):
if fmt == "file":
with open(data, "rb") as f:
im = Image.open(io.BytesIO(f.read()))
return im
if isinstance(data, bytes):
data = io.BytesIO(data)
if fmt == "png":
from . import PngImagePlugin
return PngImagePlugin.PngImageFile(data)
elif fmt == "DIB":
from . import BmpImagePlugin
return BmpImagePlugin.DibImageFile(data)
return None
else:
raise NotImplementedError("ImageGrab.grabclipboard() is macOS and Windows only")

View File

@ -32,6 +32,7 @@
#ifdef _WIN32
#include <Shlobj.h>
#include "ImDib.h"
#if SIZEOF_VOID_P == 8
@ -473,33 +474,66 @@ PyObject*
PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args)
{
int clip;
HANDLE handle;
HANDLE handle = NULL;
int size;
void* data;
PyObject* result;
UINT format;
UINT formats[] = { CF_DIB, CF_DIBV5, CF_HDROP, RegisterClipboardFormatA("PNG"), 0 };
LPCSTR format_names[] = { "DIB", "DIB", "file", "png", NULL };
clip = OpenClipboard(NULL);
/* FIXME: check error status */
handle = GetClipboardData(CF_DIB);
if (!handle) {
/* FIXME: add CF_HDROP support to allow cut-and-paste from
the explorer */
CloseClipboard();
Py_INCREF(Py_None);
return Py_None;
if (!OpenClipboard(NULL)) {
PyErr_SetString(PyExc_OSError, "failed to open clipboard");
return NULL;
}
// find best format as set by clipboard owner
format = 0;
while (!handle && (format = EnumClipboardFormats(format))) {
for (UINT i = 0; formats[i] != 0; i++) {
if (format == formats[i]) {
handle = GetClipboardData(format);
format = i;
break;
}
}
}
if (!handle) {
CloseClipboard();
return Py_BuildValue("zO", NULL, Py_None);
}
if (formats[format] == CF_HDROP) {
LPDROPFILES files = (LPDROPFILES)GlobalLock(handle);
size = GlobalSize(handle);
if (files->fWide) {
LPCWSTR filename = (LPCWSTR)(((char*)files) + files->pFiles);
size = wcsnlen(filename, (size - files->pFiles) / 2);
result = Py_BuildValue("zu#", "file", filename, size);
}
else {
LPCSTR filename = (LPCSTR)(((char*)files) + files->pFiles);
size = strnlen(filename, size - files->pFiles);
result = Py_BuildValue("zs#", "file", filename, size);
}
GlobalUnlock(handle);
CloseClipboard();
return result;
}
size = GlobalSize(handle);
data = GlobalLock(handle);
size = GlobalSize(handle);
result = PyBytes_FromStringAndSize(data, size);
GlobalUnlock(handle);
CloseClipboard();
return result;
return Py_BuildValue("zN", format_names[format], result);
}
/* -------------------------------------------------------------------- */