Merge branch 'main' into ImageStat_getextrema_opt

This commit is contained in:
Andreas Florath 2023-12-05 16:39:18 +01:00 committed by GitHub
commit ed03954d9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 122 additions and 140 deletions

View File

@ -22,7 +22,7 @@ JPEGTURBO_VERSION=3.0.1
OPENJPEG_VERSION=2.5.0 OPENJPEG_VERSION=2.5.0
XZ_VERSION=5.4.5 XZ_VERSION=5.4.5
TIFF_VERSION=4.6.0 TIFF_VERSION=4.6.0
LCMS2_VERSION=2.15 LCMS2_VERSION=2.16
if [[ -n "$IS_MACOS" ]]; then if [[ -n "$IS_MACOS" ]]; then
GIFLIB_VERSION=5.1.4 GIFLIB_VERSION=5.1.4
else else

View File

@ -1,12 +1,12 @@
repos: repos:
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.4 rev: v0.1.6
hooks: hooks:
- id: ruff - id: ruff
args: [--fix, --exit-non-zero-on-fix] args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/psf/black-pre-commit-mirror - repo: https://github.com/psf/black-pre-commit-mirror
rev: 23.10.1 rev: 23.11.0
hooks: hooks:
- id: black - id: black
@ -42,12 +42,12 @@ repos:
exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/ exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/
- repo: https://github.com/sphinx-contrib/sphinx-lint - repo: https://github.com/sphinx-contrib/sphinx-lint
rev: v0.8.1 rev: v0.9.0
hooks: hooks:
- id: sphinx-lint - id: sphinx-lint
- repo: https://github.com/tox-dev/pyproject-fmt - repo: https://github.com/tox-dev/pyproject-fmt
rev: 1.4.1 rev: 1.5.3
hooks: hooks:
- id: pyproject-fmt - id: pyproject-fmt

View File

@ -5,7 +5,19 @@ Changelog (Pillow)
10.2.0 (unreleased) 10.2.0 (unreleased)
------------------- -------------------
- Raise ValueError when TrueType font size is not greater than zero #7584 - Added support for reading DX10 BC4 DDS images #7603
[sambvfx, radarhere]
- Optimized ImageStat.Stat.count #7599
[florath]
- Correct PDF palette size when saving #7555
[radarhere]
- Fixed closing file pointer with olefile 0.47 #7594
[radarhere]
- Raise ValueError when TrueType font size is not greater than zero #7584, #7587
[akx, radarhere] [akx, radarhere]
- If absent, do not try to close fp when closing image #7557 - If absent, do not try to close fp when closing image #7557

View File

@ -94,7 +94,6 @@ Released as needed privately to individual vendors for critical security-related
## Source and Binary Distributions ## Source and Binary Distributions
### macOS and Linux
* [ ] Download sdist and wheels from the [GitHub Actions "Wheels" workflow](https://github.com/python-pillow/Pillow/actions/workflows/wheels.yml) * [ ] Download sdist and wheels from the [GitHub Actions "Wheels" workflow](https://github.com/python-pillow/Pillow/actions/workflows/wheels.yml)
and copy into `dist/`. For example using [GitHub CLI](https://github.com/cli/cli): and copy into `dist/`. For example using [GitHub CLI](https://github.com/cli/cli):
```bash ```bash
@ -104,14 +103,6 @@ Released as needed privately to individual vendors for critical security-related
* [ ] Download the Linux aarch64 wheels created by Travis CI from [GitHub releases](https://github.com/python-pillow/Pillow/releases) * [ ] Download the Linux aarch64 wheels created by Travis CI from [GitHub releases](https://github.com/python-pillow/Pillow/releases)
and copy into `dist`. and copy into `dist`.
### Windows
* [ ] Download the artifacts from the [GitHub Actions "Test Windows" workflow](https://github.com/python-pillow/Pillow/actions/workflows/test-windows.yml)
and copy into `dist/`. For example using [GitHub CLI](https://github.com/cli/cli):
```bash
gh run download --dir dist
# select dist-x.y.z
```
## Publicize Release ## Publicize Release
* [ ] Announce release availability via [Twitter](https://twitter.com/pythonpillow) and [Mastodon](https://fosstodon.org/@pillow) e.g. https://twitter.com/PythonPillow/status/1013789184354603010 * [ ] Announce release availability via [Twitter](https://twitter.com/pythonpillow) and [Mastodon](https://fosstodon.org/@pillow) e.g. https://twitter.com/PythonPillow/status/1013789184354603010

Binary file not shown.

BIN
Tests/images/bc4_unorm.dds Normal file

Binary file not shown.

BIN
Tests/images/bc4_unorm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

View File

@ -356,9 +356,7 @@ def test_apng_save(tmp_path):
assert im.getpixel((64, 32)) == (0, 255, 0, 255) assert im.getpixel((64, 32)) == (0, 255, 0, 255)
with Image.open("Tests/images/apng/single_frame_default.png") as im: with Image.open("Tests/images/apng/single_frame_default.png") as im:
frames = [] frames = [frame_im.copy() for frame_im in ImageSequence.Iterator(im)]
for frame_im in ImageSequence.Iterator(im):
frames.append(frame_im.copy())
frames[0].save( frames[0].save(
test_file, save_all=True, default_image=True, append_images=frames[1:] test_file, save_all=True, default_image=True, append_images=frames[1:]
) )

View File

@ -12,6 +12,8 @@ TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds"
TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds" TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds"
TEST_FILE_ATI1 = "Tests/images/ati1.dds" TEST_FILE_ATI1 = "Tests/images/ati1.dds"
TEST_FILE_ATI2 = "Tests/images/ati2.dds" TEST_FILE_ATI2 = "Tests/images/ati2.dds"
TEST_FILE_DX10_BC4_TYPELESS = "Tests/images/bc4_typeless.dds"
TEST_FILE_DX10_BC4_UNORM = "Tests/images/bc4_unorm.dds"
TEST_FILE_DX10_BC5_TYPELESS = "Tests/images/bc5_typeless.dds" TEST_FILE_DX10_BC5_TYPELESS = "Tests/images/bc5_typeless.dds"
TEST_FILE_DX10_BC5_UNORM = "Tests/images/bc5_unorm.dds" TEST_FILE_DX10_BC5_UNORM = "Tests/images/bc5_unorm.dds"
TEST_FILE_DX10_BC5_SNORM = "Tests/images/bc5_snorm.dds" TEST_FILE_DX10_BC5_SNORM = "Tests/images/bc5_snorm.dds"
@ -82,6 +84,27 @@ def test_sanity_ati1():
assert_image_equal_tofile(im, TEST_FILE_ATI1.replace(".dds", ".png")) assert_image_equal_tofile(im, TEST_FILE_ATI1.replace(".dds", ".png"))
@pytest.mark.parametrize(
"image_path",
(
TEST_FILE_DX10_BC4_UNORM,
# hexeditted to be typeless
TEST_FILE_DX10_BC4_TYPELESS,
),
)
def test_dx10_bc4(image_path):
"""Check DX10 BC4 images can be opened"""
with Image.open(image_path) as im:
im.load()
assert im.format == "DDS"
assert im.mode == "L"
assert im.size == (64, 64)
assert_image_equal_tofile(im, TEST_FILE_DX10_BC4_UNORM.replace(".dds", ".png"))
@pytest.mark.parametrize( @pytest.mark.parametrize(
"image_path", "image_path",
( (

View File

@ -521,6 +521,19 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
.. versionadded:: 2.5.0 .. versionadded:: 2.5.0
**streamtype**
Allows storing images without quantization and Huffman tables, or with
these tables but without image data. This is useful for container formats
or network protocols that handle tables separately and share them between
images.
* ``0`` (default): interchange datastream, with tables and image data
* ``1``: abbreviated table specification (tables-only) datastream
.. versionadded:: 10.2.0
* ``2``: abbreviated image (image-only) datastream
**comment** **comment**
A comment about the image. A comment about the image.

View File

@ -95,11 +95,10 @@ and :pypi:`olefile` for Pillow to read FPX and MIC images::
.. tab:: Windows .. tab:: Windows
.. warning:: Pillow > 9.5.0 no longer includes 32-bit wheels. We provide Pillow binaries for Windows compiled for the matrix of supported
Pythons in the wheel format. These include x86, x86-64 and arm64 versions
We provide Pillow binaries for Windows compiled for the matrix of (with the exception of Python 3.8 on arm64). These binaries include support
supported Pythons in 64-bit versions in the wheel format. These binaries include for all optional libraries except libimagequant and libxcb. Raqm support
support for all optional libraries except libimagequant and libxcb. Raqm support
requires FriBiDi to be installed separately:: requires FriBiDi to be installed separately::
python3 -m pip install --upgrade pip python3 -m pip install --upgrade pip
@ -176,7 +175,7 @@ Many of Pillow's features require external libraries:
* **littlecms** provides color management * **littlecms** provides color management
* Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and * 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.15**. above uses liblcms2. Tested with **1.19** and **2.7-2.16**.
* **libwebp** provides the WebP format. * **libwebp** provides the WebP format.

View File

@ -98,6 +98,8 @@ DXT5_FOURCC = 0x35545844
DXGI_FORMAT_R8G8B8A8_TYPELESS = 27 DXGI_FORMAT_R8G8B8A8_TYPELESS = 27
DXGI_FORMAT_R8G8B8A8_UNORM = 28 DXGI_FORMAT_R8G8B8A8_UNORM = 28
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29 DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29
DXGI_FORMAT_BC4_TYPELESS = 79
DXGI_FORMAT_BC4_UNORM = 80
DXGI_FORMAT_BC5_TYPELESS = 82 DXGI_FORMAT_BC5_TYPELESS = 82
DXGI_FORMAT_BC5_UNORM = 83 DXGI_FORMAT_BC5_UNORM = 83
DXGI_FORMAT_BC5_SNORM = 84 DXGI_FORMAT_BC5_SNORM = 84
@ -190,7 +192,11 @@ class DdsImageFile(ImageFile.ImageFile):
# ignoring flags which pertain to volume textures and cubemaps # ignoring flags which pertain to volume textures and cubemaps
(dxgi_format,) = struct.unpack("<I", self.fp.read(4)) (dxgi_format,) = struct.unpack("<I", self.fp.read(4))
self.fp.read(16) self.fp.read(16)
if dxgi_format in (DXGI_FORMAT_BC5_TYPELESS, DXGI_FORMAT_BC5_UNORM): if dxgi_format in (DXGI_FORMAT_BC4_TYPELESS, DXGI_FORMAT_BC4_UNORM):
self.pixel_format = "BC4"
n = 4
self._mode = "L"
elif dxgi_format in (DXGI_FORMAT_BC5_TYPELESS, DXGI_FORMAT_BC5_UNORM):
self.pixel_format = "BC5" self.pixel_format = "BC5"
n = 5 n = 5
self._mode = "RGB" self._mode = "RGB"

View File

@ -97,16 +97,15 @@ class FpxImageFile(ImageFile.ImageFile):
s = prop[0x2000002 | id] s = prop[0x2000002 | id]
colors = []
bands = i32(s, 4) bands = i32(s, 4)
if bands > 4: if bands > 4:
msg = "Invalid number of bands" msg = "Invalid number of bands"
raise OSError(msg) raise OSError(msg)
for i in range(bands):
# note: for now, we ignore the "uncalibrated" flag
colors.append(i32(s, 8 + i * 4) & 0x7FFFFFFF)
self._mode, self.rawmode = MODES[tuple(colors)] # note: for now, we ignore the "uncalibrated" flag
colors = tuple(i32(s, 8 + i * 4) & 0x7FFFFFFF for i in range(bands))
self._mode, self.rawmode = MODES[colors]
# load JPEG tables, if any # load JPEG tables, if any
self.jpeg = {} self.jpeg = {}

View File

@ -1288,9 +1288,9 @@ class Image:
if self.im.bands == 1 or multiband: if self.im.bands == 1 or multiband:
return self._new(filter.filter(self.im)) return self._new(filter.filter(self.im))
ims = [] ims = [
for c in range(self.im.bands): self._new(filter.filter(self.im.getband(c))) for c in range(self.im.bands)
ims.append(self._new(filter.filter(self.im.getband(c)))) ]
return merge(self.mode, ims) return merge(self.mode, ims)
def getbands(self): def getbands(self):
@ -1339,10 +1339,7 @@ class Image:
self.load() self.load()
if self.mode in ("1", "L", "P"): if self.mode in ("1", "L", "P"):
h = self.im.histogram() h = self.im.histogram()
out = [] out = [(h[i], i) for i in range(256) if h[i]]
for i in range(256):
if h[i]:
out.append((h[i], i))
if len(out) > maxcolors: if len(out) > maxcolors:
return None return None
return out return out
@ -1383,10 +1380,7 @@ class Image:
self.load() self.load()
if self.im.bands > 1: if self.im.bands > 1:
extrema = [] return tuple(self.im.getband(i).getextrema() for i in range(self.im.bands))
for i in range(self.im.bands):
extrema.append(self.im.getband(i).getextrema())
return tuple(extrema)
return self.im.getextrema() return self.im.getextrema()
def _getxmp(self, xmp_tags): def _getxmp(self, xmp_tags):

View File

@ -787,11 +787,8 @@ def getProfileInfo(profile):
# info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint # info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint
description = profile.profile.profile_description description = profile.profile.profile_description
cpright = profile.profile.copyright cpright = profile.profile.copyright
arr = [] elements = [element for element in (description, cpright) if element]
for elt in (description, cpright): return "\r\n\r\n".join(elements) + "\r\n\r\n"
if elt:
arr.append(elt)
return "\r\n\r\n".join(arr) + "\r\n\r\n"
except (AttributeError, OSError, TypeError, ValueError) as v: except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v) from v raise PyCMSError(v) from v

View File

@ -188,6 +188,10 @@ class FreeTypeFont:
def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None): def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None):
# FIXME: use service provider instead # FIXME: use service provider instead
if size <= 0:
msg = "font size must be greater than 0"
raise ValueError(msg)
self.path = font self.path = font
self.size = size self.size = size
self.index = index self.index = index
@ -791,10 +795,6 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None):
:exception ValueError: If the font size is not greater than zero. :exception ValueError: If the font size is not greater than zero.
""" """
if size <= 0:
msg = "font size must be greater than 0"
raise ValueError(msg)
def freetype(font): def freetype(font):
return FreeTypeFont(font, size, index, encoding, layout_engine) return FreeTypeFont(font, size, index, encoding, layout_engine)

View File

@ -557,9 +557,7 @@ def invert(image):
:param image: The image to invert. :param image: The image to invert.
:return: An image. :return: An image.
""" """
lut = [] lut = list(range(255, -1, -1))
for i in range(256):
lut.append(255 - i)
return image.point(lut) if image.mode == "1" else _lut(image, lut) return image.point(lut) if image.mode == "1" else _lut(image, lut)
@ -581,10 +579,8 @@ def posterize(image, bits):
:param bits: The number of bits to keep for each channel (1-8). :param bits: The number of bits to keep for each channel (1-8).
:return: An image. :return: An image.
""" """
lut = []
mask = ~(2 ** (8 - bits) - 1) mask = ~(2 ** (8 - bits) - 1)
for i in range(256): lut = [i & mask for i in range(256)]
lut.append(i & mask)
return _lut(image, lut) return _lut(image, lut)

View File

@ -200,21 +200,15 @@ def raw(rawmode, data):
def make_linear_lut(black, white): def make_linear_lut(black, white):
lut = []
if black == 0: if black == 0:
for i in range(256): return [white * i // 255 for i in range(256)]
lut.append(white * i // 255)
else:
msg = "unavailable when black is non-zero" msg = "unavailable when black is non-zero"
raise NotImplementedError(msg) # FIXME raise NotImplementedError(msg) # FIXME
return lut
def make_gamma_lut(exp): def make_gamma_lut(exp):
lut = [] return [int(((i / 255.0) ** exp) * 255.0 + 0.5) for i in range(256)]
for i in range(256):
lut.append(int(((i / 255.0) ** exp) * 255.0 + 0.5))
return lut
def negative(mode="RGB"): def negative(mode="RGB"):
@ -226,9 +220,7 @@ def negative(mode="RGB"):
def random(mode="RGB"): def random(mode="RGB"):
from random import randint from random import randint
palette = [] palette = [randint(0, 255) for _ in range(256 * len(mode))]
for i in range(256 * len(mode)):
palette.append(randint(0, 255))
return ImagePalette(mode, palette) return ImagePalette(mode, palette)

View File

@ -103,12 +103,10 @@ def align8to32(bytes, width, mode):
if not extra_padding: if not extra_padding:
return bytes return bytes
new_data = [] new_data = [
for i in range(len(bytes) // bytes_per_line): bytes[i * bytes_per_line : (i + 1) * bytes_per_line] + b"\x00" * extra_padding
new_data.append( for i in range(len(bytes) // bytes_per_line)
bytes[i * bytes_per_line : (i + 1) * bytes_per_line] ]
+ b"\x00" * extra_padding
)
return b"".join(new_data) return b"".join(new_data)
@ -131,15 +129,11 @@ def _toqclass_helper(im):
format = qt_format.Format_Mono format = qt_format.Format_Mono
elif im.mode == "L": elif im.mode == "L":
format = qt_format.Format_Indexed8 format = qt_format.Format_Indexed8
colortable = [] colortable = [rgb(i, i, i) for i in range(256)]
for i in range(256):
colortable.append(rgb(i, i, i))
elif im.mode == "P": elif im.mode == "P":
format = qt_format.Format_Indexed8 format = qt_format.Format_Indexed8
colortable = []
palette = im.getpalette() palette = im.getpalette()
for i in range(0, len(palette), 3): colortable = [rgb(*palette[i : i + 3]) for i in range(0, len(palette), 3)]
colortable.append(rgb(*palette[i : i + 3]))
elif im.mode == "RGB": elif im.mode == "RGB":
# Populate the 4th channel with 255 # Populate the 4th channel with 255
im = im.convert("RGBA") im = im.convert("RGBA")

View File

@ -21,9 +21,7 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
import functools
import math import math
import operator
class Stat: class Stat:
@ -64,18 +62,11 @@ class Stat:
break break
return res_min, res_max return res_min, res_max
v = [] return [minmax(self.h[i:]) for i in range(0, len(self.h), 256)]
for i in range(0, len(self.h), 256):
v.append(minmax(self.h[i : i + 256]))
return v
def _getcount(self): def _getcount(self):
"""Get total number of pixels in each layer""" """Get total number of pixels in each layer"""
return [sum(self.h[i : i + 256]) for i in range(0, len(self.h), 256)]
v = []
for i in range(0, len(self.h), 256):
v.append(functools.reduce(operator.add, self.h[i : i + 256]))
return v
def _getsum(self): def _getsum(self):
"""Get sum of all pixels in each layer""" """Get sum of all pixels in each layer"""
@ -101,11 +92,7 @@ class Stat:
def _getmean(self): def _getmean(self):
"""Get average pixel level for each layer""" """Get average pixel level for each layer"""
return [self.sum[i] / self.count[i] for i in self.bands]
v = []
for i in self.bands:
v.append(self.sum[i] / self.count[i])
return v
def _getmedian(self): def _getmedian(self):
"""Get median pixel level for each layer""" """Get median pixel level for each layer"""
@ -124,28 +111,18 @@ class Stat:
def _getrms(self): def _getrms(self):
"""Get RMS for each layer""" """Get RMS for each layer"""
return [math.sqrt(self.sum2[i] / self.count[i]) for i in self.bands]
v = []
for i in self.bands:
v.append(math.sqrt(self.sum2[i] / self.count[i]))
return v
def _getvar(self): def _getvar(self):
"""Get variance for each layer""" """Get variance for each layer"""
return [
v = [] (self.sum2[i] - (self.sum[i] ** 2.0) / self.count[i]) / self.count[i]
for i in self.bands: for i in self.bands
n = self.count[i] ]
v.append((self.sum2[i] - (self.sum[i] ** 2.0) / n) / n)
return v
def _getstddev(self): def _getstddev(self):
"""Get standard deviation for each layer""" """Get standard deviation for each layer"""
return [math.sqrt(self.var[i]) for i in self.bands]
v = []
for i in self.bands:
v.append(math.sqrt(self.var[i]))
return v
Global = Stat # compatibility Global = Stat # compatibility

View File

@ -233,9 +233,7 @@ def SOF(self, marker):
# fixup icc profile # fixup icc profile
self.icclist.sort() # sort by sequence number self.icclist.sort() # sort by sequence number
if self.icclist[0][13] == len(self.icclist): if self.icclist[0][13] == len(self.icclist):
profile = [] profile = [p[14:] for p in self.icclist]
for p in self.icclist:
profile.append(p[14:])
icc_profile = b"".join(profile) icc_profile = b"".join(profile)
else: else:
icc_profile = None # wrong number of fragments icc_profile = None # wrong number of fragments

View File

@ -51,10 +51,11 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
# find ACI subfiles with Image members (maybe not the # find ACI subfiles with Image members (maybe not the
# best way to identify MIC files, but what the... ;-) # best way to identify MIC files, but what the... ;-)
self.images = [] self.images = [
for path in self.ole.listdir(): path
if path[1:] and path[0][-4:] == ".ACI" and path[1] == "Image": for path in self.ole.listdir()
self.images.append(path) if path[1:] and path[0][-4:] == ".ACI" and path[1] == "Image"
]
# if we didn't find any images, this is probably not # if we didn't find any images, this is probably not
# an MIC file. # an MIC file.

View File

@ -129,9 +129,8 @@ class PcfFontFile(FontFile.FontFile):
nprops = i32(fp.read(4)) nprops = i32(fp.read(4))
# read property description # read property description
p = [] p = [(i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4))) for _ in range(nprops)]
for i in range(nprops):
p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4))))
if nprops & 3: if nprops & 3:
fp.seek(4 - (nprops & 3), io.SEEK_CUR) # pad fp.seek(4 - (nprops & 3), io.SEEK_CUR) # pad
@ -186,8 +185,6 @@ class PcfFontFile(FontFile.FontFile):
# #
# bitmap data # bitmap data
bitmaps = []
fp, format, i16, i32 = self._getformat(PCF_BITMAPS) fp, format, i16, i32 = self._getformat(PCF_BITMAPS)
nbitmaps = i32(fp.read(4)) nbitmaps = i32(fp.read(4))
@ -196,13 +193,9 @@ class PcfFontFile(FontFile.FontFile):
msg = "Wrong number of bitmaps" msg = "Wrong number of bitmaps"
raise OSError(msg) raise OSError(msg)
offsets = [] offsets = [i32(fp.read(4)) for _ in range(nbitmaps)]
for i in range(nbitmaps):
offsets.append(i32(fp.read(4)))
bitmap_sizes = [] bitmap_sizes = [i32(fp.read(4)) for _ in range(4)]
for i in range(4):
bitmap_sizes.append(i32(fp.read(4)))
# byteorder = format & 4 # non-zero => MSB # byteorder = format & 4 # non-zero => MSB
bitorder = format & 8 # non-zero => MSB bitorder = format & 8 # non-zero => MSB
@ -218,6 +211,7 @@ class PcfFontFile(FontFile.FontFile):
if bitorder: if bitorder:
mode = "1" mode = "1"
bitmaps = []
for i in range(nbitmaps): for i in range(nbitmaps):
xsize, ysize = metrics[i][:2] xsize, ysize = metrics[i][:2]
b, e = offsets[i : i + 2] b, e = offsets[i : i + 2]

View File

@ -96,7 +96,7 @@ def _write_image(im, filename, existing_pdf, image_refs):
dict_obj["ColorSpace"] = [ dict_obj["ColorSpace"] = [
PdfParser.PdfName("Indexed"), PdfParser.PdfName("Indexed"),
PdfParser.PdfName("DeviceRGB"), PdfParser.PdfName("DeviceRGB"),
255, len(palette) // 3 - 1,
PdfParser.PdfBinary(palette), PdfParser.PdfBinary(palette),
] ]
procset = "ImageI" # indexed color procset = "ImageI" # indexed color

View File

@ -238,9 +238,7 @@ def makeSpiderHeader(im):
if nvalues < 23: if nvalues < 23:
return [] return []
hdr = [] hdr = [0.0] * nvalues
for i in range(nvalues):
hdr.append(0.0)
# NB these are Fortran indices # NB these are Fortran indices
hdr[1] = 1.0 # nslice (=1 for an image) hdr[1] = 1.0 # nslice (=1 for an image)

View File

@ -279,10 +279,10 @@ DEPS = {
"libs": [r"objs\{msbuild_arch}\Release Static\freetype.lib"], "libs": [r"objs\{msbuild_arch}\Release Static\freetype.lib"],
}, },
"lcms2": { "lcms2": {
"url": SF_PROJECTS + "/lcms/files/lcms/2.15/lcms2-2.15.tar.gz/download", "url": SF_PROJECTS + "/lcms/files/lcms/2.16/lcms2-2.16.tar.gz/download",
"filename": "lcms2-2.15.tar.gz", "filename": "lcms2-2.16.tar.gz",
"dir": "lcms2-2.15", "dir": "lcms2-2.16",
"license": "COPYING", "license": "LICENSE",
"patch": { "patch": {
r"Projects\VC2022\lcms2_static\lcms2_static.vcxproj": { r"Projects\VC2022\lcms2_static\lcms2_static.vcxproj": {
# default is /MD for x86 and /MT for x64, we need /MD always # default is /MD for x86 and /MT for x64, we need /MD always