Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion Tests/test_imagegrab.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import os
import subprocess
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:
Expand Down Expand Up @@ -71,3 +72,27 @@ def test_grabclipboard(self):

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 len(im) == 1
assert os.path.samefile(im[0], "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")
24 changes: 20 additions & 4 deletions src/PIL/ImageGrab.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,28 @@ def grabclipboard():
os.unlink(filepath)
return im
elif sys.platform == "win32":
data = Image.core.grabclipboard_win32()
fmt, data = Image.core.grabclipboard_win32()
if fmt == "file": # CF_HDROP
import struct

o = struct.unpack_from("I", data)[0]
if data[16] != 0:
files = data[o:].decode("utf-16le").split("\0")
else:
files = data[o:].decode("mbcs").split("\0")
return files[: files.index("")]
if isinstance(data, bytes):
from . import BmpImagePlugin
import io

return BmpImagePlugin.DibImageFile(io.BytesIO(data))
return data
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
Copy link
Copy Markdown
Member

@radarhere radarhere Jun 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might just be a curious question - why change this from return data to return None?

Copy link
Copy Markdown
Contributor Author

@nulano nulano Jun 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be dead code in both cases, I just think None is a safer fallback then returning undefined data. If I had to guess, the original return was written in anticipation of implementing CF_HDROP support in C code, but I found it easier in Python.

else:
raise NotImplementedError("ImageGrab.grabclipboard() is macOS and Windows only")
34 changes: 23 additions & 11 deletions src/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -473,33 +473,45 @@ 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 */
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;
}
}
}

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;
return Py_BuildValue("zO", NULL, Py_None);
}

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

/* -------------------------------------------------------------------- */
Expand Down