diff --git a/pelican/plugins/image_process/image_process.py b/pelican/plugins/image_process/image_process.py index 82b7c93..9dac200 100644 --- a/pelican/plugins/image_process/image_process.py +++ b/pelican/plugins/image_process/image_process.py @@ -429,7 +429,12 @@ def process_img_tag(img, settings, derivative): if not isinstance(process, list): process = process["ops"] - process_image((path.source, destination, process), settings) + image_size = process_image((path.source, destination, process), settings) + if image_size: + if "width" not in img.attrs: + img["width"] = image_size[0] + if "height" not in img.attrs: + img["height"] = image_size[1] def format_srcset_element(path, condition): @@ -695,6 +700,12 @@ def try_open_image(path): def process_image(image, settings): + """Actually process the image. + + Copies over the Exif tags, if ExifTool is available. + + Returns (int, int): tuple of the width and height of the resulting image. + """ # remove URL encoding to get to physical filenames image = list(image) image[0] = unquote(image[0]) @@ -714,7 +725,7 @@ def process_image(image, settings): try: i = try_open_image(image[0]) except (UnidentifiedImageError, FileNotFoundError): - return + return None for step in image[2]: if callable(step): @@ -731,8 +742,11 @@ def process_image(image, settings): i.save(image[1], progressive=True) ExifTool.copy_tags(image[0], image[1]) - else: - logger.debug(f"{LOG_PREFIX} Skipping {image[0]} -> {image[1]}") + return i.width, i.height + + logger.debug(f"{LOG_PREFIX} Skipping {image[0]} -> {image[1]}") + i = Image.open(image[1]) + return i.width, i.height def dump_config(pelican): diff --git a/pelican/plugins/image_process/test_image_process.py b/pelican/plugins/image_process/test_image_process.py index d238363..f3cab2b 100644 --- a/pelican/plugins/image_process/test_image_process.py +++ b/pelican/plugins/image_process/test_image_process.py @@ -5,6 +5,7 @@ import subprocess import warnings +from bs4 import BeautifulSoup from PIL import Image, UnidentifiedImageError import pytest @@ -196,9 +197,8 @@ def test_path_normalization(mocker, orig_src, orig_img, new_src, new_img): img_tag_processed = harvest_images_in_fragment(img_tag_orig, settings) - assert img_tag_processed == ( - f'' - ) + soup = BeautifulSoup(img_tag_processed, "html.parser") + assert soup.img["src"] == new_src process.assert_called_once_with( ( @@ -299,8 +299,10 @@ def test_path_normalization(mocker, orig_src, orig_img, new_src, new_img): "orig_tag, new_tag, call_args", [ ( - '', - '', + '', + # Mock height and width added after processing. + '', [ ( "tmp/test.jpg", @@ -310,9 +312,11 @@ def test_path_normalization(mocker, orig_src, orig_img, new_src, new_img): ], ), ( - '', - '', + '', + # Pre-existing height and width unchanged after processing. + '', [ ( "tmp/test.jpg", @@ -502,6 +506,7 @@ def test_path_normalization(mocker, orig_src, orig_img, new_src, new_img): def test_html_and_pictures_generation(mocker, orig_tag, new_tag, call_args): """Tests that the generated html is as expected and the images are processed.""" process = mocker.patch("pelican.plugins.image_process.image_process.process_image") + process.return_value = (512, 384) settings = get_settings( IMAGE_PROCESS=COMPLEX_TRANSFORMS, IMAGE_PROCESS_DIR="derivs" @@ -528,22 +533,23 @@ def test_html_and_pictures_generation(mocker, orig_tag, new_tag, call_args): # src attribute with no quotes, spaces or commas. ( '', - '', + '', ), # src attribute with double quotes, spaces and commas. ( '', - '", + '', ), # src attribute with single and double quotes, spaces and commas. ( '', - '', + '', ), # srcset attribute with no quotes, spaces or commas. ( @@ -647,7 +653,8 @@ def test_special_chars_in_image_path_are_handled_properly(mocker, orig_tag, new_ Related to issue #78 https://github.com/pelican-plugins/image-process/issues/78 """ - mocker.patch("pelican.plugins.image_process.image_process.process_image") + process = mocker.patch("pelican.plugins.image_process.image_process.process_image") + process.return_value = (512, 384) settings = get_settings( IMAGE_PROCESS=COMPLEX_TRANSFORMS, IMAGE_PROCESS_DIR="derivs" @@ -776,7 +783,8 @@ def test_try_open_image(): [ ( '', - '', + '', [ # Default settings. {}, {"IMAGE_PROCESS_ADD_CLASS": True}, @@ -789,7 +797,8 @@ def test_try_open_image(): ), ( '', - '', + '', [ # Custom class prefix. {"IMAGE_PROCESS_CLASS_PREFIX": "custom-prefix-"}, { @@ -800,14 +809,15 @@ def test_try_open_image(): ), ( '', - '', + '', [ # Special case: empty string as class prefix. {"IMAGE_PROCESS_CLASS_PREFIX": ""}, ], ), ( '', - '', + '', [ # No class added. {"IMAGE_PROCESS_ADD_CLASS": False}, {"IMAGE_PROCESS_ADD_CLASS": False, "IMAGE_PROCESS_CLASS_PREFIX": ""}, @@ -818,7 +828,8 @@ def test_try_open_image(): def test_class_settings(mocker, orig_tag, new_tag, setting_overrides): """Test the IMAGE_PROCESS_ADD_CLASS and IMAGE_PROCESS_CLASS_PREFIX settings.""" # Silence image transforms. - mocker.patch("pelican.plugins.image_process.image_process.process_image") + process = mocker.patch("pelican.plugins.image_process.image_process.process_image") + process.return_value = (512, 384) for override in setting_overrides: settings = get_settings(**override)