将图像复制到剪贴板并保留透明度
Copy image to clipboard and preserve transparency
我正在尝试实现现代 Web 浏览器(如 Firefox 和 Chrome 允许您执行的相同操作。当您右键单击网络上的透明图像然后 select“复制图像”时,该图像将被复制到您的剪贴板。因此,您可以稍后将其粘贴到 Discord 聊天室。 并保留透明度。
我想在 Python 中做同样的事情 3. 我希望能够使用 python 脚本,然后将其粘贴到 Discord 聊天室并保留透明度。
尝试 #1
我发现 post 具有以下代码。但是正如我在代码中看到的那样,图像仅转换为 RGB 并且 alpha 通道丢失了。
from io import BytesIO
import win32clipboard
from PIL import Image
def send_to_clipboard(clip_type, data):
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardData(clip_type, data)
win32clipboard.CloseClipboard()
image = Image.open("test.png")
output = BytesIO()
image.convert("RGB").save(output, "BMP")
data = output.getvalue()[14:]
output.close()
send_to_clipboard(win32clipboard.CF_DIB, data)
我使用上述 post 中的代码得到的结果:
期望的结果:
我也试过将图像保存为 png 格式:image.save(output, "PNG")
.
但这没有用,当我尝试将图像粘贴到聊天时,discord 崩溃了。
尝试 #2
接下来我尝试使用 CF_HDROP
,希望 discord 桌面应用程序能够识别它。
import ctypes
import pythoncom
import win32clipboard
from ctypes import wintypes
class DROPFILES(ctypes.Structure):
_fields_ = [("pFiles", wintypes.DWORD),
("pt", wintypes.POINT),
("fNC", wintypes.BOOL),
("fWide", wintypes.BOOL)]
path = r"D:\Visual Studio Code Projects\clipboard-test\test.png"
offset = ctypes.sizeof(DROPFILES)
size = offset + (len(path) + 1) * ctypes.sizeof(ctypes.c_wchar) + 1
buffer = (ctypes.c_char * size)()
df = DROPFILES.from_buffer(buffer)
df.pFiles = offset
df.fWide = True
wchars = (ctypes.c_wchar * (len(path) + 1)).from_buffer(buffer, offset)
wchars.value = path
stg = pythoncom.STGMEDIUM()
stg.set(pythoncom.TYMED_HGLOBAL, buffer)
win32clipboard.OpenClipboard()
try:
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardData(win32clipboard.CF_HDROP, stg.data)
finally:
win32clipboard.CloseClipboard()
Windows 文件资源管理器能够识别我存储在剪贴板中的数据,但 discord 不能。所以那里没有可见的结果。
尝试 #3
最后我找到了大约 CF_HTML
or html clipboard format。
import win32clipboard
class HTMLClipboard:
def __init__(self):
win32clipboard.OpenClipboard()
self.format = win32clipboard.RegisterClipboardFormat("HTML Format")
self.headers = "Version:0.9\r\n" +\
"StartHTML:00000000\r\n" +\
"EndHTML:00000000\r\n" +\
"StartFragment:00000000\r\n" +\
"EndFragment:00000000\r\n"
def _insertHeaders(self, data):
data = self.headers + data
hStartHtml = data.find("StartHTML")
startHtml = str(data.find("<html>"))
data = data[:hStartHtml + 18 - len(startHtml)] + startHtml + data[hStartHtml + 19:]
hEndHtml = data.find("EndHTML")
endHtml = str(len(data) - 1)
data = data[:hEndHtml + 16 - len(endHtml)] + endHtml + data[hEndHtml + 17:]
hStartFragment = data.find("StartFragment")
startFragment = str(data.find("<!--StartFragment-->") + 20)
data = data[:hStartFragment + 22 - len(startFragment)] + startFragment + data[hStartFragment + 23:]
hEndFragment = data.find("EndFragment")
endFragment = str(data.find("<!--EndFragment-->") + 1)
data = data[:hEndFragment + 20 - len(endFragment)] + endFragment + data[hEndFragment + 21:]
return data
def write(self, html):
data = "<html>\r\n" +\
"<body>\r\n" +\
"<!--StartFragment-->" +\
html + "" +\
"<!--EndFragment-->\r\n" +\
"</body>\r\n" +\
"</html>"
data = self._insertHeaders(data)
win32clipboard.SetClipboardData(self.format, data.encode("ascii"))
def read(self):
pass
def close(self):
win32clipboard.CloseClipboard()
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.close()
with HTMLClipboard() as clip:
clip.write("<img class='lnXdpd' alt='Google' src='https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png' srcset='/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png 1x, /images/branding/googlelogo/2x/googlelogo_color_272x92dp.png 2x' data-atf='1' width='272' height='92'>")
Discord 再次没有显示任何可见结果。但是奇怪的事情发生了,我使用 Microsoft Edge(基于 Chromium 的新版本)从网络上复制图像到剪贴板,并尝试使用我的代码仅重写 html 格式部分,但它仍然有效。
所以我猜测要么我仍然忘记了一些东西,一些我没有设置但浏览器设置的剪贴板格式,要么 discord 根本不使用 html 剪贴板格式从中导入图像剪贴板。
我什至尝试将上述尝试中的所有剪贴板格式一起使用,但除了透明丢失的格式(黑色背景)之外没有任何可见结果。
我真的不知道网络浏览器是如何做的。任何帮助将不胜感激。
我今天遇到了同样的问题。我发现可以对 Magick++ 使用 Python 绑定(我使用 Wand)来复制具有透明度的图像。然后,您可以将其粘贴到 Discord、Paint.NET 以及可能还有其他应用程序中。
以下是使用 Wand 的方法:
from wand.image import Image
Image(filename='test.png').save(filename='clipboard:')
编辑:不适用于所有图像。
编辑 2:我找到了另一个更适合您的解决方案。我把它发布在一个单独的答案中。
在我发现我的原始答案对大多数图像不起作用后,我进行了一些研究并构建了一个可行的解决方案。不幸的是,它有一些缺点:
- 它可能不适用于所有应用程序(但它确实适用于 Discord)。
- 它不能用于从内存中复制图像,只能从现有文件中复制图像。
- 它 绝对 不是跨平台的(我怀疑它是否适用于旧版本的 Windows,甚至。它似乎在 Windows 上运行良好至少 10 个)。
解决方案利用pywin32
如下:
import os
import win32clipboard as clp
file_path = 'test.png'
clp.OpenClipboard()
clp.EmptyClipboard()
# This works for Discord, but not for Paint.NET:
wide_path = os.path.abspath(file_path).encode('utf-16-le') + b'[=10=]'
clp.SetClipboardData(clp.RegisterClipboardFormat('FileNameW'), wide_path)
# This works for Paint.NET, but not for Discord:
file = open(file_path, 'rb')
clp.SetClipboardData(clp.RegisterClipboardFormat('image/png'), file.read())
clp.CloseClipboard()
我正在尝试实现现代 Web 浏览器(如 Firefox 和 Chrome 允许您执行的相同操作。当您右键单击网络上的透明图像然后 select“复制图像”时,该图像将被复制到您的剪贴板。因此,您可以稍后将其粘贴到 Discord 聊天室。 并保留透明度。
我想在 Python 中做同样的事情 3. 我希望能够使用 python 脚本,然后将其粘贴到 Discord 聊天室并保留透明度。
尝试 #1
我发现
from io import BytesIO
import win32clipboard
from PIL import Image
def send_to_clipboard(clip_type, data):
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardData(clip_type, data)
win32clipboard.CloseClipboard()
image = Image.open("test.png")
output = BytesIO()
image.convert("RGB").save(output, "BMP")
data = output.getvalue()[14:]
output.close()
send_to_clipboard(win32clipboard.CF_DIB, data)
我使用上述 post 中的代码得到的结果:
期望的结果:
我也试过将图像保存为 png 格式:image.save(output, "PNG")
.
但这没有用,当我尝试将图像粘贴到聊天时,discord 崩溃了。
尝试 #2
接下来我尝试使用 CF_HDROP
,希望 discord 桌面应用程序能够识别它。
import ctypes
import pythoncom
import win32clipboard
from ctypes import wintypes
class DROPFILES(ctypes.Structure):
_fields_ = [("pFiles", wintypes.DWORD),
("pt", wintypes.POINT),
("fNC", wintypes.BOOL),
("fWide", wintypes.BOOL)]
path = r"D:\Visual Studio Code Projects\clipboard-test\test.png"
offset = ctypes.sizeof(DROPFILES)
size = offset + (len(path) + 1) * ctypes.sizeof(ctypes.c_wchar) + 1
buffer = (ctypes.c_char * size)()
df = DROPFILES.from_buffer(buffer)
df.pFiles = offset
df.fWide = True
wchars = (ctypes.c_wchar * (len(path) + 1)).from_buffer(buffer, offset)
wchars.value = path
stg = pythoncom.STGMEDIUM()
stg.set(pythoncom.TYMED_HGLOBAL, buffer)
win32clipboard.OpenClipboard()
try:
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardData(win32clipboard.CF_HDROP, stg.data)
finally:
win32clipboard.CloseClipboard()
Windows 文件资源管理器能够识别我存储在剪贴板中的数据,但 discord 不能。所以那里没有可见的结果。
尝试 #3
最后我找到了大约 CF_HTML
or html clipboard format。
import win32clipboard
class HTMLClipboard:
def __init__(self):
win32clipboard.OpenClipboard()
self.format = win32clipboard.RegisterClipboardFormat("HTML Format")
self.headers = "Version:0.9\r\n" +\
"StartHTML:00000000\r\n" +\
"EndHTML:00000000\r\n" +\
"StartFragment:00000000\r\n" +\
"EndFragment:00000000\r\n"
def _insertHeaders(self, data):
data = self.headers + data
hStartHtml = data.find("StartHTML")
startHtml = str(data.find("<html>"))
data = data[:hStartHtml + 18 - len(startHtml)] + startHtml + data[hStartHtml + 19:]
hEndHtml = data.find("EndHTML")
endHtml = str(len(data) - 1)
data = data[:hEndHtml + 16 - len(endHtml)] + endHtml + data[hEndHtml + 17:]
hStartFragment = data.find("StartFragment")
startFragment = str(data.find("<!--StartFragment-->") + 20)
data = data[:hStartFragment + 22 - len(startFragment)] + startFragment + data[hStartFragment + 23:]
hEndFragment = data.find("EndFragment")
endFragment = str(data.find("<!--EndFragment-->") + 1)
data = data[:hEndFragment + 20 - len(endFragment)] + endFragment + data[hEndFragment + 21:]
return data
def write(self, html):
data = "<html>\r\n" +\
"<body>\r\n" +\
"<!--StartFragment-->" +\
html + "" +\
"<!--EndFragment-->\r\n" +\
"</body>\r\n" +\
"</html>"
data = self._insertHeaders(data)
win32clipboard.SetClipboardData(self.format, data.encode("ascii"))
def read(self):
pass
def close(self):
win32clipboard.CloseClipboard()
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.close()
with HTMLClipboard() as clip:
clip.write("<img class='lnXdpd' alt='Google' src='https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png' srcset='/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png 1x, /images/branding/googlelogo/2x/googlelogo_color_272x92dp.png 2x' data-atf='1' width='272' height='92'>")
Discord 再次没有显示任何可见结果。但是奇怪的事情发生了,我使用 Microsoft Edge(基于 Chromium 的新版本)从网络上复制图像到剪贴板,并尝试使用我的代码仅重写 html 格式部分,但它仍然有效。
所以我猜测要么我仍然忘记了一些东西,一些我没有设置但浏览器设置的剪贴板格式,要么 discord 根本不使用 html 剪贴板格式从中导入图像剪贴板。
我什至尝试将上述尝试中的所有剪贴板格式一起使用,但除了透明丢失的格式(黑色背景)之外没有任何可见结果。
我真的不知道网络浏览器是如何做的。任何帮助将不胜感激。
我今天遇到了同样的问题。我发现可以对 Magick++ 使用 Python 绑定(我使用 Wand)来复制具有透明度的图像。然后,您可以将其粘贴到 Discord、Paint.NET 以及可能还有其他应用程序中。
以下是使用 Wand 的方法:
from wand.image import Image
Image(filename='test.png').save(filename='clipboard:')
编辑:不适用于所有图像。
编辑 2:我找到了另一个更适合您的解决方案。我把它发布在一个单独的答案中。
在我发现我的原始答案对大多数图像不起作用后,我进行了一些研究并构建了一个可行的解决方案。不幸的是,它有一些缺点:
- 它可能不适用于所有应用程序(但它确实适用于 Discord)。
- 它不能用于从内存中复制图像,只能从现有文件中复制图像。
- 它 绝对 不是跨平台的(我怀疑它是否适用于旧版本的 Windows,甚至。它似乎在 Windows 上运行良好至少 10 个)。
解决方案利用pywin32
如下:
import os
import win32clipboard as clp
file_path = 'test.png'
clp.OpenClipboard()
clp.EmptyClipboard()
# This works for Discord, but not for Paint.NET:
wide_path = os.path.abspath(file_path).encode('utf-16-le') + b'[=10=]'
clp.SetClipboardData(clp.RegisterClipboardFormat('FileNameW'), wide_path)
# This works for Paint.NET, but not for Discord:
file = open(file_path, 'rb')
clp.SetClipboardData(clp.RegisterClipboardFormat('image/png'), file.read())
clp.CloseClipboard()