Skip to content

Commit

Permalink
More image operations
Browse files Browse the repository at this point in the history
  • Loading branch information
tomkins committed Jul 6, 2024
1 parent 3da051e commit 82b37a1
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 23 deletions.
114 changes: 91 additions & 23 deletions pyimgproxy/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,23 +500,52 @@ def watermark_shadow(self) -> "Image":
def style(self) -> "Image":
raise NotImplementedError

def strip_metadata(self) -> "Image":
raise NotImplementedError
def strip_metadata(self, strip_metadata: bool = False) -> "Image":
"""
When set to `True`, imgproxy will strip the metadata (EXIF, IPTC, etc.) on JPEG and WebP
output images. This is normally controlled by the `IMGPROXY_STRIP_METADATA` configuration
but this processing option allows the configuration to be set for each request.
"""
return self.add_option("strip_metadata", strip_metadata)

def keep_copyright(self) -> "Image":
raise NotImplementedError
def keep_copyright(self, keep_copyright: bool = False) -> "Image":
"""
When set to `True`, imgproxy will not remove copyright info while stripping metadata. This
is normally controlled by the `IMGPROXY_KEEP_COPYRIGHT` configuration but this processing
option allows the configuration to be set for each request.
"""
return self.add_option("keep_copyright", keep_copyright)

def dpi(self) -> "Image":
raise NotImplementedError

def strip_color_profile(self) -> "Image":
raise NotImplementedError
def strip_color_profile(self, strip_color_profile: bool = False) -> "Image":
"""
When set to `True`, imgproxy will transform the embedded color profile (ICC) to sRGB and
remove it from the image. Otherwise, imgproxy will try to keep it as is. This is normally
controlled by the `IMGPROXY_STRIP_COLOR_PROFILE` configuration but this processing option
allows the configuration to be set for each request.
"""
return self.add_option("strip_color_profile", strip_color_profile)

def enforce_thumbnail(self) -> "Image":
raise NotImplementedError
def enforce_thumbnail(self, enforce_thumbnail: bool = False) -> "Image":
"""
When set to `True` and the source image has an embedded thumbnail, imgproxy will always
use the embedded thumbnail instead of the main image. Currently, only thumbnails embedded
in heic and avif are supported. This is normally controlled by the
`IMGPROXY_ENFORCE_THUMBNAIL` configuration but this processing option allows the
configuration to be set for each request.
"""
return self.add_option("enforce_thumbnail", enforce_thumbnail)

def quality(self) -> "Image":
raise NotImplementedError
def quality(self, quality: int) -> "Image":
"""
Redefines quality of the resulting image, as a percentage. When set to `0`, quality is
assumed based on `IMGPROXY_QUALITY` and `format_quality`.
Default: `0`
"""
return self.add_option("quality", quality)

def format_quality(self) -> "Image":
raise NotImplementedError
Expand Down Expand Up @@ -578,7 +607,6 @@ def disable_animation(self, disable: bool) -> "Image":
Default: `False`
"""

return self.add_option("disable_animation", disable)

def video_thumbnail_second(self) -> "Image":
Expand Down Expand Up @@ -613,35 +641,75 @@ def raw(self, raw: bool) -> "Image":
"""
return self.add_option("raw", raw)

def cachebuster(self) -> "Image":
raise NotImplementedError
def cachebuster(self, string: str) -> "Image":
"""
Cache buster doesn't affect image processing but its changing allows for bypassing the
CDN, proxy server and browser cache. Useful when you have changed some things that are not
reflected in the URL, like image quality settings, presets, or watermark data.
It's highly recommended to prefer the `cachebuster` option over a URL query string because
that option can be properly signed.
Default: empty
"""
return self.add_option("cachebuster", string)

def expires(self) -> "Image":
raise NotImplementedError

def filename(self) -> "Image":
raise NotImplementedError

def return_attachment(self) -> "Image":
raise NotImplementedError
def return_attachment(self, return_attachment: bool) -> "Image":
"""
When set to `True`, imgproxy will return `attachment` in the `Content-Disposition` header,
and the browser will open a 'Save as' dialog. This is normally controlled by the
`IMGPROXY_RETURN_ATTACHMENT` configuration but this processing option allows the
configuration to be set for each request.
"""
return self.add_option("return_attachment", return_attachment)

def preset(self) -> "Image":
raise NotImplementedError

def hashsum(self) -> "Image":
raise NotImplementedError

def max_src_resolution(self) -> "Image":
raise NotImplementedError
def max_src_resolution(self, resolution: int) -> "Image":
"""
Allows redefining `IMGPROXY_MAX_SRC_RESOLUTION` config.
def max_src_file_size(self) -> "Image":
raise NotImplementedError
Since this option allows redefining a security restriction, its usage is not allowed
unless the `IMGPROXY_ALLOW_SECURITY_OPTIONS` config is set to true.
"""
return self.add_option("max_src_resolution", resolution)

def max_animation_frames(self) -> "Image":
raise NotImplementedError
def max_src_file_size(self, size: int) -> "Image":
"""
Allows redefining `IMGPROXY_MAX_SRC_FILE_SIZE` config.
def max_animation_frame_resolution(self) -> "Image":
raise NotImplementedError
Since this option allows redefining a security restriction, its usage is not allowed
unless the `IMGPROXY_ALLOW_SECURITY_OPTIONS` config is set to true.
"""
return self.add_option("max_src_file_size", size)

def max_animation_frames(self, size: int) -> "Image":
"""
Allows redefining `IMGPROXY_MAX_ANIMATION_FRAMES` config.
Since this option allows redefining a security restriction, its usage is not allowed
unless the `IMGPROXY_ALLOW_SECURITY_OPTIONS` config is set to true.
"""
return self.add_option("max_animation_frames", size)

def max_animation_frame_resolution(self, size: int) -> "Image":
"""
Allows redefining `IMGPROXY_MAX_ANIMATION_FRAME_RESOLUTION` config.
Since this option allows redefining a security restriction, its usage is not allowed
unless the `IMGPROXY_ALLOW_SECURITY_OPTIONS` config is set to true.
"""
return self.add_option("max_animation_frame_resolution", size)

def _source_url_needs_encoding(self) -> bool:
"""
Expand Down
75 changes: 75 additions & 0 deletions tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,81 @@ def test_draw_detections_empty(self):

self.assertEqual(image.options, ["draw_detections:True"])

def test_strip_metadata(self):
image = self.image.strip_metadata(strip_metadata=True)

self.assertEqual(image.options, ["strip_metadata:True"])

def test_keep_copyright(self):
image = self.image.keep_copyright(keep_copyright=True)

self.assertEqual(image.options, ["keep_copyright:True"])

def test_strip_color_profile(self):
image = self.image.strip_color_profile(strip_color_profile=True)

self.assertEqual(image.options, ["strip_color_profile:True"])

def test_enforce_thumbnail(self):
image = self.image.enforce_thumbnail(enforce_thumbnail=True)

self.assertEqual(image.options, ["enforce_thumbnail:True"])

def test_quality(self):
image = self.image.quality(quality=50)

self.assertEqual(image.options, ["quality:50"])

def test_format(self):
image = self.image.format(extension="png")

self.assertEqual(image.options, ["format:png"])

def test_page(self):
image = self.image.page(page=3)

self.assertEqual(image.options, ["page:3"])

def test_disable_animation(self):
image = self.image.disable_animation(disable=True)

self.assertEqual(image.options, ["disable_animation:True"])

def test_raw(self):
image = self.image.raw(raw=True)

self.assertEqual(image.options, ["raw:True"])

def test_cachebuster(self):
image = self.image.cachebuster(string="cache_bypass")

self.assertEqual(image.options, ["cachebuster:cache_bypass"])

def test_return_attachment(self):
image = self.image.return_attachment(return_attachment=True)

self.assertEqual(image.options, ["return_attachment:True"])

def test_max_src_resolution(self):
image = self.image.max_src_resolution(resolution=60)

self.assertEqual(image.options, ["max_src_resolution:60"])

def test_max_src_file_size(self):
image = self.image.max_src_file_size(size=1000000)

self.assertEqual(image.options, ["max_src_file_size:1000000"])

def test_max_animation_frames(self):
image = self.image.max_animation_frames(size=3)

self.assertEqual(image.options, ["max_animation_frames:3"])

def test_max_animation_frame_resolution(self):
image = self.image.max_animation_frame_resolution(size=4)

self.assertEqual(image.options, ["max_animation_frame_resolution:4"])


class ImageTestCase(TestCase):
def test_repr(self):
Expand Down

0 comments on commit 82b37a1

Please sign in to comment.