Merge branch 'main' into yaff-font-format
This commit is contained in:
commit
98087e32b4
@ -1 +1 @@
|
||||
cibuildwheel==3.4.0
|
||||
cibuildwheel==3.4.1
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
mypy==1.19.1
|
||||
mypy==1.20.2
|
||||
arro3-compute
|
||||
arro3-core
|
||||
IceSpringPySideStubs-PyQt6
|
||||
|
||||
6
.github/dependencies.json
vendored
6
.github/dependencies.json
vendored
@ -3,12 +3,12 @@
|
||||
"bzip2": "1.0.8",
|
||||
"freetype": "2.14.3",
|
||||
"fribidi": "1.0.16",
|
||||
"harfbuzz": "13.2.1",
|
||||
"harfbuzz": "14.2.0",
|
||||
"jpegturbo": "3.1.4.1",
|
||||
"lcms2": "2.18",
|
||||
"lcms2": "2.19",
|
||||
"libavif": "1.4.1",
|
||||
"libimagequant": "4.4.1",
|
||||
"libpng": "1.6.56",
|
||||
"libpng": "1.6.58",
|
||||
"libwebp": "1.6.0",
|
||||
"libxcb": "1.17.0",
|
||||
"openjpeg": "2.5.4",
|
||||
|
||||
405
.github/generate-sbom.py
vendored
405
.github/generate-sbom.py
vendored
@ -26,6 +26,11 @@ def get_version() -> str:
|
||||
return version_file.read_text(encoding="utf-8").split('"')[1]
|
||||
|
||||
|
||||
def load_dep_versions() -> dict[str, str]:
|
||||
deps_file = Path(__file__).parent / "dependencies.json"
|
||||
return json.loads(deps_file.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def sha256_file(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
@ -58,6 +63,7 @@ def generate(version: str) -> dict:
|
||||
purl = f"pkg:pypi/pillow@{version}"
|
||||
root = Path(__file__).parent.parent
|
||||
thirdparty = root / "src" / "thirdparty"
|
||||
versions = load_dep_versions()
|
||||
|
||||
metadata_component = {
|
||||
"bom-ref": purl,
|
||||
@ -79,18 +85,18 @@ def generate(version: str) -> dict:
|
||||
}
|
||||
|
||||
c_extensions = [
|
||||
("PIL._avif", "AVIF image format extension"),
|
||||
(
|
||||
"PIL._imaging",
|
||||
"Core image processing extension "
|
||||
"(decode, encode, map, display, outline, path, libImaging)",
|
||||
),
|
||||
("PIL._imagingft", "FreeType font rendering extension"),
|
||||
("PIL._imagingcms", "LittleCMS2 colour management extension"),
|
||||
("PIL._webp", "WebP image format extension"),
|
||||
("PIL._avif", "AVIF image format extension"),
|
||||
("PIL._imagingtk", "Tk/Tcl display extension"),
|
||||
("PIL._imagingft", "FreeType font rendering extension"),
|
||||
("PIL._imagingmath", "Image math operations extension"),
|
||||
("PIL._imagingmorph", "Image morphology extension"),
|
||||
("PIL._imagingtk", "Tk/Tcl display extension"),
|
||||
("PIL._webp", "WebP image format extension"),
|
||||
]
|
||||
|
||||
ext_components = [
|
||||
@ -107,6 +113,51 @@ def generate(version: str) -> dict:
|
||||
]
|
||||
|
||||
vendored_components = [
|
||||
{
|
||||
"bom-ref": f"{purl}#thirdparty/fribidi-shim",
|
||||
"type": "library",
|
||||
"name": "fribidi-shim",
|
||||
"version": "1.x",
|
||||
"description": "FriBiDi runtime-loading shim "
|
||||
"(vendored in src/thirdparty/fribidi-shim/); "
|
||||
"loads libfribidi dynamically",
|
||||
"licenses": [{"license": {"id": "LGPL-2.1-or-later"}}],
|
||||
"hashes": [
|
||||
{
|
||||
"alg": "SHA-256",
|
||||
"content": sha256_file(thirdparty / "fribidi-shim" / "fribidi.c"),
|
||||
}
|
||||
],
|
||||
"pedigree": {
|
||||
"notes": "Pillow-authored shim; not taken from an upstream project."
|
||||
},
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://github.com/fribidi/fribidi"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:github/python/pythoncapi-compat",
|
||||
"type": "library",
|
||||
"name": "pythoncapi_compat",
|
||||
"description": "Backport header for new CPython C-API functions "
|
||||
"(vendored in src/thirdparty/pythoncapi_compat.h)",
|
||||
"licenses": [{"license": {"id": "0BSD"}}],
|
||||
"hashes": [
|
||||
{
|
||||
"alg": "SHA-256",
|
||||
"content": sha256_file(thirdparty / "pythoncapi_compat.h"),
|
||||
}
|
||||
],
|
||||
"pedigree": {
|
||||
"notes": "Vendored unmodified from upstream python/pythoncapi-compat."
|
||||
},
|
||||
"externalReferences": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/python/pythoncapi-compat",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": f"{purl}#thirdparty/raqm",
|
||||
"type": "library",
|
||||
@ -191,61 +242,99 @@ def generate(version: str) -> dict:
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": f"{purl}#thirdparty/fribidi-shim",
|
||||
"type": "library",
|
||||
"name": "fribidi-shim",
|
||||
"version": "1.x",
|
||||
"description": "FriBiDi runtime-loading shim "
|
||||
"(vendored in src/thirdparty/fribidi-shim/); "
|
||||
"loads libfribidi dynamically",
|
||||
"licenses": [{"license": {"id": "LGPL-2.1-or-later"}}],
|
||||
"hashes": [
|
||||
{
|
||||
"alg": "SHA-256",
|
||||
"content": sha256_file(thirdparty / "fribidi-shim" / "fribidi.c"),
|
||||
}
|
||||
],
|
||||
"pedigree": {
|
||||
"notes": "Pillow-authored shim; not taken from an upstream project."
|
||||
},
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://github.com/fribidi/fribidi"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:github/python/pythoncapi-compat",
|
||||
"type": "library",
|
||||
"name": "pythoncapi_compat",
|
||||
"description": "Backport header for new CPython C-API functions "
|
||||
"(vendored in src/thirdparty/pythoncapi_compat.h)",
|
||||
"licenses": [{"license": {"id": "0BSD"}}],
|
||||
"hashes": [
|
||||
{
|
||||
"alg": "SHA-256",
|
||||
"content": sha256_file(thirdparty / "pythoncapi_compat.h"),
|
||||
}
|
||||
],
|
||||
"pedigree": {
|
||||
"notes": "Vendored unmodified from upstream python/pythoncapi-compat."
|
||||
},
|
||||
"externalReferences": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/python/pythoncapi-compat",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
native_deps = [
|
||||
{
|
||||
"bom-ref": "pkg:generic/freetype2",
|
||||
"type": "library",
|
||||
"name": "FreeType",
|
||||
"version": versions["freetype"],
|
||||
"scope": "optional",
|
||||
"description": "Font rendering (optional, used by PIL._imagingft). "
|
||||
"Required for text/font support.",
|
||||
"licenses": [{"license": {"id": "FTL"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://freetype.org"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://download.savannah.gnu.org/releases/freetype/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/fribidi",
|
||||
"type": "library",
|
||||
"name": "FriBiDi",
|
||||
"version": versions["fribidi"],
|
||||
"scope": "optional",
|
||||
"description": "Unicode bidi algorithm library (optional, "
|
||||
"loaded at runtime by fribidi-shim).",
|
||||
"licenses": [{"license": {"id": "LGPL-2.1-or-later"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://github.com/fribidi/fribidi"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://github.com/fribidi/fribidi/releases",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/harfbuzz",
|
||||
"type": "library",
|
||||
"name": "HarfBuzz",
|
||||
"version": versions["harfbuzz"],
|
||||
"scope": "optional",
|
||||
"description": "Text shaping (optional, required by libraqm "
|
||||
"for complex text layout).",
|
||||
"licenses": [{"license": {"id": "MIT"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://harfbuzz.github.io"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://github.com/harfbuzz/harfbuzz/releases",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/libavif",
|
||||
"type": "library",
|
||||
"name": "libavif",
|
||||
"version": versions["libavif"],
|
||||
"scope": "optional",
|
||||
"description": "AVIF codec (optional, used by PIL._avif).",
|
||||
"licenses": [{"license": {"id": "BSD-2-Clause"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://github.com/AOMediaCodec/libavif"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://github.com/AOMediaCodec/libavif/releases",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/libimagequant",
|
||||
"type": "library",
|
||||
"name": "libimagequant",
|
||||
"version": versions["libimagequant"],
|
||||
"scope": "optional",
|
||||
"description": "Improved colour quantization (optional).",
|
||||
"licenses": [{"license": {"id": "GPL-3.0-or-later"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://pngquant.org/lib/"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://github.com/ImageOptim/libimagequant/tags",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/libjpeg",
|
||||
"type": "library",
|
||||
"name": "libjpeg / libjpeg-turbo",
|
||||
"version": versions["jpegturbo"],
|
||||
"description": "JPEG codec (required by default; disable with "
|
||||
"-C jpeg=disable). Tested with libjpeg 6b/8/9-9d "
|
||||
"and libjpeg-turbo 2-3.",
|
||||
"-C jpeg=disable).",
|
||||
"licenses": [
|
||||
{"license": {"id": "IJG"}},
|
||||
{"license": {"id": "BSD-3-Clause"}},
|
||||
@ -259,24 +348,13 @@ def generate(version: str) -> dict:
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/zlib",
|
||||
"type": "library",
|
||||
"name": "zlib",
|
||||
"description": "Deflate/PNG compression (required by default; "
|
||||
"disable with -C zlib=disable).",
|
||||
"licenses": [{"license": {"id": "Zlib"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://zlib.net"},
|
||||
{"type": "distribution", "url": "https://zlib.net"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/libtiff",
|
||||
"type": "library",
|
||||
"name": "libtiff",
|
||||
"version": versions["tiff"],
|
||||
"scope": "optional",
|
||||
"description": "TIFF codec (optional). Tested with libtiff 4.0-4.7.1.",
|
||||
"description": "TIFF codec (optional).",
|
||||
"licenses": [{"license": {"id": "libtiff"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://libtiff.gitlab.io/libtiff/"},
|
||||
@ -286,42 +364,11 @@ def generate(version: str) -> dict:
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/freetype2",
|
||||
"type": "library",
|
||||
"name": "FreeType",
|
||||
"scope": "optional",
|
||||
"description": "Font rendering (optional, used by PIL._imagingft). "
|
||||
"Required for text/font support.",
|
||||
"licenses": [{"license": {"id": "FTL"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://freetype.org"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://download.savannah.gnu.org/releases/freetype/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/littlecms2",
|
||||
"type": "library",
|
||||
"name": "Little CMS 2",
|
||||
"scope": "optional",
|
||||
"description": "Colour management (optional, used by PIL._imagingcms). "
|
||||
"Tested with lcms2 2.7-2.18.",
|
||||
"licenses": [{"license": {"id": "MIT"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://www.littlecms.com"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://github.com/mm2/Little-CMS/releases",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/libwebp",
|
||||
"type": "library",
|
||||
"name": "libwebp",
|
||||
"version": versions["libwebp"],
|
||||
"scope": "optional",
|
||||
"description": "WebP codec (optional, used by PIL._webp).",
|
||||
"licenses": [{"license": {"id": "BSD-3-Clause"}}],
|
||||
@ -336,90 +383,11 @@ def generate(version: str) -> dict:
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/openjpeg",
|
||||
"type": "library",
|
||||
"name": "OpenJPEG",
|
||||
"scope": "optional",
|
||||
"description": "JPEG 2000 codec (optional). "
|
||||
"Tested with openjpeg 2.0.0-2.5.4.",
|
||||
"licenses": [{"license": {"id": "BSD-2-Clause"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://www.openjpeg.org"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://github.com/uclouvain/openjpeg/releases",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/libavif",
|
||||
"type": "library",
|
||||
"name": "libavif",
|
||||
"scope": "optional",
|
||||
"description": "AVIF codec (optional, used by PIL._avif). "
|
||||
"Requires libavif >= 1.0.0.",
|
||||
"licenses": [{"license": {"id": "BSD-2-Clause"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://github.com/AOMediaCodec/libavif"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://github.com/AOMediaCodec/libavif/releases",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/harfbuzz",
|
||||
"type": "library",
|
||||
"name": "HarfBuzz",
|
||||
"scope": "optional",
|
||||
"description": "Text shaping (optional, required by libraqm "
|
||||
"for complex text layout).",
|
||||
"licenses": [{"license": {"id": "MIT"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://harfbuzz.github.io"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://github.com/harfbuzz/harfbuzz/releases",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/fribidi",
|
||||
"type": "library",
|
||||
"name": "FriBiDi",
|
||||
"scope": "optional",
|
||||
"description": "Unicode bidi algorithm library (optional, "
|
||||
"loaded at runtime by fribidi-shim).",
|
||||
"licenses": [{"license": {"id": "LGPL-2.1-or-later"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://github.com/fribidi/fribidi"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://github.com/fribidi/fribidi/releases",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/libimagequant",
|
||||
"type": "library",
|
||||
"name": "libimagequant",
|
||||
"scope": "optional",
|
||||
"description": "Improved colour quantization (optional). "
|
||||
"Tested with 2.6-4.4.1.",
|
||||
"licenses": [{"license": {"id": "GPL-3.0-or-later"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://pngquant.org/lib/"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://github.com/ImageOptim/libimagequant/tags",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/libxcb",
|
||||
"type": "library",
|
||||
"name": "libxcb",
|
||||
"version": versions["libxcb"],
|
||||
"scope": "optional",
|
||||
"description": "X11 screen-grab support (optional, "
|
||||
"used by PIL._imaging on macOS and Linux).",
|
||||
@ -432,6 +400,38 @@ def generate(version: str) -> dict:
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/littlecms2",
|
||||
"type": "library",
|
||||
"name": "Little CMS 2",
|
||||
"version": versions["lcms2"],
|
||||
"scope": "optional",
|
||||
"description": "Colour management (optional, used by PIL._imagingcms).",
|
||||
"licenses": [{"license": {"id": "MIT"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://www.littlecms.com"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://github.com/mm2/Little-CMS/releases",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/openjpeg",
|
||||
"type": "library",
|
||||
"name": "OpenJPEG",
|
||||
"version": versions["openjpeg"],
|
||||
"scope": "optional",
|
||||
"description": "JPEG 2000 codec (optional).",
|
||||
"licenses": [{"license": {"id": "BSD-2-Clause"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://www.openjpeg.org"},
|
||||
{
|
||||
"type": "distribution",
|
||||
"url": "https://github.com/uclouvain/openjpeg/releases",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:pypi/pybind11",
|
||||
"type": "library",
|
||||
@ -447,6 +447,19 @@ def generate(version: str) -> dict:
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:generic/zlib",
|
||||
"type": "library",
|
||||
"name": "zlib",
|
||||
"version": versions["zlib-ng"],
|
||||
"description": "Deflate/PNG compression (required by default; "
|
||||
"disable with -C zlib=disable).",
|
||||
"licenses": [{"license": {"id": "Zlib"}}],
|
||||
"externalReferences": [
|
||||
{"type": "website", "url": "https://zlib.net"},
|
||||
{"type": "distribution", "url": "https://zlib.net"},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
@ -455,43 +468,43 @@ def generate(version: str) -> dict:
|
||||
"dependsOn": [e["bom-ref"] for e in ext_components],
|
||||
},
|
||||
{
|
||||
"ref": f"{purl}#c-ext/PIL._imaging",
|
||||
"dependsOn": [
|
||||
"pkg:generic/libjpeg",
|
||||
"pkg:generic/zlib",
|
||||
"pkg:generic/libtiff",
|
||||
"pkg:generic/openjpeg",
|
||||
"pkg:generic/libimagequant",
|
||||
"pkg:generic/libxcb",
|
||||
],
|
||||
"ref": f"{purl}#c-ext/PIL._avif",
|
||||
"dependsOn": ["pkg:generic/libavif"],
|
||||
},
|
||||
{
|
||||
"ref": f"{purl}#c-ext/PIL._imagingft",
|
||||
"ref": f"{purl}#c-ext/PIL._imaging",
|
||||
"dependsOn": [
|
||||
"pkg:generic/freetype2",
|
||||
f"{purl}#thirdparty/raqm",
|
||||
f"{purl}#thirdparty/fribidi-shim",
|
||||
"pkg:generic/harfbuzz",
|
||||
"pkg:generic/fribidi",
|
||||
"pkg:generic/libimagequant",
|
||||
"pkg:generic/libjpeg",
|
||||
"pkg:generic/libtiff",
|
||||
"pkg:generic/libxcb",
|
||||
"pkg:generic/openjpeg",
|
||||
"pkg:generic/zlib",
|
||||
],
|
||||
},
|
||||
{
|
||||
"ref": f"{purl}#c-ext/PIL._imagingcms",
|
||||
"dependsOn": ["pkg:generic/littlecms2"],
|
||||
},
|
||||
{
|
||||
"ref": f"{purl}#c-ext/PIL._imagingft",
|
||||
"dependsOn": [
|
||||
"pkg:generic/freetype2",
|
||||
"pkg:generic/fribidi",
|
||||
"pkg:generic/harfbuzz",
|
||||
f"{purl}#thirdparty/fribidi-shim",
|
||||
f"{purl}#thirdparty/raqm",
|
||||
],
|
||||
},
|
||||
{
|
||||
"ref": f"{purl}#c-ext/PIL._webp",
|
||||
"dependsOn": ["pkg:generic/libwebp"],
|
||||
},
|
||||
{
|
||||
"ref": f"{purl}#c-ext/PIL._avif",
|
||||
"dependsOn": ["pkg:generic/libavif"],
|
||||
},
|
||||
{
|
||||
"ref": f"{purl}#thirdparty/raqm",
|
||||
"dependsOn": [
|
||||
f"{purl}#thirdparty/fribidi-shim",
|
||||
"pkg:generic/harfbuzz",
|
||||
f"{purl}#thirdparty/fribidi-shim",
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
4
.github/workflows/cifuzz.yml
vendored
4
.github/workflows/cifuzz.yml
vendored
@ -30,14 +30,14 @@ jobs:
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@e41e2f295eb18d630932fdd33d072527ba74c87b # master
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@c11174f47deee98f260dede5d661614bda78ae39 # master
|
||||
with:
|
||||
oss-fuzz-project-name: 'pillow'
|
||||
language: python
|
||||
dry-run: false
|
||||
- name: Run Fuzzers
|
||||
id: run
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@e41e2f295eb18d630932fdd33d072527ba74c87b # master
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@c11174f47deee98f260dede5d661614bda78ae39 # master
|
||||
with:
|
||||
oss-fuzz-project-name: 'pillow'
|
||||
fuzz-seconds: 600
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
||||
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
|
||||
- name: Lint
|
||||
run: uvx --with tox-uv tox -e lint
|
||||
- name: Mypy
|
||||
|
||||
1
.github/workflows/test-docker.yml
vendored
1
.github/workflows/test-docker.yml
vendored
@ -45,6 +45,7 @@ jobs:
|
||||
debian-13-trixie-x86,
|
||||
debian-13-trixie-amd64,
|
||||
fedora-43-amd64,
|
||||
fedora-44-amd64,
|
||||
gentoo,
|
||||
ubuntu-22.04-jammy-amd64,
|
||||
ubuntu-24.04-noble-amd64,
|
||||
|
||||
8
.github/workflows/wheels.yml
vendored
8
.github/workflows/wheels.yml
vendored
@ -270,7 +270,7 @@ jobs:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
- name: Upload wheels to scientific-python-nightly-wheels
|
||||
uses: scientific-python/upload-nightly-action@5748273c71e2d8d3a61f3a11a16421c8954f9ecf # 0.6.3
|
||||
uses: scientific-python/upload-nightly-action@e76cfec8a4611fd02808a801b0ff5a7d7c1b2d99 # 0.6.4
|
||||
with:
|
||||
artifacts_path: dist
|
||||
anaconda_nightly_upload_token: ${{ secrets.ANACONDA_ORG_UPLOAD_TOKEN }}
|
||||
@ -294,12 +294,12 @@ jobs:
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: sbom
|
||||
path: "*.cdx.json"
|
||||
path: "pillow-*.cdx.json"
|
||||
|
||||
- name: Validate SBOM
|
||||
run: |
|
||||
python3 -m pip install -r .ci/requirements-sbom.txt
|
||||
check-jsonschema --schemafile "https://raw.githubusercontent.com/CycloneDX/specification/1.7/schema/bom-1.7.schema.json" *.cdx.json
|
||||
check-jsonschema --schemafile "https://raw.githubusercontent.com/CycloneDX/specification/1.7/schema/bom-1.7.schema.json" pillow-*.cdx.json
|
||||
|
||||
sbom-publish:
|
||||
if: |
|
||||
@ -320,7 +320,7 @@ jobs:
|
||||
- name: Attach SBOM to GitHub release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh release upload "$GITHUB_REF_NAME" *.cdx.json
|
||||
run: gh release upload "$GITHUB_REF_NAME" pillow-*.cdx.json
|
||||
|
||||
pypi-publish:
|
||||
if: github.event.repository.fork == false && github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -97,3 +97,6 @@ pillow-test-images.zip
|
||||
|
||||
# pyinstaller
|
||||
*.spec
|
||||
|
||||
# Generated SBOM
|
||||
pillow-*.cdx.json
|
||||
|
||||
@ -502,8 +502,9 @@ class TestFilePng:
|
||||
im = roundtrip(im)
|
||||
assert im.info["transparency"] == (248, 248, 248)
|
||||
|
||||
im = roundtrip(im, transparency=(0, 1, 2))
|
||||
assert im.info["transparency"] == (0, 1, 2)
|
||||
for transparency in ((0, 1, 2), [0, 1, 2]):
|
||||
im = roundtrip(im, transparency=transparency)
|
||||
assert im.info["transparency"] == (0, 1, 2)
|
||||
|
||||
def test_trns_p(self, tmp_path: Path) -> None:
|
||||
# Check writing a transparency of 0, issue #528
|
||||
@ -518,6 +519,36 @@ class TestFilePng:
|
||||
|
||||
assert_image_equal(im2.convert("RGBA"), im.convert("RGBA"))
|
||||
|
||||
def test_trns_invalid(self, tmp_path: Path) -> None:
|
||||
out = tmp_path / "temp.png"
|
||||
|
||||
for mode in ("1", "L", "I;16"):
|
||||
im = Image.new(mode, (1, 1))
|
||||
with pytest.raises(
|
||||
ValueError, match=f"transparency for {mode} must be an integer"
|
||||
):
|
||||
im.save(out, transparency="invalid")
|
||||
|
||||
im = Image.new("I", (1, 1))
|
||||
with pytest.warns(DeprecationWarning, match="Saving I mode images as PNG"):
|
||||
with pytest.raises(ValueError):
|
||||
im.save(out, transparency="invalid")
|
||||
|
||||
im = Image.new("P", (1, 1))
|
||||
with pytest.raises(
|
||||
ValueError, match="transparency for P must be an integer or bytes"
|
||||
):
|
||||
im.save(out, transparency="invalid")
|
||||
|
||||
im = Image.new("RGB", (1, 1))
|
||||
with pytest.raises(
|
||||
ValueError, match="transparency for RGB must be list or tuple"
|
||||
):
|
||||
im.save(out, transparency="invalid")
|
||||
|
||||
with pytest.raises(ValueError, match="transparency for RGB must have length 3"):
|
||||
im.save(out, transparency=(1, 2))
|
||||
|
||||
def test_trns_null(self) -> None:
|
||||
# Check reading images with null tRNS value, issue #1239
|
||||
test_file = "Tests/images/tRNS_null_1x1.png"
|
||||
|
||||
@ -862,7 +862,7 @@ class TestImage:
|
||||
def test_exif_webp(self, tmp_path: Path) -> None:
|
||||
with Image.open("Tests/images/hopper.webp") as im:
|
||||
exif = im.getexif()
|
||||
assert exif == {}
|
||||
assert dict(exif) == {}
|
||||
|
||||
out = tmp_path / "temp.webp"
|
||||
exif[258] = 8
|
||||
@ -884,7 +884,7 @@ class TestImage:
|
||||
def test_exif_png(self, tmp_path: Path) -> None:
|
||||
with Image.open("Tests/images/exif.png") as im:
|
||||
exif = im.getexif()
|
||||
assert exif == {274: 1}
|
||||
assert dict(exif) == {274: 1}
|
||||
|
||||
out = tmp_path / "temp.png"
|
||||
exif[258] = 8
|
||||
|
||||
@ -627,3 +627,37 @@ class TestCoreResampleBox:
|
||||
0.4,
|
||||
f">>> {size} {box} {flt}",
|
||||
)
|
||||
|
||||
|
||||
class TestCoreResample16bpc:
|
||||
# Lanczos weighting during downsampling can push accumulated float sums
|
||||
@pytest.mark.parametrize(
|
||||
"offset",
|
||||
(
|
||||
# below 0. These must be clamped to 0, not corrupted byte-by-byte.
|
||||
0, # Left half = 65535, right half = 0
|
||||
# above 65535. These must be clamped to 65535, not corrupted byte-by-byte.
|
||||
50, # # Left half = 0, right half = 65535
|
||||
),
|
||||
)
|
||||
def test_resampling_clamp_overflow(self, offset: int) -> None:
|
||||
ims = {}
|
||||
width, height = 100, 10
|
||||
for mode in ("I;16", "F"):
|
||||
im = Image.new(mode, (width, height))
|
||||
im.paste(65535, (offset, 0, offset + width // 2, height))
|
||||
|
||||
# 5x downsampling with Lanczos
|
||||
# creates ~8.7% overshoot or undershoot at the step edge
|
||||
ims[mode] = im.resize((20, height), Image.Resampling.LANCZOS)
|
||||
|
||||
for y in range(height):
|
||||
for x in range(20):
|
||||
v = ims["F"].getpixel((x, y))
|
||||
assert isinstance(v, float)
|
||||
expected = max(0, min(65535, round(v)))
|
||||
|
||||
value = ims["I;16"].getpixel((x, y))
|
||||
assert (
|
||||
value == expected
|
||||
), f"Pixel ({x}, {y}): expected {expected}, got {value}"
|
||||
|
||||
@ -51,7 +51,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.18**.
|
||||
above uses liblcms2. Tested with **1.19** and **2.7-2.19**.
|
||||
|
||||
* **libwebp** provides the WebP format.
|
||||
|
||||
|
||||
@ -31,6 +31,8 @@ These platforms are built and tested for every change.
|
||||
+----------------------------------+----------------------------+---------------------+
|
||||
| Fedora 43 | 3.14 | x86-64 |
|
||||
+----------------------------------+----------------------------+---------------------+
|
||||
| Fedora 44 | 3.14 | x86-64 |
|
||||
+----------------------------------+----------------------------+---------------------+
|
||||
| Gentoo | 3.13 | x86-64 |
|
||||
+----------------------------------+----------------------------+---------------------+
|
||||
| macOS 15 Sequoia | 3.11, 3.12, 3.13, 3.14, | arm64 |
|
||||
|
||||
@ -2639,11 +2639,8 @@ class Image:
|
||||
if is_path(fp):
|
||||
filename = os.fspath(fp)
|
||||
open_fp = True
|
||||
elif fp == sys.stdout:
|
||||
try:
|
||||
fp = sys.stdout.buffer
|
||||
except AttributeError:
|
||||
pass
|
||||
elif fp == sys.stdout and isinstance(sys.stdout, io.TextIOWrapper):
|
||||
fp = sys.stdout.buffer
|
||||
if not filename and hasattr(fp, "name") and is_path(fp.name):
|
||||
# only set the name for metadata purposes
|
||||
filename = os.fspath(fp.name)
|
||||
|
||||
@ -383,7 +383,7 @@ class PdfParser:
|
||||
msg = "specify buf or f or filename, but not both buf and f"
|
||||
raise RuntimeError(msg)
|
||||
self.filename = filename
|
||||
self.buf: bytes | bytearray | mmap.mmap | None = buf
|
||||
self.buf: bytes | bytearray | memoryview | mmap.mmap | None = buf
|
||||
self.f = f
|
||||
self.start_offset = start_offset
|
||||
self.should_close_buf = False
|
||||
@ -435,7 +435,9 @@ class PdfParser:
|
||||
self.seek_end()
|
||||
|
||||
def close_buf(self) -> None:
|
||||
if isinstance(self.buf, mmap.mmap):
|
||||
if isinstance(self.buf, memoryview):
|
||||
self.buf.release()
|
||||
elif isinstance(self.buf, mmap.mmap):
|
||||
self.buf.close()
|
||||
self.buf = None
|
||||
|
||||
|
||||
@ -1443,35 +1443,47 @@ def _save(
|
||||
palette_bytes += b"\0"
|
||||
chunk(fp, b"PLTE", palette_bytes)
|
||||
|
||||
transparency = im.encoderinfo.get("transparency", im.info.get("transparency", None))
|
||||
transparency = im.encoderinfo.get("transparency", im.info.get("transparency"))
|
||||
|
||||
if transparency or transparency == 0:
|
||||
if transparency is not None:
|
||||
if im.mode == "P":
|
||||
# limit to actual palette size
|
||||
alpha_bytes = colors
|
||||
if isinstance(transparency, bytes):
|
||||
chunk(fp, b"tRNS", transparency[:alpha_bytes])
|
||||
else:
|
||||
elif isinstance(transparency, int):
|
||||
transparency = max(0, min(255, transparency))
|
||||
alpha = b"\xff" * transparency + b"\0"
|
||||
chunk(fp, b"tRNS", alpha[:alpha_bytes])
|
||||
else:
|
||||
msg = "transparency for P must be an integer or bytes"
|
||||
raise ValueError(msg)
|
||||
elif im.mode in ("1", "L", "I", "I;16"):
|
||||
transparency = max(0, min(65535, transparency))
|
||||
chunk(fp, b"tRNS", o16(transparency))
|
||||
if isinstance(transparency, int):
|
||||
transparency = max(0, min(65535, transparency))
|
||||
chunk(fp, b"tRNS", o16(transparency))
|
||||
else:
|
||||
msg = f"transparency for {im.mode} must be an integer"
|
||||
raise ValueError(msg)
|
||||
elif im.mode == "RGB":
|
||||
red, green, blue = transparency
|
||||
chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue))
|
||||
else:
|
||||
if "transparency" in im.encoderinfo:
|
||||
# don't bother with transparency if it's an RGBA
|
||||
# and it's in the info dict. It's probably just stale.
|
||||
msg = "cannot use transparency for this mode"
|
||||
raise OSError(msg)
|
||||
else:
|
||||
if im.mode == "P" and im.im.getpalettemode() == "RGBA":
|
||||
alpha = im.im.getpalette("RGBA", "A")
|
||||
alpha_bytes = colors
|
||||
chunk(fp, b"tRNS", alpha[:alpha_bytes])
|
||||
if not isinstance(transparency, (list, tuple)):
|
||||
msg = "transparency for RGB must be list or tuple"
|
||||
raise ValueError(msg)
|
||||
elif len(transparency) != 3:
|
||||
msg = "transparency for RGB must have length 3"
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
red, green, blue = transparency
|
||||
chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue))
|
||||
elif im.encoderinfo.get("transparency") is not None:
|
||||
# don't bother with transparency if it's an RGBA
|
||||
# and it's in the info dict. It's probably just stale.
|
||||
msg = "cannot use transparency for this mode"
|
||||
raise OSError(msg)
|
||||
elif im.mode == "P" and im.im.getpalettemode() == "RGBA":
|
||||
alpha = im.im.getpalette("RGBA", "A")
|
||||
alpha_bytes = colors
|
||||
chunk(fp, b"tRNS", alpha[:alpha_bytes])
|
||||
|
||||
if dpi := im.encoderinfo.get("dpi"):
|
||||
chunk(
|
||||
|
||||
@ -37,8 +37,6 @@
|
||||
#define MAX(a, b) (a) > (b) ? (a) : (b)
|
||||
#define MIN(a, b) (a) < (b) ? (a) : (b)
|
||||
|
||||
#define CLIP16(v) ((v) <= 0 ? 0 : (v) >= 65535 ? 65535 : (v))
|
||||
|
||||
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
|
||||
#define L(rgb) ((INT32)(rgb)[0] * 299 + (INT32)(rgb)[1] * 587 + (INT32)(rgb)[2] * 114)
|
||||
#define L24(rgb) ((rgb)[0] * 19595 + (rgb)[1] * 38470 + (rgb)[2] * 7471 + 0x8000)
|
||||
|
||||
@ -27,6 +27,8 @@
|
||||
|
||||
#define CLIP8(v) ((v) <= 0 ? 0 : (v) < 256 ? (v) : 255)
|
||||
|
||||
#define CLIP16(v) ((v) <= 0 ? 0 : (v) < 65536 ? (v) : 65535)
|
||||
|
||||
/* This is to work around a bug in GCC prior 4.9 in 64 bit mode.
|
||||
GCC generates code with partial dependency which is 3 times slower.
|
||||
See: https://stackoverflow.com/a/26588074/253146 */
|
||||
|
||||
@ -492,9 +492,9 @@ ImagingResampleHorizontal_16bpc(
|
||||
<< 8)) *
|
||||
k[x];
|
||||
}
|
||||
ss_int = ROUND_UP(ss);
|
||||
imOut->image8[yy][xx * 2 + (bigendian ? 1 : 0)] = CLIP8(ss_int % 256);
|
||||
imOut->image8[yy][xx * 2 + (bigendian ? 0 : 1)] = CLIP8(ss_int >> 8);
|
||||
ss_int = CLIP16(ROUND_UP(ss));
|
||||
imOut->image8[yy][xx * 2 + (bigendian ? 1 : 0)] = ss_int & 0xFF;
|
||||
imOut->image8[yy][xx * 2 + (bigendian ? 0 : 1)] = ss_int >> 8;
|
||||
}
|
||||
}
|
||||
ImagingSectionLeave(&cookie);
|
||||
@ -531,9 +531,9 @@ ImagingResampleVertical_16bpc(
|
||||
(imIn->image8[y + ymin][xx * 2 + (bigendian ? 0 : 1)] << 8)) *
|
||||
k[y];
|
||||
}
|
||||
ss_int = ROUND_UP(ss);
|
||||
imOut->image8[yy][xx * 2 + (bigendian ? 1 : 0)] = CLIP8(ss_int % 256);
|
||||
imOut->image8[yy][xx * 2 + (bigendian ? 0 : 1)] = CLIP8(ss_int >> 8);
|
||||
ss_int = CLIP16(ROUND_UP(ss));
|
||||
imOut->image8[yy][xx * 2 + (bigendian ? 1 : 0)] = ss_int & 0xFF;
|
||||
imOut->image8[yy][xx * 2 + (bigendian ? 0 : 1)] = ss_int >> 8;
|
||||
}
|
||||
}
|
||||
ImagingSectionLeave(&cookie);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user