mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-28 02:04:36 +03:00
Merge pull request #4700 from nulano/features-version
This commit is contained in:
commit
1bc67c9f0f
|
@ -1,4 +1,5 @@
|
||||||
import io
|
import io
|
||||||
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import features
|
from PIL import features
|
||||||
|
@ -21,6 +22,27 @@ def test_check():
|
||||||
assert features.check_feature(feature) == features.check(feature)
|
assert features.check_feature(feature) == features.check(feature)
|
||||||
|
|
||||||
|
|
||||||
|
def test_version():
|
||||||
|
# Check the correctness of the convenience function
|
||||||
|
# and the format of version numbers
|
||||||
|
|
||||||
|
def test(name, function):
|
||||||
|
version = features.version(name)
|
||||||
|
if not features.check(name):
|
||||||
|
assert version is None
|
||||||
|
else:
|
||||||
|
assert function(name) == version
|
||||||
|
if name != "PIL":
|
||||||
|
assert version is None or re.search(r"\d+(\.\d+)*$", version)
|
||||||
|
|
||||||
|
for module in features.modules:
|
||||||
|
test(module, features.version_module)
|
||||||
|
for codec in features.codecs:
|
||||||
|
test(codec, features.version_codec)
|
||||||
|
for feature in features.features:
|
||||||
|
test(feature, features.version_feature)
|
||||||
|
|
||||||
|
|
||||||
@skip_unless_feature("webp")
|
@skip_unless_feature("webp")
|
||||||
def test_webp_transparency():
|
def test_webp_transparency():
|
||||||
assert features.check("transp_webp") != _webp.WebPDecoderBuggyAlpha()
|
assert features.check("transp_webp") != _webp.WebPDecoderBuggyAlpha()
|
||||||
|
@ -37,9 +59,22 @@ def test_webp_anim():
|
||||||
assert features.check("webp_anim") == _webp.HAVE_WEBPANIM
|
assert features.check("webp_anim") == _webp.HAVE_WEBPANIM
|
||||||
|
|
||||||
|
|
||||||
|
@skip_unless_feature("libjpeg_turbo")
|
||||||
|
def test_libjpeg_turbo_version():
|
||||||
|
assert re.search(r"\d+\.\d+\.\d+$", features.version("libjpeg_turbo"))
|
||||||
|
|
||||||
|
|
||||||
|
@skip_unless_feature("libimagequant")
|
||||||
|
def test_libimagequant_version():
|
||||||
|
assert re.search(r"\d+\.\d+\.\d+$", features.version("libimagequant"))
|
||||||
|
|
||||||
|
|
||||||
def test_check_modules():
|
def test_check_modules():
|
||||||
for feature in features.modules:
|
for feature in features.modules:
|
||||||
assert features.check_module(feature) in [True, False]
|
assert features.check_module(feature) in [True, False]
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_codecs():
|
||||||
for feature in features.codecs:
|
for feature in features.codecs:
|
||||||
assert features.check_codec(feature) in [True, False]
|
assert features.check_codec(feature) in [True, False]
|
||||||
|
|
||||||
|
@ -64,6 +99,8 @@ def test_unsupported_codec():
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
features.check_codec(codec)
|
features.check_codec(codec)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
features.version_codec(codec)
|
||||||
|
|
||||||
|
|
||||||
def test_unsupported_module():
|
def test_unsupported_module():
|
||||||
|
@ -72,6 +109,8 @@ def test_unsupported_module():
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
features.check_module(module)
|
features.check_module(module)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
features.version_module(module)
|
||||||
|
|
||||||
|
|
||||||
def test_pilinfo():
|
def test_pilinfo():
|
||||||
|
|
|
@ -2,14 +2,14 @@ import io
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import IcnsImagePlugin, Image
|
from PIL import IcnsImagePlugin, Image, features
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar
|
from .helper import assert_image_equal, assert_image_similar
|
||||||
|
|
||||||
# sample icon file
|
# sample icon file
|
||||||
TEST_FILE = "Tests/images/pillow.icns"
|
TEST_FILE = "Tests/images/pillow.icns"
|
||||||
|
|
||||||
ENABLE_JPEG2K = hasattr(Image.core, "jp2klib_version")
|
ENABLE_JPEG2K = features.check_codec("jpg_2000")
|
||||||
|
|
||||||
|
|
||||||
def test_sanity():
|
def test_sanity():
|
||||||
|
|
|
@ -3,7 +3,7 @@ import re
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import ExifTags, Image, ImageFile, JpegImagePlugin
|
from PIL import ExifTags, Image, ImageFile, JpegImagePlugin, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image,
|
assert_image,
|
||||||
|
@ -41,7 +41,7 @@ class TestFileJpeg:
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
|
|
||||||
# internal version number
|
# internal version number
|
||||||
assert re.search(r"\d+\.\d+$", Image.core.jpeglib_version)
|
assert re.search(r"\d+\.\d+$", features.version_codec("jpg"))
|
||||||
|
|
||||||
with Image.open(TEST_FILE) as im:
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
|
@ -2,7 +2,7 @@ import re
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageFile, Jpeg2KImagePlugin
|
from PIL import Image, ImageFile, Jpeg2KImagePlugin, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
|
@ -35,7 +35,7 @@ def roundtrip(im, **options):
|
||||||
|
|
||||||
def test_sanity():
|
def test_sanity():
|
||||||
# Internal version number
|
# Internal version number
|
||||||
assert re.search(r"\d+\.\d+\.\d+$", Image.core.jp2klib_version)
|
assert re.search(r"\d+\.\d+\.\d+$", features.version_codec("jpg_2000"))
|
||||||
|
|
||||||
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||||
px = im.load()
|
px = im.load()
|
||||||
|
|
|
@ -3,11 +3,12 @@ import io
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from ctypes import c_float
|
from ctypes import c_float
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags
|
from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
|
@ -47,6 +48,9 @@ class LibTiffTestCase:
|
||||||
|
|
||||||
|
|
||||||
class TestFileLibTiff(LibTiffTestCase):
|
class TestFileLibTiff(LibTiffTestCase):
|
||||||
|
def test_version(self):
|
||||||
|
assert re.search(r"\d+\.\d+\.\d+$", features.version_codec("libtiff"))
|
||||||
|
|
||||||
def test_g4_tiff(self, tmp_path):
|
def test_g4_tiff(self, tmp_path):
|
||||||
"""Test the ordinary file path load path"""
|
"""Test the ordinary file path load path"""
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import zlib
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageFile, PngImagePlugin
|
from PIL import Image, ImageFile, PngImagePlugin, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
PillowLeakTestCase,
|
PillowLeakTestCase,
|
||||||
|
@ -73,7 +73,7 @@ class TestFilePng:
|
||||||
def test_sanity(self, tmp_path):
|
def test_sanity(self, tmp_path):
|
||||||
|
|
||||||
# internal version number
|
# internal version number
|
||||||
assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", Image.core.zlib_version)
|
assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", features.version_codec("zlib"))
|
||||||
|
|
||||||
test_file = str(tmp_path / "temp.png")
|
test_file = str(tmp_path / "temp.png")
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import io
|
import io
|
||||||
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, WebPImagePlugin
|
from PIL import Image, WebPImagePlugin, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_similar,
|
assert_image_similar,
|
||||||
|
@ -38,6 +39,7 @@ class TestFileWebp:
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
_webp.WebPDecoderVersion()
|
_webp.WebPDecoderVersion()
|
||||||
_webp.WebPDecoderBuggyAlpha()
|
_webp.WebPDecoderBuggyAlpha()
|
||||||
|
assert re.search(r"\d+\.\d+\.\d+$", features.version_module("webp"))
|
||||||
|
|
||||||
def test_read_rgb(self):
|
def test_read_rgb(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -4,7 +4,7 @@ import re
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageMode
|
from PIL import Image, ImageMode, features
|
||||||
|
|
||||||
from .helper import assert_image, assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image, assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ def test_sanity():
|
||||||
assert list(map(type, v)) == [str, str, str, str]
|
assert list(map(type, v)) == [str, str, str, str]
|
||||||
|
|
||||||
# internal version number
|
# internal version number
|
||||||
assert re.search(r"\d+\.\d+$", ImageCms.core.littlecms_version)
|
assert re.search(r"\d+\.\d+$", features.version_module("littlecms2"))
|
||||||
|
|
||||||
skip_missing()
|
skip_missing()
|
||||||
i = ImageCms.profileToProfile(hopper(), SRGB, SRGB)
|
i = ImageCms.profileToProfile(hopper(), SRGB, SRGB)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import sys
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
|
@ -41,7 +41,7 @@ class TestImageFont:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_class(self):
|
def setup_class(self):
|
||||||
freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version)
|
freetype = distutils.version.StrictVersion(features.version_module("freetype2"))
|
||||||
|
|
||||||
self.metrics = self.METRICS["Default"]
|
self.metrics = self.METRICS["Default"]
|
||||||
for conditions, metrics in self.METRICS.items():
|
for conditions, metrics in self.METRICS.items():
|
||||||
|
@ -68,7 +68,7 @@ class TestImageFont:
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
assert re.search(r"\d+\.\d+\.\d+$", ImageFont.core.freetype2_version)
|
assert re.search(r"\d+\.\d+\.\d+$", features.version_module("freetype2"))
|
||||||
|
|
||||||
def test_font_properties(self):
|
def test_font_properties(self):
|
||||||
ttf = self.get_font()
|
ttf = self.get_font()
|
||||||
|
@ -620,7 +620,7 @@ class TestImageFont:
|
||||||
def test_variation_get(self):
|
def test_variation_get(self):
|
||||||
font = self.get_font()
|
font = self.get_font()
|
||||||
|
|
||||||
freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version)
|
freetype = distutils.version.StrictVersion(features.version_module("freetype2"))
|
||||||
if freetype < "2.9.1":
|
if freetype < "2.9.1":
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
font.get_variation_names()
|
font.get_variation_names()
|
||||||
|
@ -692,7 +692,7 @@ class TestImageFont:
|
||||||
def test_variation_set_by_name(self):
|
def test_variation_set_by_name(self):
|
||||||
font = self.get_font()
|
font = self.get_font()
|
||||||
|
|
||||||
freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version)
|
freetype = distutils.version.StrictVersion(features.version_module("freetype2"))
|
||||||
if freetype < "2.9.1":
|
if freetype < "2.9.1":
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
font.set_variation_by_name("Bold")
|
font.set_variation_by_name("Bold")
|
||||||
|
@ -716,7 +716,7 @@ class TestImageFont:
|
||||||
def test_variation_set_by_axes(self):
|
def test_variation_set_by_axes(self):
|
||||||
font = self.get_font()
|
font = self.get_font()
|
||||||
|
|
||||||
freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version)
|
freetype = distutils.version.StrictVersion(features.version_module("freetype2"))
|
||||||
if freetype < "2.9.1":
|
if freetype < "2.9.1":
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
font.set_variation_by_axes([100])
|
font.set_variation_by_axes([100])
|
||||||
|
|
|
@ -8,6 +8,7 @@ The :py:mod:`PIL.features` module can be used to detect which Pillow features ar
|
||||||
|
|
||||||
.. autofunction:: PIL.features.pilinfo
|
.. autofunction:: PIL.features.pilinfo
|
||||||
.. autofunction:: PIL.features.check
|
.. autofunction:: PIL.features.check
|
||||||
|
.. autofunction:: PIL.features.version
|
||||||
.. autofunction:: PIL.features.get_supported
|
.. autofunction:: PIL.features.get_supported
|
||||||
|
|
||||||
Modules
|
Modules
|
||||||
|
@ -16,28 +17,31 @@ Modules
|
||||||
Support for the following modules can be checked:
|
Support for the following modules can be checked:
|
||||||
|
|
||||||
* ``pil``: The Pillow core module, required for all functionality.
|
* ``pil``: The Pillow core module, required for all functionality.
|
||||||
* ``tkinter``: Tkinter support.
|
* ``tkinter``: Tkinter support. Version number not available.
|
||||||
* ``freetype2``: FreeType font support via :py:func:`PIL.ImageFont.truetype`.
|
* ``freetype2``: FreeType font support via :py:func:`PIL.ImageFont.truetype`.
|
||||||
* ``littlecms2``: LittleCMS 2 support via :py:mod:`PIL.ImageCms`.
|
* ``littlecms2``: LittleCMS 2 support via :py:mod:`PIL.ImageCms`.
|
||||||
* ``webp``: WebP image support.
|
* ``webp``: WebP image support.
|
||||||
|
|
||||||
.. autofunction:: PIL.features.check_module
|
.. autofunction:: PIL.features.check_module
|
||||||
|
.. autofunction:: PIL.features.version_module
|
||||||
.. autofunction:: PIL.features.get_supported_modules
|
.. autofunction:: PIL.features.get_supported_modules
|
||||||
|
|
||||||
Codecs
|
Codecs
|
||||||
------
|
------
|
||||||
|
|
||||||
These are only checked during Pillow compilation.
|
Support for these is only checked during Pillow compilation.
|
||||||
If the required library was uninstalled from the system, the ``pil`` core module may fail to load instead.
|
If the required library was uninstalled from the system, the ``pil`` core module may fail to load instead.
|
||||||
|
Except for ``jpg``, the version number is checked at run-time.
|
||||||
|
|
||||||
Support for the following codecs can be checked:
|
Support for the following codecs can be checked:
|
||||||
|
|
||||||
* ``jpg``: (compile time) Libjpeg support, required for JPEG based image formats.
|
* ``jpg``: (compile time) Libjpeg support, required for JPEG based image formats. Only compile time version number is available.
|
||||||
* ``jpg_2000``: (compile time) OpenJPEG support, required for JPEG 2000 image formats.
|
* ``jpg_2000``: (compile time) OpenJPEG support, required for JPEG 2000 image formats.
|
||||||
* ``zlib``: (compile time) Zlib support, required for zlib compressed formats, such as PNG.
|
* ``zlib``: (compile time) Zlib support, required for zlib compressed formats, such as PNG.
|
||||||
* ``libtiff``: (compile time) LibTIFF support, required for TIFF based image formats.
|
* ``libtiff``: (compile time) LibTIFF support, required for TIFF based image formats.
|
||||||
|
|
||||||
.. autofunction:: PIL.features.check_codec
|
.. autofunction:: PIL.features.check_codec
|
||||||
|
.. autofunction:: PIL.features.version_codec
|
||||||
.. autofunction:: PIL.features.get_supported_codecs
|
.. autofunction:: PIL.features.get_supported_codecs
|
||||||
|
|
||||||
Features
|
Features
|
||||||
|
@ -45,16 +49,18 @@ Features
|
||||||
|
|
||||||
Some of these are only checked during Pillow compilation.
|
Some of these are only checked during Pillow compilation.
|
||||||
If the required library was uninstalled from the system, the relevant module may fail to load instead.
|
If the required library was uninstalled from the system, the relevant module may fail to load instead.
|
||||||
|
Feature version numbers are available only where stated.
|
||||||
|
|
||||||
Support for the following features can be checked:
|
Support for the following features can be checked:
|
||||||
|
|
||||||
* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg.
|
* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. Compile-time version number is available.
|
||||||
* ``transp_webp``: Support for transparency in WebP images.
|
* ``transp_webp``: Support for transparency in WebP images.
|
||||||
* ``webp_mux``: (compile time) Support for EXIF data in WebP images.
|
* ``webp_mux``: (compile time) Support for EXIF data in WebP images.
|
||||||
* ``webp_anim``: (compile time) Support for animated WebP images.
|
* ``webp_anim``: (compile time) Support for animated WebP images.
|
||||||
* ``raqm``: Raqm library, required for ``ImageFont.LAYOUT_RAQM`` in :py:func:`PIL.ImageFont.truetype`.
|
* ``raqm``: Raqm library, required for ``ImageFont.LAYOUT_RAQM`` in :py:func:`PIL.ImageFont.truetype`. Run-time version number is available for Raqm 0.7.0 or newer.
|
||||||
* ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`.
|
* ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`. Run-time version number is available.
|
||||||
* ``xcb``: (compile time) Support for X11 in :py:func:`PIL.ImageGrab.grab` via the XCB library.
|
* ``xcb``: (compile time) Support for X11 in :py:func:`PIL.ImageGrab.grab` via the XCB library.
|
||||||
|
|
||||||
.. autofunction:: PIL.features.check_feature
|
.. autofunction:: PIL.features.check_feature
|
||||||
|
.. autofunction:: PIL.features.version_feature
|
||||||
.. autofunction:: PIL.features.get_supported_features
|
.. autofunction:: PIL.features.get_supported_features
|
||||||
|
|
|
@ -23,10 +23,10 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from PIL import Image, ImageFile, PngImagePlugin
|
from PIL import Image, ImageFile, PngImagePlugin, features
|
||||||
from PIL._binary import i8
|
from PIL._binary import i8
|
||||||
|
|
||||||
enable_jpeg2k = hasattr(Image.core, "jp2klib_version")
|
enable_jpeg2k = features.check_codec("jpg_2000")
|
||||||
if enable_jpeg2k:
|
if enable_jpeg2k:
|
||||||
from PIL import Jpeg2KImagePlugin
|
from PIL import Jpeg2KImagePlugin
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,11 @@ import PIL
|
||||||
from . import Image
|
from . import Image
|
||||||
|
|
||||||
modules = {
|
modules = {
|
||||||
"pil": "PIL._imaging",
|
"pil": ("PIL._imaging", "PILLOW_VERSION"),
|
||||||
"tkinter": "PIL._tkinter_finder",
|
"tkinter": ("PIL._tkinter_finder", None),
|
||||||
"freetype2": "PIL._imagingft",
|
"freetype2": ("PIL._imagingft", "freetype2_version"),
|
||||||
"littlecms2": "PIL._imagingcms",
|
"littlecms2": ("PIL._imagingcms", "littlecms_version"),
|
||||||
"webp": "PIL._webp",
|
"webp": ("PIL._webp", "webpdecoder_version"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ def check_module(feature):
|
||||||
if not (feature in modules):
|
if not (feature in modules):
|
||||||
raise ValueError("Unknown module %s" % feature)
|
raise ValueError("Unknown module %s" % feature)
|
||||||
|
|
||||||
module = modules[feature]
|
module, ver = modules[feature]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
__import__(module)
|
__import__(module)
|
||||||
|
@ -36,6 +36,24 @@ def check_module(feature):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def version_module(feature):
|
||||||
|
"""
|
||||||
|
:param feature: The module to check for.
|
||||||
|
:returns:
|
||||||
|
The loaded version number as a string, or ``None`` if unknown or not available.
|
||||||
|
:raises ValueError: If the module is not defined in this version of Pillow.
|
||||||
|
"""
|
||||||
|
if not check_module(feature):
|
||||||
|
return None
|
||||||
|
|
||||||
|
module, ver = modules[feature]
|
||||||
|
|
||||||
|
if ver is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return getattr(__import__(module, fromlist=[ver]), ver)
|
||||||
|
|
||||||
|
|
||||||
def get_supported_modules():
|
def get_supported_modules():
|
||||||
"""
|
"""
|
||||||
:returns: A list of all supported modules.
|
:returns: A list of all supported modules.
|
||||||
|
@ -43,7 +61,12 @@ def get_supported_modules():
|
||||||
return [f for f in modules if check_module(f)]
|
return [f for f in modules if check_module(f)]
|
||||||
|
|
||||||
|
|
||||||
codecs = {"jpg": "jpeg", "jpg_2000": "jpeg2k", "zlib": "zip", "libtiff": "libtiff"}
|
codecs = {
|
||||||
|
"jpg": ("jpeg", "jpeglib"),
|
||||||
|
"jpg_2000": ("jpeg2k", "jp2klib"),
|
||||||
|
"zlib": ("zip", "zlib"),
|
||||||
|
"libtiff": ("libtiff", "libtiff"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def check_codec(feature):
|
def check_codec(feature):
|
||||||
|
@ -57,11 +80,32 @@ def check_codec(feature):
|
||||||
if feature not in codecs:
|
if feature not in codecs:
|
||||||
raise ValueError("Unknown codec %s" % feature)
|
raise ValueError("Unknown codec %s" % feature)
|
||||||
|
|
||||||
codec = codecs[feature]
|
codec, lib = codecs[feature]
|
||||||
|
|
||||||
return codec + "_encoder" in dir(Image.core)
|
return codec + "_encoder" in dir(Image.core)
|
||||||
|
|
||||||
|
|
||||||
|
def version_codec(feature):
|
||||||
|
"""
|
||||||
|
:param feature: The codec to check for.
|
||||||
|
:returns:
|
||||||
|
The version number as a string, or ``None`` if not available.
|
||||||
|
Checked at compile time for ``jpg``, run-time otherwise.
|
||||||
|
:raises ValueError: If the codec is not defined in this version of Pillow.
|
||||||
|
"""
|
||||||
|
if not check_codec(feature):
|
||||||
|
return None
|
||||||
|
|
||||||
|
codec, lib = codecs[feature]
|
||||||
|
|
||||||
|
version = getattr(Image.core, lib + "_version")
|
||||||
|
|
||||||
|
if feature == "libtiff":
|
||||||
|
return version.split("\n")[0].split("Version ")[1]
|
||||||
|
|
||||||
|
return version
|
||||||
|
|
||||||
|
|
||||||
def get_supported_codecs():
|
def get_supported_codecs():
|
||||||
"""
|
"""
|
||||||
:returns: A list of all supported codecs.
|
:returns: A list of all supported codecs.
|
||||||
|
@ -70,13 +114,13 @@ def get_supported_codecs():
|
||||||
|
|
||||||
|
|
||||||
features = {
|
features = {
|
||||||
"webp_anim": ("PIL._webp", "HAVE_WEBPANIM"),
|
"webp_anim": ("PIL._webp", "HAVE_WEBPANIM", None),
|
||||||
"webp_mux": ("PIL._webp", "HAVE_WEBPMUX"),
|
"webp_mux": ("PIL._webp", "HAVE_WEBPMUX", None),
|
||||||
"transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY"),
|
"transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY", None),
|
||||||
"raqm": ("PIL._imagingft", "HAVE_RAQM"),
|
"raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"),
|
||||||
"libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO"),
|
"libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"),
|
||||||
"libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT"),
|
"libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"),
|
||||||
"xcb": ("PIL._imaging", "HAVE_XCB"),
|
"xcb": ("PIL._imaging", "HAVE_XCB", None),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,7 +135,7 @@ def check_feature(feature):
|
||||||
if feature not in features:
|
if feature not in features:
|
||||||
raise ValueError("Unknown feature %s" % feature)
|
raise ValueError("Unknown feature %s" % feature)
|
||||||
|
|
||||||
module, flag = features[feature]
|
module, flag, ver = features[feature]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
imported_module = __import__(module, fromlist=["PIL"])
|
imported_module = __import__(module, fromlist=["PIL"])
|
||||||
|
@ -100,6 +144,23 @@ def check_feature(feature):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def version_feature(feature):
|
||||||
|
"""
|
||||||
|
:param feature: The feature to check for.
|
||||||
|
:returns: The version number as a string, or ``None`` if not available.
|
||||||
|
:raises ValueError: If the feature is not defined in this version of Pillow.
|
||||||
|
"""
|
||||||
|
if not check_feature(feature):
|
||||||
|
return None
|
||||||
|
|
||||||
|
module, flag, ver = features[feature]
|
||||||
|
|
||||||
|
if ver is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return getattr(__import__(module, fromlist=[ver]), ver)
|
||||||
|
|
||||||
|
|
||||||
def get_supported_features():
|
def get_supported_features():
|
||||||
"""
|
"""
|
||||||
:returns: A list of all supported features.
|
:returns: A list of all supported features.
|
||||||
|
@ -109,9 +170,9 @@ def get_supported_features():
|
||||||
|
|
||||||
def check(feature):
|
def check(feature):
|
||||||
"""
|
"""
|
||||||
:param feature: A module, feature, or codec name.
|
:param feature: A module, codec, or feature name.
|
||||||
:returns:
|
:returns:
|
||||||
``True`` if the module, feature, or codec is available,
|
``True`` if the module, codec, or feature is available,
|
||||||
``False`` or ``None`` otherwise.
|
``False`` or ``None`` otherwise.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -125,6 +186,22 @@ def check(feature):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def version(feature):
|
||||||
|
"""
|
||||||
|
:param feature:
|
||||||
|
The module, codec, or feature to check for.
|
||||||
|
:returns:
|
||||||
|
The version number as a string, or ``None`` if unknown or not available.
|
||||||
|
"""
|
||||||
|
if feature in modules:
|
||||||
|
return version_module(feature)
|
||||||
|
if feature in codecs:
|
||||||
|
return version_codec(feature)
|
||||||
|
if feature in features:
|
||||||
|
return version_feature(feature)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_supported():
|
def get_supported():
|
||||||
"""
|
"""
|
||||||
:returns: A list of all supported modules, features, and codecs.
|
:returns: A list of all supported modules, features, and codecs.
|
||||||
|
@ -187,6 +264,14 @@ def pilinfo(out=None, supported_formats=True):
|
||||||
("xcb", "XCB (X protocol)"),
|
("xcb", "XCB (X protocol)"),
|
||||||
]:
|
]:
|
||||||
if check(name):
|
if check(name):
|
||||||
|
if name == "jpg" and check_feature("libjpeg_turbo"):
|
||||||
|
v = "libjpeg-turbo " + version_feature("libjpeg_turbo")
|
||||||
|
else:
|
||||||
|
v = version(name)
|
||||||
|
if v is not None:
|
||||||
|
t = "compiled for" if name in ("pil", "jpg") else "loaded"
|
||||||
|
print("---", feature, "support ok,", t, v, file=out)
|
||||||
|
else:
|
||||||
print("---", feature, "support ok", file=out)
|
print("---", feature, "support ok", file=out)
|
||||||
else:
|
else:
|
||||||
print("***", feature, "support not installed", file=out)
|
print("***", feature, "support not installed", file=out)
|
||||||
|
|
|
@ -4168,12 +4168,21 @@ setup_module(PyObject* m) {
|
||||||
|
|
||||||
#ifdef LIBJPEG_TURBO_VERSION
|
#ifdef LIBJPEG_TURBO_VERSION
|
||||||
PyModule_AddObject(m, "HAVE_LIBJPEGTURBO", Py_True);
|
PyModule_AddObject(m, "HAVE_LIBJPEGTURBO", Py_True);
|
||||||
|
#define tostr1(a) #a
|
||||||
|
#define tostr(a) tostr1(a)
|
||||||
|
PyDict_SetItemString(d, "libjpeg_turbo_version", PyUnicode_FromString(tostr(LIBJPEG_TURBO_VERSION)));
|
||||||
|
#undef tostr
|
||||||
|
#undef tostr1
|
||||||
#else
|
#else
|
||||||
PyModule_AddObject(m, "HAVE_LIBJPEGTURBO", Py_False);
|
PyModule_AddObject(m, "HAVE_LIBJPEGTURBO", Py_False);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_LIBIMAGEQUANT
|
#ifdef HAVE_LIBIMAGEQUANT
|
||||||
PyModule_AddObject(m, "HAVE_LIBIMAGEQUANT", Py_True);
|
PyModule_AddObject(m, "HAVE_LIBIMAGEQUANT", Py_True);
|
||||||
|
{
|
||||||
|
extern const char* ImagingImageQuantVersion(void);
|
||||||
|
PyDict_SetItemString(d, "imagequant_version", PyUnicode_FromString(ImagingImageQuantVersion()));
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
PyModule_AddObject(m, "HAVE_LIBIMAGEQUANT", Py_False);
|
PyModule_AddObject(m, "HAVE_LIBIMAGEQUANT", Py_False);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1608,6 +1608,7 @@ static int
|
||||||
setup_module(PyObject* m) {
|
setup_module(PyObject* m) {
|
||||||
PyObject *d;
|
PyObject *d;
|
||||||
PyObject *v;
|
PyObject *v;
|
||||||
|
int vn;
|
||||||
|
|
||||||
d = PyModule_GetDict(m);
|
d = PyModule_GetDict(m);
|
||||||
|
|
||||||
|
@ -1622,7 +1623,8 @@ setup_module(PyObject* m) {
|
||||||
|
|
||||||
d = PyModule_GetDict(m);
|
d = PyModule_GetDict(m);
|
||||||
|
|
||||||
v = PyUnicode_FromFormat("%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100);
|
vn = cmsGetEncodedCMMversion();
|
||||||
|
v = PyUnicode_FromFormat("%d.%d", vn / 100, vn % 100);
|
||||||
PyDict_SetItemString(d, "littlecms_version", v);
|
PyDict_SetItemString(d, "littlecms_version", v);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -81,6 +81,7 @@ typedef struct {
|
||||||
|
|
||||||
static PyTypeObject Font_Type;
|
static PyTypeObject Font_Type;
|
||||||
|
|
||||||
|
typedef const char* (*t_raqm_version_string) (void);
|
||||||
typedef bool (*t_raqm_version_atleast)(unsigned int major,
|
typedef bool (*t_raqm_version_atleast)(unsigned int major,
|
||||||
unsigned int minor,
|
unsigned int minor,
|
||||||
unsigned int micro);
|
unsigned int micro);
|
||||||
|
@ -112,6 +113,7 @@ typedef void (*t_raqm_destroy) (raqm_t *rq);
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void* raqm;
|
void* raqm;
|
||||||
int version;
|
int version;
|
||||||
|
t_raqm_version_string version_string;
|
||||||
t_raqm_version_atleast version_atleast;
|
t_raqm_version_atleast version_atleast;
|
||||||
t_raqm_create create;
|
t_raqm_create create;
|
||||||
t_raqm_set_text set_text;
|
t_raqm_set_text set_text;
|
||||||
|
@ -173,6 +175,7 @@ setraqm(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
|
p_raqm.version_string = (t_raqm_version_atleast)dlsym(p_raqm.raqm, "raqm_version_string");
|
||||||
p_raqm.version_atleast = (t_raqm_version_atleast)dlsym(p_raqm.raqm, "raqm_version_atleast");
|
p_raqm.version_atleast = (t_raqm_version_atleast)dlsym(p_raqm.raqm, "raqm_version_atleast");
|
||||||
p_raqm.create = (t_raqm_create)dlsym(p_raqm.raqm, "raqm_create");
|
p_raqm.create = (t_raqm_create)dlsym(p_raqm.raqm, "raqm_create");
|
||||||
p_raqm.set_text = (t_raqm_set_text)dlsym(p_raqm.raqm, "raqm_set_text");
|
p_raqm.set_text = (t_raqm_set_text)dlsym(p_raqm.raqm, "raqm_set_text");
|
||||||
|
@ -206,6 +209,7 @@ setraqm(void)
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
p_raqm.version_string = (t_raqm_version_atleast)GetProcAddress(p_raqm.raqm, "raqm_version_string");
|
||||||
p_raqm.version_atleast = (t_raqm_version_atleast)GetProcAddress(p_raqm.raqm, "raqm_version_atleast");
|
p_raqm.version_atleast = (t_raqm_version_atleast)GetProcAddress(p_raqm.raqm, "raqm_version_atleast");
|
||||||
p_raqm.create = (t_raqm_create)GetProcAddress(p_raqm.raqm, "raqm_create");
|
p_raqm.create = (t_raqm_create)GetProcAddress(p_raqm.raqm, "raqm_create");
|
||||||
p_raqm.set_text = (t_raqm_set_text)GetProcAddress(p_raqm.raqm, "raqm_set_text");
|
p_raqm.set_text = (t_raqm_set_text)GetProcAddress(p_raqm.raqm, "raqm_set_text");
|
||||||
|
@ -1257,6 +1261,9 @@ setup_module(PyObject* m) {
|
||||||
setraqm();
|
setraqm();
|
||||||
v = PyBool_FromLong(!!p_raqm.raqm);
|
v = PyBool_FromLong(!!p_raqm.raqm);
|
||||||
PyDict_SetItemString(d, "HAVE_RAQM", v);
|
PyDict_SetItemString(d, "HAVE_RAQM", v);
|
||||||
|
if (p_raqm.version_string) {
|
||||||
|
PyDict_SetItemString(d, "raqm_version", PyUnicode_FromString(p_raqm.version_string()));
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
13
src/_webp.c
13
src/_webp.c
|
@ -821,6 +821,16 @@ PyObject* WebPDecoderVersion_wrapper() {
|
||||||
return Py_BuildValue("i", WebPGetDecoderVersion());
|
return Py_BuildValue("i", WebPGetDecoderVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Version as string
|
||||||
|
const char*
|
||||||
|
WebPDecoderVersion_str(void)
|
||||||
|
{
|
||||||
|
static char version[20];
|
||||||
|
int version_number = WebPGetDecoderVersion();
|
||||||
|
sprintf(version, "%d.%d.%d", version_number >> 16, (version_number >> 8) % 0x100, version_number % 0x100);
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The version of webp that ships with (0.1.3) Ubuntu 12.04 doesn't handle alpha well.
|
* The version of webp that ships with (0.1.3) Ubuntu 12.04 doesn't handle alpha well.
|
||||||
* Files that are valid with 0.3 are reported as being invalid.
|
* Files that are valid with 0.3 are reported as being invalid.
|
||||||
|
@ -872,10 +882,13 @@ void addTransparencyFlagToModule(PyObject* m) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int setup_module(PyObject* m) {
|
static int setup_module(PyObject* m) {
|
||||||
|
PyObject* d = PyModule_GetDict(m);
|
||||||
addMuxFlagToModule(m);
|
addMuxFlagToModule(m);
|
||||||
addAnimFlagToModule(m);
|
addAnimFlagToModule(m);
|
||||||
addTransparencyFlagToModule(m);
|
addTransparencyFlagToModule(m);
|
||||||
|
|
||||||
|
PyDict_SetItemString(d, "webpdecoder_version", PyUnicode_FromString(WebPDecoderVersion_str()));
|
||||||
|
|
||||||
#ifdef HAVE_WEBPANIM
|
#ifdef HAVE_WEBPANIM
|
||||||
/* Ready object types */
|
/* Ready object types */
|
||||||
if (PyType_Ready(&WebPAnimDecoder_Type) < 0 ||
|
if (PyType_Ready(&WebPAnimDecoder_Type) < 0 ||
|
||||||
|
|
|
@ -113,4 +113,13 @@ err:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char*
|
||||||
|
ImagingImageQuantVersion(void)
|
||||||
|
{
|
||||||
|
static char version[20];
|
||||||
|
int number = liq_version();
|
||||||
|
sprintf(version, "%d.%d.%d", number / 10000, (number / 100) % 100, number % 100);
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -373,7 +373,7 @@ ImagingZipEncodeCleanup(ImagingCodecState state) {
|
||||||
const char*
|
const char*
|
||||||
ImagingZipVersion(void)
|
ImagingZipVersion(void)
|
||||||
{
|
{
|
||||||
return ZLIB_VERSION;
|
return zlibVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue
Block a user