如何在没有 PIL 的情况下将图像从剪贴板保存到文件?
How to save image to file from clipboard without PIL?
我的剪贴板中有截图。
我用win32clipboard.GetClipboardData(win32con.CF_DIB)
获取了一个字符串,并写入了一个.bmp文件,但是图片浏览器打不开
那么,在没有PIL(以及其他图片第三方库)的情况下,如何将剪贴板中的图片写入本地?
您的简单方法的主要问题是写入文件的字符串缺少 .bmp
文件 header,这是一个 BITMAPFILEHEADER
结构。
为了创建文件 header,至少必须对 GetClipboardData()
调用返回的字符串中的某些信息进行解码。对于 CF_DIB
剪贴板格式,字符串中数据的第一部分将是 BITMAPINFOHEADER
.
这个 header 结构是一个非常通用的结构,因为有许多不同风格的 DIB,具有各种 bits-per-component 和 kinds-of-compression。幸运的是,用于屏幕截图的非常简单——未压缩的 RGBA 像素。
这一事实使事情变得容易得多,因为否则确定 BITMAPFILEHEADER
的 bfOffBits
字段中的值会变得复杂,因为在大多数其他情况下还有 [=33] =] 颜色 table 跟随 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))
我的剪贴板中有截图。
我用win32clipboard.GetClipboardData(win32con.CF_DIB)
获取了一个字符串,并写入了一个.bmp文件,但是图片浏览器打不开
那么,在没有PIL(以及其他图片第三方库)的情况下,如何将剪贴板中的图片写入本地?
您的简单方法的主要问题是写入文件的字符串缺少 .bmp
文件 header,这是一个 BITMAPFILEHEADER
结构。
为了创建文件 header,至少必须对 GetClipboardData()
调用返回的字符串中的某些信息进行解码。对于 CF_DIB
剪贴板格式,字符串中数据的第一部分将是 BITMAPINFOHEADER
.
这个 header 结构是一个非常通用的结构,因为有许多不同风格的 DIB,具有各种 bits-per-component 和 kinds-of-compression。幸运的是,用于屏幕截图的非常简单——未压缩的 RGBA 像素。
这一事实使事情变得容易得多,因为否则确定 BITMAPFILEHEADER
的 bfOffBits
字段中的值会变得复杂,因为在大多数其他情况下还有 [=33] =] 颜色 table 跟随 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))