繁体   English   中英

将图像复制到剪贴板并保持透明度

[英]Copy image to clipboard and preserve transparency

我正在尝试实现与 Firefox 和 Chrome 等现代 web 浏览器相同的事情。 当您右键单击 web 上的透明图像,然后单击 select“复制图像”时,图像将被复制到剪贴板。 因此,您可以稍后将其粘贴到 Discord 聊天中。 并且保留了透明度。

I want to do the same thing in Python 3. I want to be able to copy image stored on my computer (for example in.png format) to windows clipboard using python script and then paste it to Discord chat with the transparency preserved.

尝试#1

我发现这篇文章包含以下代码。 但正如我在代码中看到的那样,图像仅转换为 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)

结果我通过使用上述帖子中的代码得到:
Alpha 通道丢失

期望的结果:
期望的结果

我还尝试将图像保存为 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或 html 剪贴板格式

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 的新版本)从 web 复制图像到剪贴板,并尝试使用我的代码仅重写 html 格式部分,它仍然有效。

所以我猜要么我仍然忘记了一些东西,一些我没有设置但浏览器设置的剪贴板格式,或者 discord 根本不使用 html 剪贴板格式从剪贴板导入图像。

我什至尝试同时使用上述尝试中的所有剪贴板格式,但没有可见结果,除了透明度丢失(黑色背景)的那个。

我真的不知道 web 浏览器是如何做到的。 任何帮助,将不胜感激。

我今天遇到了同样的问题。 我发现可以使用 Magick++ 的 Python 绑定(我使用Wand )来复制具有透明度的图像。 然后,您可以将其粘贴到 Discord、Paint.NET 以及可能的其他应用程序中。
以下是使用 Wand 的方法:

from wand.image import Image
Image(filename='test.png').save(filename='clipboard:')

在我发现我的原始答案不适用于大多数图像后,我做了一些研究并构建了一个可行的解决方案。 不幸的是,它有一些缺点:

  • 它可能不适用于所有应用程序(但它确实适用于 Discord)。
  • 它不能用于从 memory 复制图像,只能从现有文件中复制。
  • 绝对不是跨平台的(我怀疑它甚至可以在旧版本的 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'\0'
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()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM