Merge remote-tracking branch 'upstream/master' into docs-imageshow

This commit is contained in:
nulano 2020-06-20 13:08:47 +02:00
commit 23b771973d
24 changed files with 321 additions and 135 deletions

View File

@ -52,6 +52,11 @@ jobs:
python-version: ${{ matrix.python-version }}
architecture: ${{ matrix.architecture }}
- name: Set up TCL
if: "contains(matrix.python-version, 'pypy')"
run: Write-Host "::set-env name=TCL_LIBRARY::$env:pythonLocation\tcl\tcl8.5"
shell: pwsh
- name: Print build system information
run: python .github/workflows/system-info.py

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,3 +1,5 @@
import io
import pytest
from PIL import Image, WebPImagePlugin
@ -54,15 +56,10 @@ class TestFileWebp:
# dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm
assert_image_similar_tofile(image, "Tests/images/hopper_webp_bits.ppm", 1.0)
def test_write_rgb(self, tmp_path):
"""
Can we write a RGB mode file to webp without error.
Does it have the bits we expect?
"""
def _roundtrip(self, tmp_path, mode, epsilon, args={}):
temp_file = str(tmp_path / "temp.webp")
hopper(self.rgb_mode).save(temp_file)
hopper(mode).save(temp_file, **args)
with Image.open(temp_file) as image:
assert image.mode == self.rgb_mode
assert image.size == (128, 128)
@ -70,6 +67,7 @@ class TestFileWebp:
image.load()
image.getdata()
if mode == self.rgb_mode:
# generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm
assert_image_similar_tofile(
image, "Tests/images/hopper_webp_write.ppm", 12.0
@ -78,10 +76,29 @@ class TestFileWebp:
# This test asserts that the images are similar. If the average pixel
# difference between the two images is less than the epsilon value,
# then we're going to accept that it's a reasonable lossy version of
# the image. The old lena images for WebP are showing ~16 on
# Ubuntu, the jpegs are showing ~18.
target = hopper(self.rgb_mode)
assert_image_similar(image, target, 12.0)
# the image.
target = hopper(mode)
if mode != self.rgb_mode:
target = target.convert(self.rgb_mode)
assert_image_similar(image, target, epsilon)
def test_write_rgb(self, tmp_path):
"""
Can we write a RGB mode file to webp without error?
Does it have the bits we expect?
"""
self._roundtrip(tmp_path, self.rgb_mode, 12.5)
def test_write_method(self, tmp_path):
self._roundtrip(tmp_path, self.rgb_mode, 12.0, {"method": 6})
buffer_no_args = io.BytesIO()
hopper().save(buffer_no_args, format="WEBP")
buffer_method = io.BytesIO()
hopper().save(buffer_method, format="WEBP", method=6)
assert buffer_no_args.getbuffer() != buffer_method.getbuffer()
def test_write_unsupported_mode_L(self, tmp_path):
"""
@ -89,18 +106,7 @@ class TestFileWebp:
similar to the original file.
"""
temp_file = str(tmp_path / "temp.webp")
hopper("L").save(temp_file)
with Image.open(temp_file) as image:
assert image.mode == self.rgb_mode
assert image.size == (128, 128)
assert image.format == "WEBP"
image.load()
image.getdata()
target = hopper("L").convert(self.rgb_mode)
assert_image_similar(image, target, 10.0)
self._roundtrip(tmp_path, "L", 10.0)
def test_write_unsupported_mode_P(self, tmp_path):
"""
@ -108,18 +114,7 @@ class TestFileWebp:
similar to the original file.
"""
temp_file = str(tmp_path / "temp.webp")
hopper("P").save(temp_file)
with Image.open(temp_file) as image:
assert image.mode == self.rgb_mode
assert image.size == (128, 128)
assert image.format == "WEBP"
image.load()
image.getdata()
target = hopper("P").convert(self.rgb_mode)
assert_image_similar(image, target, 50.0)
self._roundtrip(tmp_path, "P", 50.0)
def test_WebPEncode_with_invalid_args(self):
"""

View File

@ -13,7 +13,6 @@ from .helper import (
assert_image_equal,
assert_image_similar,
assert_image_similar_tofile,
is_mingw,
is_pypy,
is_win32,
skip_unless_feature,
@ -661,7 +660,22 @@ class TestImageFont:
{"name": b"Size", "minimum": 0, "maximum": 300, "default": 0}
]
@pytest.mark.skipif(is_mingw(), reason="epsilon too high for meaningful test")
def _check_text(self, font, path, epsilon):
im = Image.new("RGB", (100, 75), "white")
d = ImageDraw.Draw(im)
d.text((10, 10), "Text", font=font, fill="black")
try:
with Image.open(path) as expected:
assert_image_similar(im, expected, epsilon)
except AssertionError:
if "_adobe" in path:
path = path.replace("_adobe", "_adobe_older_harfbuzz")
with Image.open(path) as expected:
assert_image_similar(im, expected, epsilon)
else:
raise
def test_variation_set_by_name(self):
font = self.get_font()
@ -674,27 +688,18 @@ class TestImageFont:
with pytest.raises(OSError):
font.set_variation_by_name("Bold")
def _check_text(font, path, epsilon):
im = Image.new("RGB", (100, 75), "white")
d = ImageDraw.Draw(im)
d.text((10, 10), "Text", font=font, fill="black")
with Image.open(path) as expected:
assert_image_similar(im, expected, epsilon)
font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf", 36)
_check_text(font, "Tests/images/variation_adobe.png", 11)
self._check_text(font, "Tests/images/variation_adobe.png", 11)
for name in ["Bold", b"Bold"]:
font.set_variation_by_name(name)
_check_text(font, "Tests/images/variation_adobe_name.png", 11)
self._check_text(font, "Tests/images/variation_adobe_name.png", 11)
font = ImageFont.truetype("Tests/fonts/TINY5x3GX.ttf", 36)
_check_text(font, "Tests/images/variation_tiny.png", 40)
self._check_text(font, "Tests/images/variation_tiny.png", 40)
for name in ["200", b"200"]:
font.set_variation_by_name(name)
_check_text(font, "Tests/images/variation_tiny_name.png", 40)
self._check_text(font, "Tests/images/variation_tiny_name.png", 40)
@pytest.mark.skipif(is_mingw(), reason="epsilon too high for meaningful test")
def test_variation_set_by_axes(self):
font = self.get_font()
@ -707,21 +712,13 @@ class TestImageFont:
with pytest.raises(OSError):
font.set_variation_by_axes([500, 50])
def _check_text(font, path, epsilon):
im = Image.new("RGB", (100, 75), "white")
d = ImageDraw.Draw(im)
d.text((10, 10), "Text", font=font, fill="black")
with Image.open(path) as expected:
assert_image_similar(im, expected, epsilon)
font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf", 36)
font.set_variation_by_axes([500, 50])
_check_text(font, "Tests/images/variation_adobe_axes.png", 5.1)
self._check_text(font, "Tests/images/variation_adobe_axes.png", 5.1)
font = ImageFont.truetype("Tests/fonts/TINY5x3GX.ttf", 36)
font.set_variation_by_axes([100])
_check_text(font, "Tests/images/variation_tiny_axes.png", 32.5)
self._check_text(font, "Tests/images/variation_tiny_axes.png", 32.5)
@skip_unless_feature("raqm")

View File

@ -17,19 +17,21 @@ def test_register():
ImageShow._viewers.pop()
def test_viewer_show():
@pytest.mark.parametrize(
"order", [-1, 0],
)
def test_viewer_show(order):
class TestViewer(ImageShow.Viewer):
methodCalled = False
def show_image(self, image, **options):
self.methodCalled = True
return True
viewer = TestViewer()
ImageShow.register(viewer, -1)
ImageShow.register(viewer, order)
for mode in ("1", "I;16", "LA", "RGB", "RGBA"):
with hopper() as im:
viewer.methodCalled = False
with hopper(mode) as im:
assert ImageShow.show(im)
assert viewer.methodCalled

View File

@ -156,7 +156,7 @@ Many of Pillow's features require external libraries:
* **littlecms** provides color management
* Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and
above uses liblcms2. Tested with **1.19** and **2.7-2.9**.
above uses liblcms2. Tested with **1.19** and **2.7-2.11**.
* **libwebp** provides the WebP format.

View File

@ -9,7 +9,7 @@ or the clipboard to a PIL image memory.
.. versionadded:: 1.1.3
.. py:function:: PIL.ImageGrab.grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None)
.. py:function:: grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None)
Take a snapshot of the screen. The pixels inside the bounding box are
returned as an "RGBA" on macOS, or an "RGB" image otherwise.
@ -26,12 +26,15 @@ or the clipboard to a PIL image memory.
.. versionadded:: 6.2.0
:param xdisplay: X11 Display address. Pass ``None`` to grab the default system screen. Pass ``""`` to grab the default X11 screen on Windows or macOS.
:param xdisplay:
X11 Display address. Pass ``None`` to grab the default system screen. Pass ``""`` to grab the default X11 screen on Windows or macOS.
You can check X11 support using :py:func:`PIL.features.check_feature` with ``feature="xcb"``.
.. versionadded:: 7.1.0
:return: An image
.. py:function:: PIL.ImageGrab.grabclipboard()
.. py:function:: grabclipboard()
Take a snapshot of the clipboard image, if any. Only macOS and Windows are currently supported.

View File

@ -0,0 +1,60 @@
.. py:module:: PIL.features
.. py:currentmodule:: PIL.features
:py:mod:`features` Module
==========================
The :py:mod:`PIL.features` module can be used to detect which Pillow features are available on your system.
.. autofunction:: PIL.features.pilinfo
.. autofunction:: PIL.features.check
.. autofunction:: PIL.features.get_supported
Modules
-------
Support for the following modules can be checked:
* ``pil``: The Pillow core module, required for all functionality.
* ``tkinter``: Tkinter support.
* ``freetype2``: FreeType font support via :py:func:`PIL.ImageFont.truetype`.
* ``littlecms2``: LittleCMS 2 support via :py:mod:`PIL.ImageCms`.
* ``webp``: WebP image support.
.. autofunction:: PIL.features.check_module
.. autofunction:: PIL.features.get_supported_modules
Codecs
------
These are only checked during Pillow compilation.
If the required library was uninstalled from the system, the ``pil`` core module may fail to load instead.
Support for the following codecs can be checked:
* ``jpg``: (compile time) Libjpeg support, required for JPEG based 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.
* ``libtiff``: (compile time) LibTIFF support, required for TIFF based image formats.
.. autofunction:: PIL.features.check_codec
.. autofunction:: PIL.features.get_supported_codecs
Features
--------
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.
Support for the following features can be checked:
* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg.
* ``transp_webp``: Support for transparency in WebP images.
* ``webp_mux``: (compile time) Support for EXIF data in 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`.
* ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`.
* ``xcb``: (compile time) Support for X11 in :py:func:`PIL.ImageGrab.grab` via the XCB library.
.. autofunction:: PIL.features.check_feature
.. autofunction:: PIL.features.get_supported_features

View File

@ -32,6 +32,7 @@ Reference
PSDraw
PixelAccess
PyAccess
features
../PIL
plugins
internal_design

View File

@ -0,0 +1,29 @@
7.2.0
-----
API Changes
===========
Replaced TiffImagePlugin DEBUG with logging
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``TiffImagePlugin.DEBUG = True`` has been a way to print various debugging
information when interacting with TIFF images. This has now been removed
in favour of Python's ``logging`` module, already used in other places in the
Pillow source code.
Corrected default offset when writing EXIF data
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Previously, the default ``offset`` argument for
:py:meth:`~PIL.Image.Exif.tobytes` was 0, which did not include the magic
header. It is now 8.
Moved to ImageFileDirectory_v2 in Image.Exif
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Moved from the legacy :py:class:`PIL.TiffImagePlugin.ImageFileDirectory_v1` to
:py:class:`PIL.TiffImagePlugin.ImageFileDirectory_v2` in
:py:class:`PIL.Image.Exif`. This means that Exif RATIONALs and SIGNED_RATIONALs
are now read as :py:class:`PIL.TiffImagePlugin.IFDRational`, instead of as a
tuple with a numerator and a denominator.

View File

@ -6,6 +6,7 @@ Release Notes
.. toctree::
:maxdepth: 2
7.2.0
7.1.2
7.1.1
7.1.0

View File

@ -1051,10 +1051,12 @@ class Image:
of colors.
:param colors: The desired number of colors, <= 256
:param method: 0 = median cut
1 = maximum coverage
2 = fast octree
3 = libimagequant
:param method: ``Image.MEDIANCUT=0`` (median cut),
``Image.MAXCOVERAGE=1`` (maximum coverage),
``Image.FASTOCTREE=2`` (fast octree),
``Image.LIBIMAGEQUANT=3`` (libimagequant; check support using
:py:func:`PIL.features.check_feature`
with ``feature="libimagequant"``).
:param kmeans: Integer
:param palette: Quantize to the palette of given
:py:class:`PIL.Image.Image`.

View File

@ -637,6 +637,11 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None):
encoding of any text provided in subsequent operations.
:param layout_engine: Which layout engine to use, if available:
`ImageFont.LAYOUT_BASIC` or `ImageFont.LAYOUT_RAQM`.
You can check support for Raqm layout using
:py:func:`PIL.features.check_feature` with ``feature="raqm"``.
.. versionadded:: 4.2.0
:return: A font object.
:exception OSError: If the file could not be read.
"""

View File

@ -39,7 +39,7 @@ def register(viewer, order=1):
pass # raised if viewer wasn't a class
if order > 0:
_viewers.append(viewer)
elif order < 0:
else:
_viewers.insert(0, viewer)

View File

@ -23,23 +23,29 @@
import logging
import sys
from cffi import FFI
try:
from cffi import FFI
defs = """
struct Pixel_RGBA {
unsigned char r,g,b,a;
};
struct Pixel_I16 {
unsigned char l,r;
};
"""
ffi = FFI()
ffi.cdef(defs)
except ImportError as ex:
# Allow error import for doc purposes, but error out when accessing
# anything in core.
from ._util import deferred_error
FFI = ffi = deferred_error(ex)
logger = logging.getLogger(__name__)
defs = """
struct Pixel_RGBA {
unsigned char r,g,b,a;
};
struct Pixel_I16 {
unsigned char l,r;
};
"""
ffi = FFI()
ffi.cdef(defs)
class PyAccess:
def __init__(self, img, readonly=False):
vals = dict(img.im.unsafe_ptrs)

View File

@ -314,6 +314,7 @@ def _save(im, fp, filename):
if isinstance(exif, Image.Exif):
exif = exif.tobytes()
xmp = im.encoderinfo.get("xmp", "")
method = im.encoderinfo.get("method", 0)
if im.mode not in _VALID_WEBP_LEGACY_MODES:
alpha = (
@ -331,6 +332,7 @@ def _save(im, fp, filename):
float(quality),
im.mode,
icc_profile,
method,
exif,
xmp,
)

View File

@ -17,6 +17,13 @@ modules = {
def check_module(feature):
"""
Checks if a module is available.
:param feature: The module to check for.
:returns: ``True`` if available, ``False`` otherwise.
:raises ValueError: If the module is not defined in this version of Pillow.
"""
if not (feature in modules):
raise ValueError("Unknown module %s" % feature)
@ -30,6 +37,9 @@ def check_module(feature):
def get_supported_modules():
"""
:returns: A list of all supported modules.
"""
return [f for f in modules if check_module(f)]
@ -37,6 +47,13 @@ codecs = {"jpg": "jpeg", "jpg_2000": "jpeg2k", "zlib": "zip", "libtiff": "libtif
def check_codec(feature):
"""
Checks if a codec is available.
:param feature: The codec to check for.
:returns: ``True`` if available, ``False`` otherwise.
:raises ValueError: If the codec is not defined in this version of Pillow.
"""
if feature not in codecs:
raise ValueError("Unknown codec %s" % feature)
@ -46,6 +63,9 @@ def check_codec(feature):
def get_supported_codecs():
"""
:returns: A list of all supported codecs.
"""
return [f for f in codecs if check_codec(f)]
@ -61,6 +81,13 @@ features = {
def check_feature(feature):
"""
Checks if a feature is available.
:param feature: The feature to check for.
:returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown.
:raises ValueError: If the feature is not defined in this version of Pillow.
"""
if feature not in features:
raise ValueError("Unknown feature %s" % feature)
@ -74,10 +101,20 @@ def check_feature(feature):
def get_supported_features():
"""
:returns: A list of all supported features.
"""
return [f for f in features if check_feature(f)]
def check(feature):
"""
:param feature: A module, feature, or codec name.
:returns:
``True`` if the module, feature, or codec is available,
``False`` or ``None`` otherwise.
"""
if feature in modules:
return check_module(feature)
if feature in codecs:
@ -89,6 +126,10 @@ def check(feature):
def get_supported():
"""
:returns: A list of all supported modules, features, and codecs.
"""
ret = get_supported_modules()
ret.extend(get_supported_features())
ret.extend(get_supported_codecs())
@ -96,6 +137,16 @@ def get_supported():
def pilinfo(out=None, supported_formats=True):
"""
Prints information about this installation of Pillow.
This function can be called with ``python -m PIL``.
:param out:
The output stream to print to. Defaults to ``sys.stdout`` if ``None``.
:param supported_formats:
If ``True``, a list of all supported image file formats will be printed.
"""
if out is None:
out = sys.stdout

View File

@ -545,6 +545,7 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
int height;
int lossless;
float quality_factor;
int method;
uint8_t* rgb;
uint8_t* icc_bytes;
uint8_t* exif_bytes;
@ -556,49 +557,75 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
Py_ssize_t exif_size;
Py_ssize_t xmp_size;
size_t ret_size;
int rgba_mode;
int channels;
int ok;
ImagingSectionCookie cookie;
WebPConfig config;
WebPMemoryWriter writer;
WebPPicture pic;
if (!PyArg_ParseTuple(args, "y#iiifss#s#s#",
if (!PyArg_ParseTuple(args, "y#iiifss#is#s#",
(char**)&rgb, &size, &width, &height, &lossless, &quality_factor, &mode,
&icc_bytes, &icc_size, &exif_bytes, &exif_size, &xmp_bytes, &xmp_size)) {
&icc_bytes, &icc_size, &method, &exif_bytes, &exif_size, &xmp_bytes, &xmp_size)) {
return NULL;
}
if (strcmp(mode, "RGBA")==0){
if (size < width * height * 4){
rgba_mode = strcmp(mode, "RGBA") == 0;
if (!rgba_mode && strcmp(mode, "RGB") != 0) {
Py_RETURN_NONE;
}
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
if (lossless) {
ImagingSectionEnter(&cookie);
ret_size = WebPEncodeLosslessRGBA(rgb, width, height, 4 * width, &output);
ImagingSectionLeave(&cookie);
} else
#endif
{
ImagingSectionEnter(&cookie);
ret_size = WebPEncodeRGBA(rgb, width, height, 4 * width, quality_factor, &output);
ImagingSectionLeave(&cookie);
}
} else if (strcmp(mode, "RGB")==0){
if (size < width * height * 3){
channels = rgba_mode ? 4 : 3;
if (size < width * height * channels) {
Py_RETURN_NONE;
}
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
if (lossless) {
ImagingSectionEnter(&cookie);
ret_size = WebPEncodeLosslessRGB(rgb, width, height, 3 * width, &output);
ImagingSectionLeave(&cookie);
} else
#endif
{
ImagingSectionEnter(&cookie);
ret_size = WebPEncodeRGB(rgb, width, height, 3 * width, quality_factor, &output);
ImagingSectionLeave(&cookie);
// Setup config for this frame
if (!WebPConfigInit(&config)) {
PyErr_SetString(PyExc_RuntimeError, "failed to initialize config!");
return NULL;
}
config.lossless = lossless;
config.quality = quality_factor;
config.method = method;
// Validate the config
if (!WebPValidateConfig(&config)) {
PyErr_SetString(PyExc_ValueError, "invalid configuration");
return NULL;
}
if (!WebPPictureInit(&pic)) {
PyErr_SetString(PyExc_ValueError, "could not initialise picture");
return NULL;
}
pic.width = width;
pic.height = height;
pic.use_argb = 1; // Don't convert RGB pixels to YUV
if (rgba_mode) {
WebPPictureImportRGBA(&pic, rgb, channels * width);
} else {
Py_RETURN_NONE;
WebPPictureImportRGB(&pic, rgb, channels * width);
}
WebPMemoryWriterInit(&writer);
pic.writer = WebPMemoryWrite;
pic.custom_ptr = &writer;
ImagingSectionEnter(&cookie);
ok = WebPEncode(&config, &pic);
ImagingSectionLeave(&cookie);
WebPPictureFree(&pic);
if (!ok) {
PyErr_SetString(PyExc_ValueError, "encoding error");
return NULL;
}
output = writer.mem;
ret_size = writer.size;
#ifndef HAVE_WEBPMUX
if (ret_size > 0) {
PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size);

View File

@ -105,9 +105,9 @@ header = [
# dependencies, listed in order of compilation
deps = {
"libjpeg": {
"url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz",
"filename": "libjpeg-turbo-2.0.3.tar.gz",
"dir": "libjpeg-turbo-2.0.3",
"url": SF_MIRROR + "/project/libjpeg-turbo/2.0.4/libjpeg-turbo-2.0.4.tar.gz",
"filename": "libjpeg-turbo-2.0.4.tar.gz",
"dir": "libjpeg-turbo-2.0.4",
"build": [
cmd_cmake(
[
@ -195,9 +195,9 @@ deps = {
# "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"],
},
"lcms2": {
"url": SF_MIRROR + "/project/lcms/lcms/2.10/lcms2-2.10.tar.gz",
"filename": "lcms2-2.10.tar.gz",
"dir": "lcms2-2.10",
"url": SF_MIRROR + "/project/lcms/lcms/2.11/lcms2-2.11.tar.gz",
"filename": "lcms2-2.11.tar.gz",
"dir": "lcms2-2.11",
"patch": {
r"Projects\VC2017\lcms2_static\lcms2_static.vcxproj": {
# default is /MD for x86 and /MT for x64, we need /MD always
@ -251,9 +251,9 @@ deps = {
"libs": [r"*.lib"],
},
"harfbuzz": {
"url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.4.zip",
"filename": "harfbuzz-2.6.4.zip",
"dir": "harfbuzz-2.6.4",
"url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.7.zip",
"filename": "harfbuzz-2.6.7.zip",
"dir": "harfbuzz-2.6.7",
"build": [
cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"),
cmd_nmake(target="clean"),