diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 82e746fda..9b5084acf 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -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") diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 39d5e23c7..9fd8b1610 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -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") diff --git a/src/display.c b/src/display.c index 9a337d1c0..0d52ca2cf 100644 --- a/src/display.c +++ b/src/display.c @@ -32,6 +32,7 @@ #ifdef _WIN32 +#include #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); } /* -------------------------------------------------------------------- */