spyoungtech/pyclip

Windows 10 screen capture raises UnparsableClipboardFormatException

Opened this issue · 4 comments

Hi there!

I think this is a really great tool and I am excited to use it for engineering practice where it's common to copy-paste screen grabs of analysis models.

I install pyclip and immediately tried to use it with a screen capture only to be greeted with the following:

---------------------------------------------------------------------------
UnparsableClipboardFormatException        Traceback (most recent call last)
Input In [15], in <cell line: 1>()
----> 1 nb.paste.paste_image()

File ~\Notebooks\package_development\nbengineer\nbengineer\paste.py:31, in paste_image()
     29 immediately in the current Jupyter notebook.
     30 """
---> 31 clipboard_data = pyclip.paste()
     32 first_eight_bytes = clipboard_data[:8]
     33 image_type = _identify_image_type(first_eight_bytes)

File ~\Anaconda3\lib\site-packages\pyclip\__init__.py:42, in paste(*args, **kwargs)
     40 if DEFAULT_CLIPBOARD is None:
     41     raise ClipboardSetupException("Could not setup clipboard").with_traceback(_CLIPBOARD_EXCEPTION_TB)
---> 42 return DEFAULT_CLIPBOARD.paste(*args, **kwargs)

File ~\Anaconda3\lib\site-packages\pyclip\win_clip.py:218, in WindowsClipboard.paste(self, encoding, text, errors)
    216 acceptable_formats = [f for f in all_formats if f in self._implemented_formats]
    217 if not acceptable_formats:
--> 218     raise UnparsableClipboardFormatException("Clipboard contents have no standard formats available. "
    219                                     "The contents can only be understood by a private program")
    220 if (text or encoding or errors) and format not in self._string_formats:
    221     raise ClipboardNotTextFormatException("Clipboard has no text formats available, but text options "
    222                                           "were specified.")

UnparsableClipboardFormatException: Clipboard contents have no standard formats available. The contents can only be understood by a private program

I was hoping that with all of the additional clipboard formats available on Windows that it would be quite flexible. Is this something that is just a small fix or is there something funny going on with Windows and their proprietary formats?

Thanks for any time you can spend on this!

Thanks for raising this issue. Right now, pyclip supports a narrow set of clipboard formats. Essentially, just text formats and hdrop (file copy/paste).

It seems you have copied and subsequently tried to paste through the pyclip program a format that is not supported.

I guess I would wonder: (1) what format(s) is stored when screen grabs are used to populate the clipboard and (2) what should the behavior of .paste do when dealing with these format(s).

Hi @spyoungtech,

After reading your source, and some of the Windows clipboard documentation, it seems that when one calls .GetClipboardData(), a person has various options available to them on what format they would like their data in. After reviewing the various formats available, I used a print() call to see what format a screen capture comes out with: 49161.

It seems that this clipboard format code is something of a generic code to refer to a DataObject. For screenshots, it seems that format codes 8: 'CF_DIB' and/or 17: 'CF_DIBV5' are the codes that return the byte arrays for the bitmap image.

I added an argument to .paste() called format_override which allows for the user to specify a custom Windows format code to get the data in the format required. The new .paste() code would look like this:

    def paste(self, encoding: str = None, text: bool = None, errors: str = None, format_override: int = None) -> Union[str, bytes]:
        """
        Returns clipboard contents

        :param encoding: same meaning as in ``bytes.encode``. Implies ``text=True``
        :param text: if True, bytes object will be en
        :param errors: same meaning as in ``bytes.encode``. Implies ``text=True``.
        :param format_override: an attempt is made to find the format of the clipboard data internally but sometimes,
                    on Windows, this format is incorrect. Allows user to specify a format to return the data in.
                    See: https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
                    Use format code 8 for bitmap screen captures.
        :return: clipboard contents. Return value is bytes by default
            or str if any of ``encoding``, ``text``, or ``errors`` is provided.
        """
        with self._clipboard as clip:
            if format_override:
                format = format_override
            else:
                format = clip.EnumClipboardFormats()
                if format == 0:
                    if text or encoding or errors:
                        return ''
                    else:
                        return b''

...

This byte data can be passed through a BytesIO into PIL.Image to then manipulate the image:

image


So, it seems that it is possible to get screen capture data using pyclip but, unfortunately, the clipboard format enum that is used by default, 49161 is ambiguous and requires the user to then specify what format they would like the bitmap data in.

In my local copy, I created this format_override which would allow me to get the functionality I need but then I would be vendoring pyclip into my own little utility package which is not ideal.

Let me know your thoughts on this. If you are interested in being able to work with Windows screen captures, then perhaps you might have some other ideas on how to manage this. My approach seems kinda hacky and it may break the API (because you would have different kwargs for different platforms).