【问题标题】:Copy image to clipboard and preserve transparency将图像复制到剪贴板并保持透明度
【发布时间】:2021-03-28 19:18:41
【问题描述】:

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

我想在 Python 3 中做同样的事情。我希望能够使用 python 脚本将存储在我计算机上的图像(例如以 .png 格式)复制到 Windows 剪贴板,然后将其粘贴到 Discord 聊天中并具有透明度保存。

尝试 #1

我发现this 帖子包含以下代码。但正如我在代码中看到的,图像仅转换为 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)

我使用上述帖子中的代码得到的结果:


想要的结果:

我还尝试将图像保存为 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 剪贴板格式从剪贴板导入图像。

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

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

【问题讨论】:

    标签: python python-3.x windows clipboard transparency


    【解决方案1】:

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

    • 它可能不适用于所有应用(但它确实适用于 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'\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()
    

    【讨论】:

      【解决方案2】:

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

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

      编辑:不适用于所有图像。

      编辑 2:我找到了另一种解决方案,可以更好地解决您的情况。我将其发布在单独的答案中。

      【讨论】:

      • 尝试了您的代码,结果与尝试 #1 相同。 alpha 通道丢失,图像的背景被黑色替换。 Here 是我用来测试的图片。
      • 我做了一些测试。如果图像的任何像素的 alpha 值严格小于 127 但不为 0,则该代码似乎不起作用。找不到解决方法,将相应地编辑帖子。
      • 不幸的是,我希望能够保留 Alpha 通道,而不管其值如何。不过还是感谢您的尝试,给了您 +1。
      • 我做了更多的测试。似乎 127 的阈值不适用于所有图像。有些图像的阈值为 192,有些则完全忽略了阈值。我无法找出原因,但我会在完成实验后更新帖子。
      猜你喜欢
      • 1970-01-01
      • 2017-10-25
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 2015-02-14
      • 1970-01-01
      • 1970-01-01
      • 2011-04-19
      相关资源
      最近更新 更多