mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-10 19:56:47 +03:00
Merge branch 'main' into apng_duration
This commit is contained in:
commit
400716da0d
|
@ -22,7 +22,8 @@ set -e
|
|||
if [[ $(uname) != CYGWIN* ]]; then
|
||||
sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\
|
||||
ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\
|
||||
cmake meson imagemagick libharfbuzz-dev libfribidi-dev
|
||||
cmake meson imagemagick libharfbuzz-dev libfribidi-dev\
|
||||
sway wl-clipboard
|
||||
fi
|
||||
|
||||
python3 -m pip install --upgrade pip
|
||||
|
@ -41,7 +42,7 @@ if [[ $(uname) != CYGWIN* ]]; then
|
|||
if ! [ "$GHA_PYTHON_VERSION" == "3.12-dev" ]; then python3 -m pip install numpy ; fi
|
||||
|
||||
# PyQt6 doesn't support PyPy3
|
||||
if [[ $GHA_PYTHON_VERSION == 3.* ]]; then
|
||||
if [[ "$GHA_PYTHON_VERSION" != "3.12-dev" && $GHA_PYTHON_VERSION == 3.* ]]; then
|
||||
sudo apt-get -qq install libegl1 libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxkbcommon-x11-0
|
||||
python3 -m pip install pyqt6
|
||||
fi
|
||||
|
|
|
@ -13,10 +13,6 @@ indent_style = space
|
|||
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.rst]
|
||||
# Four-space indentation
|
||||
indent_size = 4
|
||||
|
||||
[*.yml]
|
||||
# Two-space indentation
|
||||
indent_size = 2
|
||||
|
|
4
.github/workflows/test-windows.yml
vendored
4
.github/workflows/test-windows.yml
vendored
|
@ -65,8 +65,8 @@ jobs:
|
|||
- name: Print build system information
|
||||
run: python3 .github/workflows/system-info.py
|
||||
|
||||
- name: python3 -m pip install wheel pytest pytest-cov pytest-timeout defusedxml
|
||||
run: python3 -m pip install wheel pytest pytest-cov pytest-timeout defusedxml
|
||||
- name: python3 -m pip install setuptools wheel pytest pytest-cov pytest-timeout defusedxml
|
||||
run: python3 -m pip install setuptools wheel pytest pytest-cov pytest-timeout defusedxml
|
||||
|
||||
- name: Install dependencies
|
||||
id: install
|
||||
|
|
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
|
@ -84,7 +84,9 @@ jobs:
|
|||
python3 -m pip install pytest-reverse
|
||||
fi
|
||||
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
|
||||
xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh
|
||||
xvfb-run -s '-screen 0 1024x768x24' sway&
|
||||
export WAYLAND_DISPLAY=wayland-1
|
||||
.ci/test.sh
|
||||
else
|
||||
.ci/test.sh
|
||||
fi
|
||||
|
|
|
@ -4,9 +4,6 @@ repos:
|
|||
hooks:
|
||||
- id: black
|
||||
args: [--target-version=py38]
|
||||
# Only .py files, until https://github.com/psf/black/issues/402 resolved
|
||||
files: \.py$
|
||||
types: []
|
||||
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.12.0
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
version: 2
|
||||
|
||||
formats: all
|
||||
formats: [pdf]
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
|
|
15
CHANGES.rst
15
CHANGES.rst
|
@ -5,6 +5,21 @@ Changelog (Pillow)
|
|||
10.0.0 (unreleased)
|
||||
-------------------
|
||||
|
||||
- Improved wl-paste mimetype handling in ImageGrab #7094
|
||||
[rrcgat, radarhere]
|
||||
|
||||
- Added _repr_jpeg_() for IPython display_jpeg #7135
|
||||
[n3011, radarhere, nulano]
|
||||
|
||||
- Use "/sbin/ldconfig" if ldconfig is not found #7068
|
||||
[radarhere]
|
||||
|
||||
- Prefer screenshots using XCB over gnome-screenshot #7143
|
||||
[nulano, radarhere]
|
||||
|
||||
- Fixed joined corners for ImageDraw rounded_rectangle() odd dimensions #7151
|
||||
[radarhere]
|
||||
|
||||
- Support reading signed 8-bit TIFF images #7111
|
||||
[radarhere]
|
||||
|
||||
|
|
BIN
Tests/images/imagedraw_rounded_rectangle_x_odd.png
Normal file
BIN
Tests/images/imagedraw_rounded_rectangle_x_odd.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 565 B |
BIN
Tests/images/imagedraw_rounded_rectangle_y_odd.png
Normal file
BIN
Tests/images/imagedraw_rounded_rectangle_y_odd.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 527 B |
|
@ -252,6 +252,19 @@ def test_roundtrip_save_all(tmp_path):
|
|||
assert reread.n_frames == 5
|
||||
|
||||
|
||||
def test_roundtrip_save_all_1(tmp_path):
|
||||
out = str(tmp_path / "temp.gif")
|
||||
im = Image.new("1", (1, 1))
|
||||
im2 = Image.new("1", (1, 1), 1)
|
||||
im.save(out, save_all=True, append_images=[im2])
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert reloaded.getpixel((0, 0)) == 0
|
||||
|
||||
reloaded.seek(1)
|
||||
assert reloaded.getpixel((0, 0)) == 255
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path, mode",
|
||||
(
|
||||
|
|
|
@ -922,6 +922,19 @@ class TestFileJpeg:
|
|||
im.load()
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||
|
||||
def test_repr_jpeg(self):
|
||||
im = hopper()
|
||||
|
||||
with Image.open(BytesIO(im._repr_jpeg_())) as repr_jpeg:
|
||||
assert repr_jpeg.format == "JPEG"
|
||||
assert_image_similar(im, repr_jpeg, 17)
|
||||
|
||||
def test_repr_jpeg_error(self):
|
||||
im = hopper("F")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
im._repr_jpeg_()
|
||||
|
||||
|
||||
@pytest.mark.skipif(not is_win32(), reason="Windows only")
|
||||
@skip_unless_feature("jpg")
|
||||
|
|
|
@ -839,7 +839,9 @@ def test_rounded_rectangle_zero_radius(bbox):
|
|||
"xy, suffix",
|
||||
[
|
||||
((20, 10, 80, 90), "x"),
|
||||
((20, 10, 81, 90), "x_odd"),
|
||||
((10, 20, 90, 80), "y"),
|
||||
((10, 20, 90, 81), "y_odd"),
|
||||
((20, 20, 80, 80), "both"),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -98,3 +98,18 @@ $ms = new-object System.IO.MemoryStream(, $bytes)
|
|||
|
||||
im = ImageGrab.grabclipboard()
|
||||
assert_image_equal_tofile(im, "Tests/images/hopper.png")
|
||||
|
||||
@pytest.mark.skipif(
|
||||
(
|
||||
sys.platform != "linux"
|
||||
or not all(shutil.which(cmd) for cmd in ("wl-paste", "wl-copy"))
|
||||
),
|
||||
reason="Linux with wl-clipboard only",
|
||||
)
|
||||
@pytest.mark.parametrize("ext", ("gif", "png", "ico"))
|
||||
def test_grabclipboard_wl_clipboard(self, ext):
|
||||
image_path = "Tests/images/hopper." + ext
|
||||
with open(image_path, "rb") as fp:
|
||||
subprocess.call(["wl-copy"], stdin=fp)
|
||||
im = ImageGrab.grabclipboard()
|
||||
assert_image_equal_tofile(im, image_path)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
# Documentation: https://docs.codecov.io/docs/codecov-yaml
|
||||
# Documentation: https://docs.codecov.com/docs/codecov-yaml
|
||||
|
||||
codecov:
|
||||
# Avoid "Missing base report" due to committing CHANGES.rst with "[CI skip]"
|
||||
# https://github.com/codecov/support/issues/363
|
||||
# https://docs.codecov.io/docs/comparing-commits
|
||||
# https://docs.codecov.com/docs/comparing-commits
|
||||
allow_coverage_offsets: true
|
||||
|
||||
comment: false
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
from livereload.compiler import shell
|
||||
from livereload.task import Task
|
||||
|
||||
Task.add('*.rst', shell('make html'))
|
||||
Task.add('*/*.rst', shell('make html'))
|
||||
Task.add('Makefile', shell('make html'))
|
||||
Task.add('conf.py', shell('make html'))
|
||||
Task.add("*.rst", shell("make html"))
|
||||
Task.add("*/*.rst", shell("make html"))
|
||||
Task.add("Makefile", shell("make html"))
|
||||
Task.add("conf.py", shell("make html"))
|
||||
|
|
11
docs/conf.py
11
docs/conf.py
|
@ -317,6 +317,17 @@ def setup(app):
|
|||
app.add_css_file("css/dark.css")
|
||||
|
||||
|
||||
linkcheck_allowed_redirects = {
|
||||
r"https://bestpractices.coreinfrastructure.org/projects/6331": r"https://bestpractices.coreinfrastructure.org/en/.*", # noqa: E501
|
||||
r"https://badges.gitter.im/python-pillow/Pillow.svg": r"https://badges.gitter.im/repo.svg", # noqa: E501
|
||||
r"https://gitter.im/python-pillow/Pillow?.*": r"https://app.gitter.im/#/room/#python-pillow_Pillow:gitter.im?.*", # noqa: E501
|
||||
r"https://pillow.readthedocs.io/?badge=latest": r"https://pillow.readthedocs.io/en/stable/?badge=latest", # noqa: E501
|
||||
r"https://pillow.readthedocs.io": r"https://pillow.readthedocs.io/en/stable/",
|
||||
r"https://tidelift.com/badges/package/pypi/Pillow?.*": r"https://img.shields.io/badge/.*", # noqa: E501
|
||||
r"https://zenodo.org/badge/17549/python-pillow/Pillow.svg": r"https://zenodo.org/badge/doi/[\.0-9]+/zenodo.[0-9]+.svg", # noqa: E501
|
||||
r"https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow": r"https://zenodo.org/record/[0-9]+", # noqa: E501
|
||||
}
|
||||
|
||||
# sphinx.ext.extlinks
|
||||
# This config is a dictionary of external sites,
|
||||
# mapping unique short aliases to a base URL and a prefix.
|
||||
|
|
|
@ -210,7 +210,7 @@ open-source users (and will reach EOL on 2023-12-08 for commercial licence holde
|
|||
|
||||
Support for PyQt5 and PySide2 has been removed from ``ImageQt``. Upgrade to
|
||||
`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
|
||||
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
|
||||
`PySide6 <https://doc.qt.io/qtforpython-6/>`_ instead.
|
||||
|
||||
Image.coerce_e
|
||||
~~~~~~~~~~~~~~
|
||||
|
|
|
@ -243,6 +243,7 @@ Methods
|
|||
.. py:method:: ImageDraw.line(xy, fill=None, width=0, joint=None)
|
||||
|
||||
Draws a line between the coordinates in the ``xy`` list.
|
||||
The coordinate pixels are included in the drawn line.
|
||||
|
||||
:param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or
|
||||
numeric values like ``[x, y, x, y, ...]``.
|
||||
|
@ -287,7 +288,7 @@ Methods
|
|||
|
||||
The polygon outline consists of straight lines between the given
|
||||
coordinates, plus a straight line between the last and the first
|
||||
coordinate.
|
||||
coordinate. The coordinate pixels are included in the drawn polygon.
|
||||
|
||||
:param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or
|
||||
numeric values like ``[x, y, x, y, ...]``.
|
||||
|
|
|
@ -15,8 +15,9 @@ or the clipboard to a PIL image memory.
|
|||
returned as an "RGBA" on macOS, or an "RGB" image otherwise.
|
||||
If the bounding box is omitted, the entire screen is copied.
|
||||
|
||||
On Linux, if ``xdisplay`` is ``None`` then ``gnome-screenshot`` will be used if it
|
||||
is installed. To capture the default X11 display instead, pass ``xdisplay=""``.
|
||||
On Linux, if ``xdisplay`` is ``None`` and the default X11 display does not return
|
||||
a snapshot of the screen, ``gnome-screenshot`` will be used as fallback if it is
|
||||
installed. To disable this behaviour, pass ``xdisplay=""`` instead.
|
||||
|
||||
.. versionadded:: 1.1.3 (Windows), 3.0.0 (macOS), 7.1.0 (Linux)
|
||||
|
||||
|
@ -39,9 +40,11 @@ or the clipboard to a PIL image memory.
|
|||
|
||||
.. py:function:: grabclipboard()
|
||||
|
||||
Take a snapshot of the clipboard image, if any. Only macOS and Windows are currently supported.
|
||||
Take a snapshot of the clipboard image, if any.
|
||||
|
||||
.. versionadded:: 1.1.4 (Windows), 3.3.0 (macOS)
|
||||
On Linux, ``wl-paste`` or ``xclip`` is required.
|
||||
|
||||
.. versionadded:: 1.1.4 (Windows), 3.3.0 (macOS), 9.4.0 (Linux)
|
||||
|
||||
:return: On Windows, an image, a list of filenames,
|
||||
or None if the clipboard does not contain image data or filenames.
|
||||
|
@ -49,3 +52,5 @@ or the clipboard to a PIL image memory.
|
|||
|
||||
On Mac, an image,
|
||||
or None if the clipboard does not contain image data.
|
||||
|
||||
On Linux, an image.
|
||||
|
|
|
@ -117,7 +117,7 @@ open-source users (and will reach EOL on 2023-12-08 for commercial licence holde
|
|||
|
||||
Support for PyQt5 and PySide2 has been removed from ``ImageQt``. Upgrade to
|
||||
`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
|
||||
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
|
||||
`PySide6 <https://doc.qt.io/qtforpython-6/>`_ instead.
|
||||
|
||||
Image.coerce_e
|
||||
^^^^^^^^^^^^^^
|
||||
|
@ -160,6 +160,18 @@ TODO
|
|||
Other Changes
|
||||
=============
|
||||
|
||||
Support display_jpeg() in IPython
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In addition to ``display()`` and ``display_png``, ``display_jpeg()`` can now
|
||||
also be used to display images in IPython::
|
||||
|
||||
from PIL import Image
|
||||
from IPython.display import display_jpeg
|
||||
|
||||
im = Image.new("RGB", (100, 100), (255, 0, 0))
|
||||
display_jpeg(im)
|
||||
|
||||
Support reading signed 8-bit TIFF images
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ open-source users (and will reach EOL on 2023-12-08 for commercial licence holde
|
|||
Support for PyQt5 and PySide2 has been deprecated from ``ImageQt`` and will be removed
|
||||
in Pillow 10 (2023-07-01). Upgrade to
|
||||
`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
|
||||
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
|
||||
`PySide6 <https://doc.qt.io/qtforpython-6/>`_ instead.
|
||||
|
||||
FreeTypeFont.getmask2 fill parameter
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
6
setup.py
6
setup.py
|
@ -10,6 +10,7 @@
|
|||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
|
@ -150,6 +151,7 @@ def _dbg(s, tp=None):
|
|||
def _find_library_dirs_ldconfig():
|
||||
# Based on ctypes.util from Python 2
|
||||
|
||||
ldconfig = "ldconfig" if shutil.which("ldconfig") else "/sbin/ldconfig"
|
||||
if sys.platform.startswith("linux") or sys.platform.startswith("gnu"):
|
||||
if struct.calcsize("l") == 4:
|
||||
machine = os.uname()[4] + "-32"
|
||||
|
@ -166,14 +168,14 @@ def _find_library_dirs_ldconfig():
|
|||
|
||||
# Assuming GLIBC's ldconfig (with option -p)
|
||||
# Alpine Linux uses musl that can't print cache
|
||||
args = ["ldconfig", "-p"]
|
||||
args = [ldconfig, "-p"]
|
||||
expr = rf".*\({abi_type}.*\) => (.*)"
|
||||
env = dict(os.environ)
|
||||
env["LC_ALL"] = "C"
|
||||
env["LANG"] = "C"
|
||||
|
||||
elif sys.platform.startswith("freebsd"):
|
||||
args = ["ldconfig", "-r"]
|
||||
args = [ldconfig, "-r"]
|
||||
expr = r".* => (.*)"
|
||||
env = {}
|
||||
|
||||
|
|
|
@ -879,7 +879,7 @@ def _get_palette_bytes(im):
|
|||
:param im: Image object
|
||||
:returns: Bytes, len<=768 suitable for inclusion in gif header
|
||||
"""
|
||||
return im.palette.palette
|
||||
return im.palette.palette if im.palette else b""
|
||||
|
||||
|
||||
def _get_background(im, info_background):
|
||||
|
|
|
@ -22,11 +22,11 @@ import os
|
|||
import struct
|
||||
import sys
|
||||
|
||||
from PIL import Image, ImageFile, PngImagePlugin, features
|
||||
from . import Image, ImageFile, PngImagePlugin, features
|
||||
|
||||
enable_jpeg2k = features.check_codec("jpg_2000")
|
||||
if enable_jpeg2k:
|
||||
from PIL import Jpeg2KImagePlugin
|
||||
from . import Jpeg2KImagePlugin
|
||||
|
||||
MAGIC = b"icns"
|
||||
HEADERSIZE = 8
|
||||
|
|
|
@ -633,19 +633,34 @@ class Image:
|
|||
)
|
||||
)
|
||||
|
||||
def _repr_png_(self):
|
||||
"""iPython display hook support
|
||||
def _repr_image(self, image_format):
|
||||
"""Helper function for iPython display hook.
|
||||
|
||||
:returns: png version of the image as bytes
|
||||
:param image_format: Image format.
|
||||
:returns: image as bytes, saved into the given format.
|
||||
"""
|
||||
b = io.BytesIO()
|
||||
try:
|
||||
self.save(b, "PNG")
|
||||
self.save(b, image_format)
|
||||
except Exception as e:
|
||||
msg = "Could not save to PNG for display"
|
||||
msg = f"Could not save to {image_format} for display"
|
||||
raise ValueError(msg) from e
|
||||
return b.getvalue()
|
||||
|
||||
def _repr_png_(self):
|
||||
"""iPython display hook support for PNG format.
|
||||
|
||||
:returns: PNG version of the image as bytes
|
||||
"""
|
||||
return self._repr_image("PNG")
|
||||
|
||||
def _repr_jpeg_(self):
|
||||
"""iPython display hook support for JPEG format.
|
||||
|
||||
:returns: JPEG version of the image as bytes
|
||||
"""
|
||||
return self._repr_image("JPEG")
|
||||
|
||||
@property
|
||||
def __array_interface__(self):
|
||||
# numpy array interface support
|
||||
|
@ -1108,7 +1123,6 @@ class Image:
|
|||
Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG`
|
||||
(default).
|
||||
:returns: A new image
|
||||
|
||||
"""
|
||||
|
||||
self.load()
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
import sys
|
||||
from enum import IntEnum
|
||||
|
||||
from PIL import Image
|
||||
from . import Image
|
||||
|
||||
try:
|
||||
from PIL import _imagingcms
|
||||
from . import _imagingcms
|
||||
except ImportError as ex:
|
||||
# Allow error import for doc purposes, but error out when accessing
|
||||
# anything in core.
|
||||
|
@ -271,7 +271,7 @@ def get_display_profile(handle=None):
|
|||
if sys.platform != "win32":
|
||||
return None
|
||||
|
||||
from PIL import ImageWin
|
||||
from . import ImageWin
|
||||
|
||||
if isinstance(handle, ImageWin.HDC):
|
||||
profile = core.get_display_profile_win32(handle, 1)
|
||||
|
|
|
@ -314,11 +314,11 @@ class ImageDraw:
|
|||
|
||||
full_x, full_y = False, False
|
||||
if all(corners):
|
||||
full_x = d >= x1 - x0
|
||||
full_x = d >= x1 - x0 - 1
|
||||
if full_x:
|
||||
# The two left and two right corners are joined
|
||||
d = x1 - x0
|
||||
full_y = d >= y1 - y0
|
||||
full_y = d >= y1 - y0 - 1
|
||||
if full_y:
|
||||
# The two top and two bottom corners are joined
|
||||
d = y1 - y0
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
import io
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
@ -61,7 +62,17 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N
|
|||
left, top, right, bottom = bbox
|
||||
im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
|
||||
return im
|
||||
elif shutil.which("gnome-screenshot"):
|
||||
try:
|
||||
if not Image.core.HAVE_XCB:
|
||||
msg = "Pillow was built without XCB support"
|
||||
raise OSError(msg)
|
||||
size, data = Image.core.grabscreen_x11(xdisplay)
|
||||
except OSError:
|
||||
if (
|
||||
xdisplay is None
|
||||
and sys.platform not in ("darwin", "win32")
|
||||
and shutil.which("gnome-screenshot")
|
||||
):
|
||||
fh, filepath = tempfile.mkstemp(".png")
|
||||
os.close(fh)
|
||||
subprocess.call(["gnome-screenshot", "-f", filepath])
|
||||
|
@ -73,15 +84,13 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N
|
|||
im.close()
|
||||
return im_cropped
|
||||
return im
|
||||
# use xdisplay=None for default display on non-win32/macOS systems
|
||||
if not Image.core.HAVE_XCB:
|
||||
msg = "Pillow was built without XCB support"
|
||||
raise OSError(msg)
|
||||
size, data = Image.core.grabscreen_x11(xdisplay)
|
||||
im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1)
|
||||
if bbox:
|
||||
im = im.crop(bbox)
|
||||
return im
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1)
|
||||
if bbox:
|
||||
im = im.crop(bbox)
|
||||
return im
|
||||
|
||||
|
||||
def grabclipboard():
|
||||
|
@ -120,8 +129,6 @@ def grabclipboard():
|
|||
files = data[o:].decode("mbcs").split("\0")
|
||||
return files[: files.index("")]
|
||||
if isinstance(data, bytes):
|
||||
import io
|
||||
|
||||
data = io.BytesIO(data)
|
||||
if fmt == "png":
|
||||
from . import PngImagePlugin
|
||||
|
@ -134,19 +141,29 @@ def grabclipboard():
|
|||
return None
|
||||
else:
|
||||
if shutil.which("wl-paste"):
|
||||
output = subprocess.check_output(["wl-paste", "-l"]).decode()
|
||||
mimetypes = output.splitlines()
|
||||
if "image/png" in mimetypes:
|
||||
mimetype = "image/png"
|
||||
elif mimetypes:
|
||||
mimetype = mimetypes[0]
|
||||
else:
|
||||
mimetype = None
|
||||
|
||||
args = ["wl-paste"]
|
||||
if mimetype:
|
||||
args.extend(["-t", mimetype])
|
||||
elif shutil.which("xclip"):
|
||||
args = ["xclip", "-selection", "clipboard", "-t", "image/png", "-o"]
|
||||
else:
|
||||
msg = "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux"
|
||||
raise NotImplementedError(msg)
|
||||
fh, filepath = tempfile.mkstemp()
|
||||
err = subprocess.run(args, stdout=fh, stderr=subprocess.PIPE).stderr
|
||||
os.close(fh)
|
||||
p = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
err = p.stderr
|
||||
if err:
|
||||
msg = f"{args[0]} error: {err.strip().decode()}"
|
||||
raise ChildProcessError(msg)
|
||||
im = Image.open(filepath)
|
||||
data = io.BytesIO(p.stdout)
|
||||
im = Image.open(data)
|
||||
im.load()
|
||||
os.unlink(filepath)
|
||||
return im
|
||||
|
|
|
@ -17,7 +17,7 @@ import subprocess
|
|||
import sys
|
||||
from shlex import quote
|
||||
|
||||
from PIL import Image
|
||||
from . import Image
|
||||
|
||||
_viewers = []
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ import os
|
|||
import struct
|
||||
import sys
|
||||
|
||||
from PIL import Image, ImageFile
|
||||
from . import Image, ImageFile
|
||||
|
||||
|
||||
def isInt(f):
|
||||
|
@ -191,7 +191,7 @@ class SpiderImageFile(ImageFile.ImageFile):
|
|||
|
||||
# returns a ImageTk.PhotoImage object, after rescaling to 0..255
|
||||
def tkPhotoImage(self):
|
||||
from PIL import ImageTk
|
||||
from . import ImageTk
|
||||
|
||||
return ImageTk.PhotoImage(self.convert2byte(), palette=256)
|
||||
|
||||
|
|
|
@ -132,6 +132,27 @@ getfont(PyObject *self_, PyObject *args, PyObject *kw) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION > 3 || PY_MINOR_VERSION > 11
|
||||
PyConfig config;
|
||||
PyConfig_InitPythonConfig(&config);
|
||||
if (!PyArg_ParseTupleAndKeywords(
|
||||
args,
|
||||
kw,
|
||||
"etf|nsy#n",
|
||||
kwlist,
|
||||
config.filesystem_encoding,
|
||||
&filename,
|
||||
&size,
|
||||
&index,
|
||||
&encoding,
|
||||
&font_bytes,
|
||||
&font_bytes_size,
|
||||
&layout_engine)) {
|
||||
PyConfig_Clear(&config);
|
||||
return NULL;
|
||||
}
|
||||
PyConfig_Clear(&config);
|
||||
#else
|
||||
if (!PyArg_ParseTupleAndKeywords(
|
||||
args,
|
||||
kw,
|
||||
|
@ -147,6 +168,7 @@ getfont(PyObject *self_, PyObject *args, PyObject *kw) {
|
|||
&layout_engine)) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
self = PyObject_New(FontObject, &Font_Type);
|
||||
if (!self) {
|
||||
|
|
|
@ -437,8 +437,14 @@ PyImaging_GrabClipboardWin32(PyObject *self, PyObject *args) {
|
|||
LPCSTR format_names[] = {"DIB", "DIB", "file", "png", NULL};
|
||||
|
||||
if (!OpenClipboard(NULL)) {
|
||||
PyErr_SetString(PyExc_OSError, "failed to open clipboard");
|
||||
return NULL;
|
||||
// Maybe the clipboard is temporarily in use by another process.
|
||||
// Wait and try again
|
||||
Sleep(500);
|
||||
|
||||
if (!OpenClipboard(NULL)) {
|
||||
PyErr_SetString(PyExc_OSError, "failed to open clipboard");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// find best format as set by clipboard owner
|
||||
|
|
|
@ -337,9 +337,9 @@ deps = {
|
|||
"libs": [r"imagequant.lib"],
|
||||
},
|
||||
"harfbuzz": {
|
||||
"url": "https://github.com/harfbuzz/harfbuzz/archive/7.2.0.zip",
|
||||
"filename": "harfbuzz-7.2.0.zip",
|
||||
"dir": "harfbuzz-7.2.0",
|
||||
"url": "https://github.com/harfbuzz/harfbuzz/archive/7.3.0.zip",
|
||||
"filename": "harfbuzz-7.3.0.zip",
|
||||
"dir": "harfbuzz-7.3.0",
|
||||
"license": "COPYING",
|
||||
"build": [
|
||||
*cmds_cmake(
|
||||
|
@ -352,12 +352,12 @@ deps = {
|
|||
"libs": [r"*.lib"],
|
||||
},
|
||||
"fribidi": {
|
||||
"url": "https://github.com/fribidi/fribidi/archive/v1.0.12.zip",
|
||||
"filename": "fribidi-1.0.12.zip",
|
||||
"dir": "fribidi-1.0.12",
|
||||
"url": "https://github.com/fribidi/fribidi/archive/v1.0.13.zip",
|
||||
"filename": "fribidi-1.0.13.zip",
|
||||
"dir": "fribidi-1.0.13",
|
||||
"license": "COPYING",
|
||||
"build": [
|
||||
cmd_copy(r"COPYING", r"{bin_dir}\fribidi-1.0.12-COPYING"),
|
||||
cmd_copy(r"COPYING", r"{bin_dir}\fribidi-1.0.13-COPYING"),
|
||||
cmd_copy(r"{winbuild_dir}\fribidi.cmake", r"CMakeLists.txt"),
|
||||
*cmds_cmake("fribidi"),
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue
Block a user