这个问题是this one的后续问题。
我使用第一个答案中的代码来获取桌面区域,并将其复制到剪贴板。看起来我似乎没有研究,但我做到了。问题是,这是我第一次接触ctypes,winapi和所有那些爵士乐。
MS Paint,Paint.net和LibreOffice可以完美地读取图像,但MS Word会改变宽高比;它只是将宽度和高度设置为15厘米。
我的问题是:Word期待什么样的数据?代码示例也很棒。
这是当前代码(与其他答案相同):
import ctypes
from ctypes import wintypes
from PIL import ImageGrab
from io import BytesIO
msvcrt = ctypes.cdll.msvcrt
windll = ctypes.windll
kernel32 = windll.kernel32
user32 = windll.user32
gdi32 = windll.gdi32
img = ImageGrab.grab()
output = BytesIO()
img.convert("RGB").save(output, "BMP")
data = output.getvalue()[14:]
output.close()
CF_DIB = 8
GMEM_MOVEABLE = 0x0002
global_mem = kernel32.GlobalAlloc(GMEM_MOVEABLE, len(data))
global_data = kernel32.GlobalLock(global_mem)
msvcrt.memcpy(ctypes.c_char_p(global_data), data, len(data))
kernel32.GlobalUnlock(global_mem)
user32.OpenClipboard(None)
user32.EmptyClipboard()
user32.SetClipboardData(CF_DIB, global_mem)
user32.CloseClipboard()
我认为使用winapi而不是使用PIL可能更容易。现在尺寸看起来正确,但图像到处都是黑色(LibreOffice,Word ...)。
class RECT(ctypes.Structure):
_fields_ = [
('left', ctypes.c_long),
('top', ctypes.c_long),
('right', ctypes.c_long),
('bottom', ctypes.c_long)
]
rect = RECT()
# self.whandle is the result of user32.GetDesktopWindow()
user32.GetWindowRect(self.whandle, ctypes.byref(rect))
hdcScreen = user32.GetDC(None)
hdc = gdi32.CreateCompatibleDC(hdcScreen)
hbmp = gdi32.CreateCompatibleBitmap(
hdcScreen,
rect.right - rect.left,
rect.bottom - rect.top
)
gdi32.SelectObject(hdc, hbmp)
PW_CLIENTONLY = 0x00000001
# This returns 0
user32.PrintWindow(self.whandle, hdc, PW_CLIENTONLY)
CF_BITMAP = 2
user32.OpenClipboard(None)
user32.EmptyClipboard()
user32.SetClipboardData(CF_BITMAP, hbmp)
user32.CloseClipboard()
gdi32.DeleteDC(hdc)
gdi32.DeleteObject(hbmp)
user32.ReleaseDC(None, hdcScreen)
PrintWindow返回0,因此失败:(
我尝试使用此代码段修补图片标头:
import struct
fmt = "LllHHLLllLL"
header_size = struct.calcsize(fmt)
# data comes from the first snippet
image_header = data[:header_size]
image_data = data[header_size:]
unpacked_header = list(struct.unpack_from(fmt, image_header))
# Indexes:
biWidth = 1
biHeight = 2
biXPelsPerMeter = 7
biYPelsPerMeter = 8
unpacked_header[biXPelsPerMeter] = 2835
unpacked_header[biYPelsPerMeter] = 2835
image_header = struct.pack(fmt, *unpacked_header)
data = image_header + image_data
*PelsPerMeter
的值是从here中提取的。
答案 0 :(得分:2)
Windows位图的分辨率由`BITMAPINFOHEADER'结构的biXPelsPerMeter
和biYPelsPerMeter
成员定义。
Python成像库(PIL)将分辨率写入每米1个像素的两个方向(BmpImagePlugin.py
的第225行)。因此,Word认为位图的大小已达数百米,并且严重缩放。 PIL不提供改变分辨率的界面。
您可以通过将值修补到data
对象中来进行hacky修复。此对象很可能以序列化BITMAPINFOHEADER
开头,但您需要检查biSize
(前四个字节)才能确定。然后,您可以分别在抵消24和28处找到biXPelsPerMeter
和biYPelsPerMeter
。
一个不太常见的修复方法是向PIL提交补丁,选择合理的默认解决方案。