23

I have an image (jpg, png, etc.) in the windows clipboard. I'd like to save it to a file. win32clipboard would seem to be the answer, but every example I can find deals with text.

copy an image to the clipboard, then

import win32clipboard
win32clipboard.OpenClipboard()
data = win32clipboard.GetClipboardData()
with open(name, 'wb') as f:
    f.write(data)
win32clipboard.CloseClipboard()

fails with

TypeError: Specified clipboard format is not available

I'd also like to do the reverse - given an image file, write it to the clipboard.

foosion
  • 7,619
  • 25
  • 65
  • 102
  • 1
    If you're open to using the wx module you could do: http://stackoverflow.com/questions/2629907/wx-read-image-from-clipboard – Ian Burris Aug 12 '11 at 19:43
  • I'm trying to write a simple command line utility, and don't currently use wx, so I'd rather not go there. – foosion Aug 12 '11 at 19:48

4 Answers4

36

I would just use Pillow:

from PIL import ImageGrab
im = ImageGrab.grabclipboard()
im.save('somefile.png','PNG')
anatoly techtonik
  • 19,847
  • 9
  • 124
  • 140
Gerrat
  • 28,863
  • 9
  • 73
  • 101
  • I was just looking at that, but I need both python 2.7 and 3.2 and PIL does not seem available for 3.2 – foosion Aug 12 '11 at 20:09
  • @foosion: I think there's a precompiled binary for python 3.2 here: http://www.lfd.uci.edu/~gohlke/pythonlibs/ – Gerrat Aug 12 '11 at 20:36
  • This is dead simple for 2.7. I was hoping that adding a few lines to the win32clipboard version would work like this. – foosion Aug 12 '11 at 20:38
  • works in 2.7. In 3.2 I get AttributeError: 'bytes' object has no attribute 'save' – foosion Aug 12 '11 at 20:42
  • 1
    @foosion: Change line 68 in ImageGrab.py to `if Image.isBytesType(data):` or use the updated installer. – cgohlke Aug 12 '11 at 21:48
  • Is there an opposite of ImageGrab - open an image file and copy the image to the clipboard? – foosion Aug 12 '11 at 22:00
  • Just wanted to mention: depending on how you've installed PIL, it might be just `import ImageGrab` instead of `from PIL import ImageGrab`. – S. Kirby Dec 16 '12 at 06:08
7

You need to pass a parameter to GetClipboardData specifying the format of the data you're looking for. You can use EnumClipboardFormats to see the formats that are available - when I copy something in Explorer there are 15 formats available to me.

Edit 2: Here's the code to get a filename after you've copied a file in Explorer. The answer will be completely different if you've copied an image from within a program, a browser for example.

import win32clipboard
win32clipboard.OpenClipboard()
filename_format = win32clipboard.RegisterClipboardFormat('FileName')
if win32clipboard.IsClipboardFormatAvailable(filename_format):
    input_filename = win32clipboard.GetClipboardData(filename_format)
win32clipboard.CloseClipboard()

Edit 3: From the comments it's clear you have an actual image in the clipboard, not the filename of an image file. You've stated that you can't use PIL, so:

import win32clipboard
win32clipboard.OpenClipboard()
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_DIB):
    data = win32clipboard.GetClipboardData(win32clipboard.CF_DIB)
win32clipboard.CloseClipboard()

At this point you have a string (in Python 2) or bytes (in Python 3) that contains the image data. The only format you'll be able to save is .BMP, and you'll have to decode the BITMAPINFOHEADER to get the parameters for a BITMAPFILEHEADER that needs to be written to the front of the file.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • I can create a filename myself. Do I cycle through EnumClipboardFormats() starting with 0? What do I do with the results? – foosion Aug 12 '11 at 20:02
  • @foosion, can you elaborate on exactly what's in the clipboard? Did you do a Copy on an image being displayed in a program, or on a filename in Explorer? – Mark Ransom Aug 12 '11 at 20:11
  • My current tests are to copy an image from a page in a web browser and from a paint program. Also, please see my comment to bgporter – foosion Aug 12 '11 at 20:16
  • Mark, I fear I may have been unclear. I'm not clicking on a filename in explorer, I'm clicking on an image and copying the image to the clipboard. – foosion Aug 12 '11 at 20:23
  • @foosion, your original question stated "I have an image file" which led to my confusion. Your comment above cleared it up. – Mark Ransom Aug 12 '11 at 20:29
  • The first code is giving me paths like "C:\Users\hamza\Desktop\ATTACH~1\FA1729~1.PNG" where the actual path is "C:\Users\hamza\Desktop\attachments\facetache (3)". How to retrieve actual paths using this? – Hamza Khurshid Jan 31 '20 at 14:43
  • @HamzaKhurshid you should be able to open the file with that name - it's a shortened version that Windows uses internally. If you really need to convert that filename into the long form, that would make a great new question. – Mark Ransom Jan 31 '20 at 14:47
  • I am not able to open using that filename. I have tried converting it to full path by listing all files of directory and choosing. I succeeded to a bit but failed in case of files with name starting from same characters. – Hamza Khurshid Jan 31 '20 at 14:50
  • Found the solution. Short path can be turned to long using win32api.GetLongPathName(shortPath) – Hamza Khurshid Jan 31 '20 at 14:57
5

Using PythonMagick (binaries):

from PythonMagick import Image
Image("clipboard:").write("PNG32:clipboard.png")  # clipboard -> file
Image("clipboard.png").write("clipboard:")  # file -> clipboard
cgohlke
  • 9,142
  • 2
  • 33
  • 36
4

The function win32clipboard.GetClipboardData() has a parameter. The default parameter specifies that you want the contents of the clipboard as text. You need to pass in the value that specifies the data format you want the clipboard to give you.

The standard clipboard data formats are documented here.

ALSO:

See here for documentation on EnumClipboardFormats() -- basically, you need code like this (untested) to get the formats that are available currently on the clipboard:

formats = []
lastFormat = 0
while 1:
    nextFormat = win32clipboard.EnumClipboardFormats(lastFormat)
    if 0 == nextFormat:
         # all done -- get out of the loop
         break
    else:
         formats.append(nextFormat)
         lastFormat = nextFormat
# when you get here, formats contains a list of format codes that
# you can retrieve from the clipboard right now.
bgporter
  • 35,114
  • 8
  • 59
  • 65
  • I just tried enumerating formats and got 49353, 0, 8, 0, 0, 0, 0, 17, 0. Do I just use 49353? win32clipboard.OpenClipboard(49353) results in error: (1400, 'OpenClipboard', 'Invalid window handle.') – foosion Aug 12 '11 at 20:14
  • Did you see the page with documentation on what the format codes are? 8 is `BITMAPINFO` and 7 is `BITMAPV5HEADER`, both of which include the bitmap data. You may end up needing to learn more about how Windows handles images than you want to... – bgporter Aug 12 '11 at 20:22
  • None of the codes listed in http://msdn.microsoft.com/en-us/library/ff729168(v=VS.85).aspx go as high as 49353. Next value is 8, which is BITMAPINFO structure, but I'm not clear on what to do with it – foosion Aug 12 '11 at 20:28
  • 49353 isn't a standard format -- Win32 has a function `GetClipboardFormatName` that should give you a clue what that format contains. – bgporter Aug 12 '11 at 20:39
  • @foosion, any format 49152 or greater isn't one of the hardcoded formats so it might vary from system to system. As bgporter says use `GetClipboardFormatName` to know what it is. – Mark Ransom Aug 12 '11 at 21:11
  • 1
    Re the first comment: The format code must be given as the parameter to `GetClipboardData()`, not to `OpenClipboard()`. – alexis Apr 10 '13 at 15:27