|
3 | 3 | from pathlib import Path |
4 | 4 |
|
5 | 5 | import pytest |
| 6 | +from PIL import Image |
6 | 7 |
|
7 | 8 | from pypdf import PdfReader |
8 | 9 | from pypdf._xobj_image_helpers import _extended_image_frombytes, _handle_flate |
| 10 | +from pypdf.constants import FilterTypes, ImageAttributes, StreamAttributes |
9 | 11 | from pypdf.errors import EmptyImageDataError, PdfReadError |
10 | | -from pypdf.generic import ArrayObject, DecodedStreamObject, NameObject, NumberObject |
| 12 | +from pypdf.filters import _xobj_to_image |
| 13 | +from pypdf.generic import ArrayObject, DecodedStreamObject, NameObject, NumberObject, StreamObject, TextStringObject |
11 | 14 |
|
12 | 15 | from . import get_data_from_url |
13 | 16 |
|
@@ -171,3 +174,43 @@ def test_get_imagemode__empty_array(): |
171 | 174 |
|
172 | 175 | with pytest.raises(expected_exception=PdfReadError, match=r"^ColorSpace field not found in .+"): |
173 | 176 | page.images[0].image.load() |
| 177 | + |
| 178 | + |
| 179 | +def test_p_image_with_alpha_mask(): |
| 180 | + # Generate the base image. Use TIFF as this is easy to do on the fly. |
| 181 | + image = Image.new(mode="P", size=(10, 10), color=0) |
| 182 | + image_data = BytesIO() |
| 183 | + image.save(image_data, format="tiff") |
| 184 | + |
| 185 | + # Set the common values. |
| 186 | + x_object = StreamObject() |
| 187 | + mask_object = StreamObject() |
| 188 | + for obj in [x_object, mask_object]: |
| 189 | + obj[NameObject(ImageAttributes.WIDTH)] = NumberObject(image.width) |
| 190 | + obj[NameObject(ImageAttributes.HEIGHT)] = NumberObject(image.height) |
| 191 | + obj[NameObject(StreamAttributes.FILTER)] = NameObject(FilterTypes.CCITT_FAX_DECODE) |
| 192 | + |
| 193 | + # Set the basic image data. |
| 194 | + x_object.set_data(image_data.getvalue()) |
| 195 | + x_object[NameObject(ImageAttributes.COLOR_SPACE)] = TextStringObject("palette") |
| 196 | + |
| 197 | + # Generate the mask image. Will be a diagonal white stripe. |
| 198 | + image = Image.new(mode="1", size=(image.width, image.height)) |
| 199 | + [image.putpixel((i, i), 1) for i in range(10)] |
| 200 | + image_data = BytesIO() |
| 201 | + image.save(image_data, format="tiff") |
| 202 | + |
| 203 | + # Set the mask data. |
| 204 | + mask_object.set_data(image_data.getvalue()) |
| 205 | + mask_object[NameObject(ImageAttributes.COLOR_SPACE)] = TextStringObject("1bit") |
| 206 | + |
| 207 | + # Add the mask to the image. |
| 208 | + x_object[NameObject("/SMask")] = mask_object |
| 209 | + |
| 210 | + # Generate the output image and make sure that the diagonal stripe is present. |
| 211 | + extension, data, image = _xobj_to_image(x_object) |
| 212 | + assert extension == ".png" |
| 213 | + assert data.startswith(b"\x89PNG") |
| 214 | + for i in range(10): |
| 215 | + for j in range(10): |
| 216 | + assert image.getpixel((i, j)) == (0, 0, 0, 255 * (i == j)) |
0 commit comments