如何在没有PIL的情况下从剪贴板保存图像到文件?(How to save image to file from clipboard without PIL?)

编程入门 行业动态 更新时间:2024-10-24 06:29:50
如何在没有PIL的情况下从剪贴板保存图像到文件?(How to save image to file from clipboard without PIL?)

我的剪贴板中有截图。

我使用win32clipboard.GetClipboardData(win32con.CF_DIB)来获取字符串,并将字符串写入.bmp文件,但它无法由图片查看器打开。

那么,如果没有PIL(和其他图像第三部分库),如何将剪贴板中的图像写入本地?

I have a screenshot in my clipboard.

I have used win32clipboard.GetClipboardData(win32con.CF_DIB) to get a string, and written the string to a .bmp file, but it could not be opened by a picture viewer.

So, without PIL (and other image third part libs), how to write the image in clipboard to local?

最满意答案

简单方法的主要问题是写入文件的字符串缺少.bmp文件头,这是一个BITMAPFILEHEADER结构。

为了创建文件头,必须至少解码GetClipboardData()调用返回的字符串中的一些信息。 对于CF_DIB剪贴板格式,字符串中数据的第一部分将是BITMAPINFOHEADER 。

这种头结构是非常通用的,因为有许多不同风格的DIB具有各种比特的每个组件和压缩种类。 幸运的是,用于截图的一个非常简单 - 未压缩的RGBA像素。

这个事实使得事情变得更容易,因为否则确定要放入BITMAPFILEHEADER的bfOffBits字段的值会变得复杂,因为在大多数其他情况下,在BITMAPINFOHEADER和像素数组的开始之后还有一个可变大小的颜色表。

下面是处理该情况的示例代码(仅):

import ctypes from ctypes.wintypes import * import win32clipboard from win32con import * import sys class BITMAPFILEHEADER(ctypes.Structure): _pack_ = 1 # structure field byte alignment _fields_ = [ ('bfType', WORD), # file type ("BM") ('bfSize', DWORD), # file size in bytes ('bfReserved1', WORD), # must be zero ('bfReserved2', WORD), # must be zero ('bfOffBits', DWORD), # byte offset to the pixel array ] SIZEOF_BITMAPFILEHEADER = ctypes.sizeof(BITMAPFILEHEADER) class BITMAPINFOHEADER(ctypes.Structure): _pack_ = 1 # structure field byte alignment _fields_ = [ ('biSize', DWORD), ('biWidth', LONG), ('biHeight', LONG), ('biPLanes', WORD), ('biBitCount', WORD), ('biCompression', DWORD), ('biSizeImage', DWORD), ('biXPelsPerMeter', LONG), ('biYPelsPerMeter', LONG), ('biClrUsed', DWORD), ('biClrImportant', DWORD) ] SIZEOF_BITMAPINFOHEADER = ctypes.sizeof(BITMAPINFOHEADER) win32clipboard.OpenClipboard() try: if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_DIB): data = win32clipboard.GetClipboardData(win32clipboard.CF_DIB) else: print('clipboard does not contain an image in DIB format') sys.exit(1) finally: win32clipboard.CloseClipboard() bmih = BITMAPINFOHEADER() ctypes.memmove(ctypes.pointer(bmih), data, SIZEOF_BITMAPINFOHEADER) if bmih.biCompression != BI_BITFIELDS: # RGBA? print('insupported compression type {}'.format(bmih.biCompression)) sys.exit(1) bmfh = BITMAPFILEHEADER() ctypes.memset(ctypes.pointer(bmfh), 0, SIZEOF_BITMAPFILEHEADER) # zero structure bmfh.bfType = ord('B') | (ord('M') << 8) bmfh.bfSize = SIZEOF_BITMAPFILEHEADER + len(data) # file size SIZEOF_COLORTABLE = 0 bmfh.bfOffBits = SIZEOF_BITMAPFILEHEADER + SIZEOF_BITMAPINFOHEADER + SIZEOF_COLORTABLE bmp_filename = 'clipboard.bmp' with open(bmp_filename, 'wb') as bmp_file: bmp_file.write(bmfh) bmp_file.write(data) print('file "{}" created from clipboard image'.format(bmp_filename))

The primary problem with your simple approach is that the string written to the file is missing a .bmp file header, which is a BITMAPFILEHEADER structure.

In order to create the file header at least some of the information in the string returned by the GetClipboardData() call must be decoded. For CF_DIB clipboard format, the first part of the data in the string will be a BITMAPINFOHEADER.

This header structure is a very general as there are many different flavors of DIBs with various bits-per-component and kinds-of-compression. Fortunately the one used for screenshots is very simple — uncompressed RGBA pixels.

That fact makes things much easier because otherwise determining the value to put in the bfOffBits field of the BITMAPFILEHEADER would be complicated by the fact that in most other cases there's also a variably-sized color table following the BITMAPINFOHEADER and the start of the pixel array.

Below is example code that handles that case (only):

import ctypes from ctypes.wintypes import * import win32clipboard from win32con import * import sys class BITMAPFILEHEADER(ctypes.Structure): _pack_ = 1 # structure field byte alignment _fields_ = [ ('bfType', WORD), # file type ("BM") ('bfSize', DWORD), # file size in bytes ('bfReserved1', WORD), # must be zero ('bfReserved2', WORD), # must be zero ('bfOffBits', DWORD), # byte offset to the pixel array ] SIZEOF_BITMAPFILEHEADER = ctypes.sizeof(BITMAPFILEHEADER) class BITMAPINFOHEADER(ctypes.Structure): _pack_ = 1 # structure field byte alignment _fields_ = [ ('biSize', DWORD), ('biWidth', LONG), ('biHeight', LONG), ('biPLanes', WORD), ('biBitCount', WORD), ('biCompression', DWORD), ('biSizeImage', DWORD), ('biXPelsPerMeter', LONG), ('biYPelsPerMeter', LONG), ('biClrUsed', DWORD), ('biClrImportant', DWORD) ] SIZEOF_BITMAPINFOHEADER = ctypes.sizeof(BITMAPINFOHEADER) win32clipboard.OpenClipboard() try: if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_DIB): data = win32clipboard.GetClipboardData(win32clipboard.CF_DIB) else: print('clipboard does not contain an image in DIB format') sys.exit(1) finally: win32clipboard.CloseClipboard() bmih = BITMAPINFOHEADER() ctypes.memmove(ctypes.pointer(bmih), data, SIZEOF_BITMAPINFOHEADER) if bmih.biCompression != BI_BITFIELDS: # RGBA? print('insupported compression type {}'.format(bmih.biCompression)) sys.exit(1) bmfh = BITMAPFILEHEADER() ctypes.memset(ctypes.pointer(bmfh), 0, SIZEOF_BITMAPFILEHEADER) # zero structure bmfh.bfType = ord('B') | (ord('M') << 8) bmfh.bfSize = SIZEOF_BITMAPFILEHEADER + len(data) # file size SIZEOF_COLORTABLE = 0 bmfh.bfOffBits = SIZEOF_BITMAPFILEHEADER + SIZEOF_BITMAPINFOHEADER + SIZEOF_COLORTABLE bmp_filename = 'clipboard.bmp' with open(bmp_filename, 'wb') as bmp_file: bmp_file.write(bmfh) bmp_file.write(data) print('file "{}" created from clipboard image'.format(bmp_filename))

更多推荐

本文发布于:2023-07-29 20:39:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1319572.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:剪贴板   情况下   图像   文件   如何在

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!