From d3a96da535272385f3ebf8eebd8637d7dbd4704c Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Thu, 19 Jul 2018 22:40:56 -0400 Subject: [PATCH 01/27] fixing typo, and adding some missing descriptions on options --- README.rst | 2 +- docs/template.rst | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 224c131d8..a388f2c8a 100644 --- a/README.rst +++ b/README.rst @@ -130,7 +130,7 @@ You can use the 'get_thumbnail':: See more examples in the section `Low level API examples`_ in the Documentation -Using in combination with other thumbnalers +Using in combination with other thumbnailers ------------------------------------------- Alternatively, you load the templatetags by {% load sorl_thumbnail %} diff --git a/docs/template.rst b/docs/template.rst index bfdc0201e..d512db007 100644 --- a/docs/template.rst +++ b/docs/template.rst @@ -103,6 +103,17 @@ engine means that you can easily subclass an engine and create new features like rounded corners or what ever processing you like. The options described below are how they are used and interpreted in the shipped engines. +``cropbox`` +^^^^^^^^^^^ +This option is used to crop to a specific set of coordinates. ``cropbox`` takes +``x, y, x2, y2`` as arguments to crop the image down via those set of coordinates. +Note that ``cropbox`` is applied before ``crop``. + +.. code-block:: python + + img = get_thumbnail(sorl_img, cropbox="{0},{1},{2},{3}".format( + x, y, x2, y2)) + ``crop`` ^^^^^^^^ This option is only used if both width and height is given. Crop behaves much @@ -179,6 +190,9 @@ Images are not padded by default, but this can be changed by setting This is the color to use for padding the image. It defaults to ``#ffffff`` and can be globally set with the setting ``THUMBNAIL_PADDING_COLOR``. +``rounded`` +^^^^^^^^^^^^ +Takes an integer for radius to round the corners of the image. Defaults to ``None``. ``options`` ^^^^^^^^^^^ From 66bc87e0276d805ca26f705e1faa8fe851e00218 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Tue, 24 Jul 2018 21:54:01 -0500 Subject: [PATCH 02/27] adding free transform option --- sorl/thumbnail/base.py | 1 + sorl/thumbnail/engines/base.py | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sorl/thumbnail/base.py b/sorl/thumbnail/base.py index c41197f85..7e463977d 100644 --- a/sorl/thumbnail/base.py +++ b/sorl/thumbnail/base.py @@ -39,6 +39,7 @@ class ThumbnailBackend(object): 'rounded': None, 'padding': settings.THUMBNAIL_PADDING, 'padding_color': settings.THUMBNAIL_PADDING_COLOR, + 'transform': False, } extra_options = ( diff --git a/sorl/thumbnail/engines/base.py b/sorl/thumbnail/engines/base.py index a3698bb37..4d55f93ce 100644 --- a/sorl/thumbnail/engines/base.py +++ b/sorl/thumbnail/engines/base.py @@ -78,14 +78,18 @@ def scale(self, image, geometry, options): Wrapper for ``_scale`` """ upscale = options['upscale'] + transform = options['transform'] x_image, y_image = map(float, self.get_image_size(image)) - factor = self._calculate_scaling_factor(x_image, y_image, geometry, options) - if factor < 1 or upscale: - width = toint(x_image * factor) - height = toint(y_image * factor) - image = self._scale(image, width, height) + if not transform: + factor = self._calculate_scaling_factor(x_image, y_image, geometry, options) + if factor < 1 or upscale: + width = toint(x_image * factor) + height = toint(y_image * factor) + image = self._scale(image, width, height) + return image + image = self._scale(image, geometry[0], geometry[1]) return image def crop(self, image, geometry, options): From 93b2a6c70c9edd9dbd5be8bb781232a76da46398 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 09:40:59 -0500 Subject: [PATCH 03/27] adding free transform docs and tests --- docs/template.rst | 6 +++ tests/thumbnail_tests/test_engines.py | 61 +++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/docs/template.rst b/docs/template.rst index d512db007..46ac32b77 100644 --- a/docs/template.rst +++ b/docs/template.rst @@ -136,6 +136,12 @@ cropping options if you don't want to generate unnecessary thumbnails. In case you are wondering, sorl-thumbnail sorts the options so the order does not matter, same options but in different order will generate only one thumbnail. +``transform`` +^^^^^^^^^^^ +Transform is a boolean and controls if the image will be free transformed to the +dimensions provided. If set to true, the image will be forcibly resized to the +supplied dimensions and stretch as needed. Defaults to ``False``. + ``upscale`` ^^^^^^^^^^^ Upscale is a boolean and controls if the image can be upscaled or not. For diff --git a/tests/thumbnail_tests/test_engines.py b/tests/thumbnail_tests/test_engines.py index 63dc5d299..a250fabdd 100644 --- a/tests/thumbnail_tests/test_engines.py +++ b/tests/thumbnail_tests/test_engines.py @@ -380,6 +380,67 @@ def test_crop_image_with_icc_profile(self): ) +class FreeTransformTestCase(BaseTestCase): + def setUp(self): + super(FreeTransformTestCase, self).setUp() + + # portrait + name = 'portrait.jpg' + fn = os.path.join(settings.MEDIA_ROOT, name) + im = Image.new('L', (100, 200)) + im.paste(255, (0, 0, 100, 100)) + im.save(fn) + self.portrait = ImageFile(Item.objects.get_or_create(image=name)[0].image) + self.KVSTORE.delete(self.portrait) + + @unittest.skipIf( + 'pil_engine' not in settings.THUMBNAIL_ENGINE, + 'the other engines fail this test', + ) + def test_PIL_freetransform(self): + + th = self.BACKEND.get_thumbnail(self.landscape, '100x100', transform=True) + engine = PILEngine() + im = engine.get_image(th) + self.assertEqual(im.width, 100) + self.assertEqual(im.height, 100) + + @unittest.skipIf( + 'convert_engine' not in settings.THUMBNAIL_ENGINE, + 'the other engines fail this test', + ) + def test_convert_engine_freetransform(self): + from sorl.thumbnail.engines import convert_engine as ConvertEngine + th = self.BACKEND.get_thumbnail(self.landscape, '100x100', transform=True) + engine = ConvertEngine() + im = engine.get_image(th) + self.assertEqual(im["size"], (100, 100)) + + @unittest.skipIf( + 'wand_engine' not in settings.THUMBNAIL_ENGINE, + 'the other engines fail this test', + ) + def test_wand_engine_freetransform(self): + from sorl.thumbnail.engines import wand_engine as WandEngine + th = self.BACKEND.get_thumbnail(self.landscape, '100x100', transform=True) + engine = WandEngine() + im = engine.get_image(th) + self.assertEqual(im.width(100), 100) + self.assertEqual(im.height(100), 100) + + @unittest.skipIf( + 'pgmagick_engine' not in settings.THUMBNAIL_ENGINE, + 'the other engines fail this test', + ) + def test_pgmagick_engine_freetransform(self): + from sorl.thumbnail.engines import pgmagick_engine as PgmagickEngine + th = self.BACKEND.get_thumbnail(self.landscape, '100x100', transform=True) + engine = PgmagickEngine() + im = engine.get_image(th) + self.assertEqual(im.width(100), 100) + self.assertEqual(im.height(100), 100) + + class DummyTestCase(unittest.TestCase): def setUp(self): self.BACKEND = get_module_class(settings.THUMBNAIL_BACKEND)() From 8d6e6e4d1fb160bb244079f91267aba04bcfe81b Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 09:57:57 -0500 Subject: [PATCH 04/27] fixing imports and proper test image --- tests/thumbnail_tests/test_engines.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/thumbnail_tests/test_engines.py b/tests/thumbnail_tests/test_engines.py index a250fabdd..690e5d1bd 100644 --- a/tests/thumbnail_tests/test_engines.py +++ b/tests/thumbnail_tests/test_engines.py @@ -399,7 +399,7 @@ def setUp(self): ) def test_PIL_freetransform(self): - th = self.BACKEND.get_thumbnail(self.landscape, '100x100', transform=True) + th = self.BACKEND.get_thumbnail(self.portrait, '100x100', transform=True) engine = PILEngine() im = engine.get_image(th) self.assertEqual(im.width, 100) @@ -410,8 +410,8 @@ def test_PIL_freetransform(self): 'the other engines fail this test', ) def test_convert_engine_freetransform(self): - from sorl.thumbnail.engines import convert_engine as ConvertEngine - th = self.BACKEND.get_thumbnail(self.landscape, '100x100', transform=True) + from sorl.thumbnail.engines.convert_engine import Engine as ConvertEngine + th = self.BACKEND.get_thumbnail(self.portrait, '100x100', transform=True) engine = ConvertEngine() im = engine.get_image(th) self.assertEqual(im["size"], (100, 100)) @@ -421,8 +421,8 @@ def test_convert_engine_freetransform(self): 'the other engines fail this test', ) def test_wand_engine_freetransform(self): - from sorl.thumbnail.engines import wand_engine as WandEngine - th = self.BACKEND.get_thumbnail(self.landscape, '100x100', transform=True) + from sorl.thumbnail.engines.wand_engine import Engine as WandEngine + th = self.BACKEND.get_thumbnail(self.portrait, '100x100', transform=True) engine = WandEngine() im = engine.get_image(th) self.assertEqual(im.width(100), 100) @@ -433,8 +433,8 @@ def test_wand_engine_freetransform(self): 'the other engines fail this test', ) def test_pgmagick_engine_freetransform(self): - from sorl.thumbnail.engines import pgmagick_engine as PgmagickEngine - th = self.BACKEND.get_thumbnail(self.landscape, '100x100', transform=True) + from sorl.thumbnail.engines.pgmagick_engine import Engine as PgmagickEngine + th = self.BACKEND.get_thumbnail(self.portrait, '100x100', transform=True) engine = PgmagickEngine() im = engine.get_image(th) self.assertEqual(im.width(100), 100) From a9321ea2ddbdc9cea5d6b4cb041f7a843b9633a2 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 13:18:33 -0500 Subject: [PATCH 05/27] removing rounded from docs --- docs/template.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/template.rst b/docs/template.rst index 46ac32b77..d9327f32c 100644 --- a/docs/template.rst +++ b/docs/template.rst @@ -196,10 +196,6 @@ Images are not padded by default, but this can be changed by setting This is the color to use for padding the image. It defaults to ``#ffffff`` and can be globally set with the setting ``THUMBNAIL_PADDING_COLOR``. -``rounded`` -^^^^^^^^^^^^ -Takes an integer for radius to round the corners of the image. Defaults to ``None``. - ``options`` ^^^^^^^^^^^ Yes this option is called ``options``. This needs to be a context variable that From 42c739b48192a31b5e54eb0de1fa4a6ea7f1ae0b Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 13:19:11 -0500 Subject: [PATCH 06/27] removing cropbox from docs, since this pull is just for transform --- docs/template.rst | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/docs/template.rst b/docs/template.rst index d9327f32c..315563064 100644 --- a/docs/template.rst +++ b/docs/template.rst @@ -103,17 +103,6 @@ engine means that you can easily subclass an engine and create new features like rounded corners or what ever processing you like. The options described below are how they are used and interpreted in the shipped engines. -``cropbox`` -^^^^^^^^^^^ -This option is used to crop to a specific set of coordinates. ``cropbox`` takes -``x, y, x2, y2`` as arguments to crop the image down via those set of coordinates. -Note that ``cropbox`` is applied before ``crop``. - -.. code-block:: python - - img = get_thumbnail(sorl_img, cropbox="{0},{1},{2},{3}".format( - x, y, x2, y2)) - ``crop`` ^^^^^^^^ This option is only used if both width and height is given. Crop behaves much From fe22652bb9f1decb4b4f0fb67627915e86076456 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 13:34:54 -0500 Subject: [PATCH 07/27] changing if logic for transform --- sorl/thumbnail/engines/base.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/sorl/thumbnail/engines/base.py b/sorl/thumbnail/engines/base.py index 4d55f93ce..6664c51e5 100644 --- a/sorl/thumbnail/engines/base.py +++ b/sorl/thumbnail/engines/base.py @@ -81,15 +81,16 @@ def scale(self, image, geometry, options): transform = options['transform'] x_image, y_image = map(float, self.get_image_size(image)) - if not transform: - factor = self._calculate_scaling_factor(x_image, y_image, geometry, options) - - if factor < 1 or upscale: - width = toint(x_image * factor) - height = toint(y_image * factor) - image = self._scale(image, width, height) + if transform: + image = self._scale(image, geometry[0], geometry[1]) return image - image = self._scale(image, geometry[0], geometry[1]) + + factor = self._calculate_scaling_factor(x_image, y_image, geometry, options) + + if factor < 1 or upscale: + width = toint(x_image * factor) + height = toint(y_image * factor) + image = self._scale(image, width, height) return image def crop(self, image, geometry, options): From 86e66f79178ac0f24c2983c66e80dc400a8a3ddf Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 16:23:25 -0500 Subject: [PATCH 08/27] updating cache to reflect new key (due to new options, and options are part of the key) --- .../test_alternative_resolutions.py | 20 +++++++++---------- tests/thumbnail_tests/test_backends.py | 2 +- tests/thumbnail_tests/test_filters.py | 8 ++++---- tests/thumbnail_tests/test_storage.py | 8 ++++---- tests/thumbnail_tests/test_templatetags.py | 14 ++++++------- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/thumbnail_tests/test_alternative_resolutions.py b/tests/thumbnail_tests/test_alternative_resolutions.py index afc909960..0824414f1 100644 --- a/tests/thumbnail_tests/test_alternative_resolutions.py +++ b/tests/thumbnail_tests/test_alternative_resolutions.py @@ -30,23 +30,23 @@ def test_retina(self): get_thumbnail(self.image, '50x50') actions = [ - 'exists: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d.jpg', + 'exists: test/cache/b4/54/b45476c738bef768e3c0f8497a889248.jpg', # save regular resolution, same as in StorageTestCase 'open: retina.jpg', - 'save: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d.jpg', - 'get_available_name: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d.jpg', - 'exists: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d.jpg', + 'save: test/cache/b4/54/b45476c738bef768e3c0f8497a889248.jpg', + 'get_available_name: test/cache/b4/54/b45476c738bef768e3c0f8497a889248.jpg', + 'exists: test/cache/b4/54/b45476c738bef768e3c0f8497a889248.jpg', # save the 1.5x resolution version - 'save: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@1.5x.jpg', - 'get_available_name: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@1.5x.jpg', - 'exists: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@1.5x.jpg', + 'save: test/cache/b4/54/b45476c738bef768e3c0f8497a889248@1.5x.jpg', + 'get_available_name: test/cache/b4/54/b45476c738bef768e3c0f8497a889248@1.5x.jpg', + 'exists: test/cache/b4/54/b45476c738bef768e3c0f8497a889248@1.5x.jpg', # save the 2x resolution version - 'save: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@2x.jpg', - 'get_available_name: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@2x.jpg', - 'exists: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@2x.jpg' + 'save: test/cache/b4/54/b45476c738bef768e3c0f8497a889248@2x.jpg', + 'get_available_name: test/cache/b4/54/b45476c738bef768e3c0f8497a889248@2x.jpg', + 'exists: test/cache/b4/54/b45476c738bef768e3c0f8497a889248@2x.jpg' ] self.assertEqual(self.log, actions) diff --git a/tests/thumbnail_tests/test_backends.py b/tests/thumbnail_tests/test_backends.py index ab091fd0e..e1ec0611d 100644 --- a/tests/thumbnail_tests/test_backends.py +++ b/tests/thumbnail_tests/test_backends.py @@ -136,7 +136,7 @@ def setUp(self): def test_nonascii(self): # also test the get_thumbnail shortcut th = get_thumbnail(self.name, '200x200') - self.assertEqual(th.url, '/media/test/cache/f5/26/f52608b56718f62abc45a90ff9459f2c.jpg') + self.assertEqual(th.url, '/media/test/cache/c2/76/c276ec92bcd66a435354083d580da243.jpg') def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) diff --git a/tests/thumbnail_tests/test_filters.py b/tests/thumbnail_tests/test_filters.py index 5364107fd..46d03d8e1 100644 --- a/tests/thumbnail_tests/test_filters.py +++ b/tests/thumbnail_tests/test_filters.py @@ -15,7 +15,7 @@ def test_html_filter(self): self.assertEqual( 'A image!', + 'src="/media/test/cache/0a/7b/0a7b030a1b690d12157b081444f6dd4d.jpg" />', val ) @@ -25,7 +25,7 @@ def test_html_filter_local_url(self): self.assertEqual( 'A image!', + 'src="/media/test/cache/6c/e5/6ce5cdfef5c75d469f7018d9eeda3acd.jpg" />', val ) @@ -34,7 +34,7 @@ def test_markdown_filter(self): val = render_to_string('markdownfilter.html', {'text': text, }).strip() self.assertEqual( - '![A image!](/media/test/cache/2e/35/2e3517d8aa949728b1ee8b26c5a7bbc4.jpg)', + '![A image!](/media/test/cache/0a/7b/0a7b030a1b690d12157b081444f6dd4d.jpg)', val ) @@ -43,6 +43,6 @@ def test_markdown_filter_local_url(self): val = render_to_string('markdownfilter.html', {'text': text, }).strip() self.assertEqual( - '![A image!](/media/test/cache/c7/f2/c7f2880b48e9f07d46a05472c22f0fde.jpg)', + '![A image!](/media/test/cache/6c/e5/6ce5cdfef5c75d469f7018d9eeda3acd.jpg)', val ) diff --git a/tests/thumbnail_tests/test_storage.py b/tests/thumbnail_tests/test_storage.py index 48f061e7b..f32f721c9 100644 --- a/tests/thumbnail_tests/test_storage.py +++ b/tests/thumbnail_tests/test_storage.py @@ -17,15 +17,15 @@ class StorageTestCase(BaseStorageTestCase): def test_new(self): get_thumbnail(self.image, '50x50') actions = [ - 'exists: test/cache/45/bb/45bbbdab11e235a80e603aa119e8786b.jpg', + 'exists: test/cache/84/6b/846b022033e7872087ba5ee2e039bc4e.jpg', # open the original for thumbnailing 'open: org.jpg', # save the file - 'save: test/cache/45/bb/45bbbdab11e235a80e603aa119e8786b.jpg', + 'save: test/cache/84/6b/846b022033e7872087ba5ee2e039bc4e.jpg', # check for filename - 'get_available_name: test/cache/45/bb/45bbbdab11e235a80e603aa119e8786b.jpg', + 'get_available_name: test/cache/84/6b/846b022033e7872087ba5ee2e039bc4e.jpg', # called by get_available_name - 'exists: test/cache/45/bb/45bbbdab11e235a80e603aa119e8786b.jpg', + 'exists: test/cache/84/6b/846b022033e7872087ba5ee2e039bc4e.jpg', ] self.assertEqual(self.log, actions) diff --git a/tests/thumbnail_tests/test_templatetags.py b/tests/thumbnail_tests/test_templatetags.py index f283f619b..c02389d73 100644 --- a/tests/thumbnail_tests/test_templatetags.py +++ b/tests/thumbnail_tests/test_templatetags.py @@ -29,8 +29,8 @@ def test_nested(self): item = Item.objects.get(image='500x500.jpg') val = render_to_string('thumbnail6.html', {'item': item, }).strip() self.assertEqual(val, ( - '' - '' + '' )) @@ -125,7 +125,7 @@ def test_portrait(self): 'dims': 'x66', }).strip() self.assertEqual(val, - '') def test_empty(self): @@ -176,10 +176,10 @@ def test_nested(self): self.assertEqual( val, ( - '' - '' + '' ) ) From 5a59fe80c7d1ebf041a0b977fb970b50982d22ab Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 16:27:44 -0500 Subject: [PATCH 09/27] forgot to update the directory as well --- tests/thumbnail_tests/test_alternative_resolutions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/thumbnail_tests/test_alternative_resolutions.py b/tests/thumbnail_tests/test_alternative_resolutions.py index 0824414f1..197a42220 100644 --- a/tests/thumbnail_tests/test_alternative_resolutions.py +++ b/tests/thumbnail_tests/test_alternative_resolutions.py @@ -51,7 +51,7 @@ def test_retina(self): self.assertEqual(self.log, actions) path = os.path.join(settings.MEDIA_ROOT, - 'test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@1.5x.jpg') + 'test/cache/b4/54/b45476c738bef768e3c0f8497a889248@1.5x.jpg') with open(path) as fp: engine = PILEngine() From b905efd7a80795b6417e0945be5fed964c6cbf2e Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 17:06:32 -0500 Subject: [PATCH 10/27] fixing wand test --- tests/thumbnail_tests/test_engines.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/thumbnail_tests/test_engines.py b/tests/thumbnail_tests/test_engines.py index 690e5d1bd..50fcda3e9 100644 --- a/tests/thumbnail_tests/test_engines.py +++ b/tests/thumbnail_tests/test_engines.py @@ -425,8 +425,8 @@ def test_wand_engine_freetransform(self): th = self.BACKEND.get_thumbnail(self.portrait, '100x100', transform=True) engine = WandEngine() im = engine.get_image(th) - self.assertEqual(im.width(100), 100) - self.assertEqual(im.height(100), 100) + self.assertEqual(im.width, 100) + self.assertEqual(im.height, 100) @unittest.skipIf( 'pgmagick_engine' not in settings.THUMBNAIL_ENGINE, From c4172d324f2b517978b09fed6b3f425ddf934a23 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 17:15:00 -0500 Subject: [PATCH 11/27] making pgmagick _scale use resize, since it is either passed already scaled ratios, or dimensions for resizing --- sorl/thumbnail/engines/pgmagick_engine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sorl/thumbnail/engines/pgmagick_engine.py b/sorl/thumbnail/engines/pgmagick_engine.py index 443dfafda..08a3ec301 100644 --- a/sorl/thumbnail/engines/pgmagick_engine.py +++ b/sorl/thumbnail/engines/pgmagick_engine.py @@ -75,7 +75,7 @@ def _colorspace(self, image, colorspace): def _scale(self, image, width, height): geometry = Geometry(width, height) - image.scale(geometry) + image.resize(geometry) return image def _crop(self, image, width, height, x_offset, y_offset): From 962690a13f26513c13a33b2193af1638bc92ebbf Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 17:47:48 -0500 Subject: [PATCH 12/27] im --- tests/thumbnail_tests/test_engines.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/thumbnail_tests/test_engines.py b/tests/thumbnail_tests/test_engines.py index 50fcda3e9..bc8c574ba 100644 --- a/tests/thumbnail_tests/test_engines.py +++ b/tests/thumbnail_tests/test_engines.py @@ -414,7 +414,7 @@ def test_convert_engine_freetransform(self): th = self.BACKEND.get_thumbnail(self.portrait, '100x100', transform=True) engine = ConvertEngine() im = engine.get_image(th) - self.assertEqual(im["size"], (100, 100)) + self.assertEqual(im, (100, 100)) @unittest.skipIf( 'wand_engine' not in settings.THUMBNAIL_ENGINE, From f6d6f8b7c9b9b1f44f01899583642366a4c4ae43 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 18:23:15 -0500 Subject: [PATCH 13/27] doing the resize according to pgmagick doc --- sorl/thumbnail/engines/pgmagick_engine.py | 4 ++-- tests/thumbnail_tests/test_engines.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sorl/thumbnail/engines/pgmagick_engine.py b/sorl/thumbnail/engines/pgmagick_engine.py index 08a3ec301..c4401a5ba 100644 --- a/sorl/thumbnail/engines/pgmagick_engine.py +++ b/sorl/thumbnail/engines/pgmagick_engine.py @@ -74,8 +74,8 @@ def _colorspace(self, image, colorspace): return image def _scale(self, image, width, height): - geometry = Geometry(width, height) - image.resize(geometry) + #geometry = Geometry(width, height) + image.resize(str(width) + 'x' + str(height)) return image def _crop(self, image, width, height, x_offset, y_offset): diff --git a/tests/thumbnail_tests/test_engines.py b/tests/thumbnail_tests/test_engines.py index bc8c574ba..50fcda3e9 100644 --- a/tests/thumbnail_tests/test_engines.py +++ b/tests/thumbnail_tests/test_engines.py @@ -414,7 +414,7 @@ def test_convert_engine_freetransform(self): th = self.BACKEND.get_thumbnail(self.portrait, '100x100', transform=True) engine = ConvertEngine() im = engine.get_image(th) - self.assertEqual(im, (100, 100)) + self.assertEqual(im["size"], (100, 100)) @unittest.skipIf( 'wand_engine' not in settings.THUMBNAIL_ENGINE, From f0032e75a4e08f8bbf6f3c81f5fdeb560786fd79 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 18:49:21 -0500 Subject: [PATCH 14/27] reverting to old way to scale --- sorl/thumbnail/engines/pgmagick_engine.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sorl/thumbnail/engines/pgmagick_engine.py b/sorl/thumbnail/engines/pgmagick_engine.py index c4401a5ba..08a3ec301 100644 --- a/sorl/thumbnail/engines/pgmagick_engine.py +++ b/sorl/thumbnail/engines/pgmagick_engine.py @@ -74,8 +74,8 @@ def _colorspace(self, image, colorspace): return image def _scale(self, image, width, height): - #geometry = Geometry(width, height) - image.resize(str(width) + 'x' + str(height)) + geometry = Geometry(width, height) + image.resize(geometry) return image def _crop(self, image, width, height, x_offset, y_offset): From 0c63d12290dfb82ee29d9ef7b85dd96434cc8607 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 21:46:30 -0500 Subject: [PATCH 15/27] undoing pgmagick engine, changing convert engine (proper one) --- sorl/thumbnail/engines/convert_engine.py | 2 +- sorl/thumbnail/engines/pgmagick_engine.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sorl/thumbnail/engines/convert_engine.py b/sorl/thumbnail/engines/convert_engine.py index eb346e757..1bc90642d 100644 --- a/sorl/thumbnail/engines/convert_engine.py +++ b/sorl/thumbnail/engines/convert_engine.py @@ -175,7 +175,7 @@ def _scale(self, image, width, height): """ Does the resizing of the image """ - image['options']['scale'] = '%sx%s!' % (width, height) + image['options']['resize'] = '%sx%s!' % (width, height) image['size'] = (width, height) # update image size return image diff --git a/sorl/thumbnail/engines/pgmagick_engine.py b/sorl/thumbnail/engines/pgmagick_engine.py index 08a3ec301..443dfafda 100644 --- a/sorl/thumbnail/engines/pgmagick_engine.py +++ b/sorl/thumbnail/engines/pgmagick_engine.py @@ -75,7 +75,7 @@ def _colorspace(self, image, colorspace): def _scale(self, image, width, height): geometry = Geometry(width, height) - image.resize(geometry) + image.scale(geometry) return image def _crop(self, image, width, height, x_offset, y_offset): From 178f82b20aaf482a12af542584ba40209545b85d Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 21:57:44 -0500 Subject: [PATCH 16/27] changing get_image to get_image_size, since convert_engine's get_image ALWAYS returns 'size': None --- tests/thumbnail_tests/test_engines.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/thumbnail_tests/test_engines.py b/tests/thumbnail_tests/test_engines.py index 50fcda3e9..f15b672b8 100644 --- a/tests/thumbnail_tests/test_engines.py +++ b/tests/thumbnail_tests/test_engines.py @@ -413,8 +413,8 @@ def test_convert_engine_freetransform(self): from sorl.thumbnail.engines.convert_engine import Engine as ConvertEngine th = self.BACKEND.get_thumbnail(self.portrait, '100x100', transform=True) engine = ConvertEngine() - im = engine.get_image(th) - self.assertEqual(im["size"], (100, 100)) + im = engine.get_image_size(th) + self.assertEqual(im, (100, 100)) @unittest.skipIf( 'wand_engine' not in settings.THUMBNAIL_ENGINE, From 00e3df1e0f7d76428f7e3d5226f03c6df562472c Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 21:59:25 -0500 Subject: [PATCH 17/27] reverting to "scale" from "resize" --- sorl/thumbnail/engines/convert_engine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sorl/thumbnail/engines/convert_engine.py b/sorl/thumbnail/engines/convert_engine.py index 1bc90642d..eb346e757 100644 --- a/sorl/thumbnail/engines/convert_engine.py +++ b/sorl/thumbnail/engines/convert_engine.py @@ -175,7 +175,7 @@ def _scale(self, image, width, height): """ Does the resizing of the image """ - image['options']['resize'] = '%sx%s!' % (width, height) + image['options']['scale'] = '%sx%s!' % (width, height) image['size'] = (width, height) # update image size return image From e7063fe76a035418308d6cb453f19aaf31964f44 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Sun, 29 Jul 2018 22:01:07 -0500 Subject: [PATCH 18/27] getting size from the actual image now, instead of source --- tests/thumbnail_tests/test_engines.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/thumbnail_tests/test_engines.py b/tests/thumbnail_tests/test_engines.py index f15b672b8..de3d6b4e4 100644 --- a/tests/thumbnail_tests/test_engines.py +++ b/tests/thumbnail_tests/test_engines.py @@ -413,8 +413,9 @@ def test_convert_engine_freetransform(self): from sorl.thumbnail.engines.convert_engine import Engine as ConvertEngine th = self.BACKEND.get_thumbnail(self.portrait, '100x100', transform=True) engine = ConvertEngine() - im = engine.get_image_size(th) - self.assertEqual(im, (100, 100)) + im = engine.get_image(th) + size = engine.get_image_size(im) + self.assertEqual(size, (100, 100)) @unittest.skipIf( 'wand_engine' not in settings.THUMBNAIL_ENGINE, From 5035029667780d59c2ed3be7eda818b6bc8dcd82 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Thu, 13 Sep 2018 08:03:16 -0400 Subject: [PATCH 19/27] moving transform into extra_options --- docs/template.rst | 2 +- sorl/thumbnail/base.py | 4 ++-- sorl/thumbnail/conf/defaults.py | 3 +++ sorl/thumbnail/engines/base.py | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/template.rst b/docs/template.rst index 315563064..9ab2bdea0 100644 --- a/docs/template.rst +++ b/docs/template.rst @@ -129,7 +129,7 @@ matter, same options but in different order will generate only one thumbnail. ^^^^^^^^^^^ Transform is a boolean and controls if the image will be free transformed to the dimensions provided. If set to true, the image will be forcibly resized to the -supplied dimensions and stretch as needed. Defaults to ``False``. +supplied dimensions and stretch as needed. Default value is ``False``. ``upscale`` ^^^^^^^^^^^ diff --git a/sorl/thumbnail/base.py b/sorl/thumbnail/base.py index 7e463977d..6f792f19d 100644 --- a/sorl/thumbnail/base.py +++ b/sorl/thumbnail/base.py @@ -38,14 +38,14 @@ class ThumbnailBackend(object): 'cropbox': None, 'rounded': None, 'padding': settings.THUMBNAIL_PADDING, - 'padding_color': settings.THUMBNAIL_PADDING_COLOR, - 'transform': False, + 'padding_color': settings.THUMBNAIL_PADDING_COLOR } extra_options = ( ('progressive', 'THUMBNAIL_PROGRESSIVE'), ('orientation', 'THUMBNAIL_ORIENTATION'), ('blur', 'THUMBNAIL_BLUR'), + ('transform', "THUMBNAIL_TRANSFORM") ) def file_extension(self, source): diff --git a/sorl/thumbnail/conf/defaults.py b/sorl/thumbnail/conf/defaults.py index 6a5aa202b..9a19e77e3 100644 --- a/sorl/thumbnail/conf/defaults.py +++ b/sorl/thumbnail/conf/defaults.py @@ -89,6 +89,9 @@ # Orientate the thumbnail with respect to source EXIF orientation tag THUMBNAIL_ORIENTATION = True +# Whether to apply free-transform to the image by default (breaking the aspect ratio lock) +THUMBNAIL_TRANSFORM = False + # This means sorl.thumbnail will generate and serve a generated dummy image # regardless of the thumbnail source content THUMBNAIL_DUMMY = False diff --git a/sorl/thumbnail/engines/base.py b/sorl/thumbnail/engines/base.py index 6664c51e5..25fc7633f 100644 --- a/sorl/thumbnail/engines/base.py +++ b/sorl/thumbnail/engines/base.py @@ -78,7 +78,7 @@ def scale(self, image, geometry, options): Wrapper for ``_scale`` """ upscale = options['upscale'] - transform = options['transform'] + transform = options.get('transform', settings.THUMBNAIL_TRANSFORM) x_image, y_image = map(float, self.get_image_size(image)) if transform: From 47dcbefa617925088b4b6c5215c8ed10bd13c138 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Thu, 13 Sep 2018 20:57:41 -0400 Subject: [PATCH 20/27] reverting some cache paths now that we're using extra_options --- tests/thumbnail_tests/test_backends.py | 2 +- tests/thumbnail_tests/test_filters.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/thumbnail_tests/test_backends.py b/tests/thumbnail_tests/test_backends.py index e1ec0611d..ab091fd0e 100644 --- a/tests/thumbnail_tests/test_backends.py +++ b/tests/thumbnail_tests/test_backends.py @@ -136,7 +136,7 @@ def setUp(self): def test_nonascii(self): # also test the get_thumbnail shortcut th = get_thumbnail(self.name, '200x200') - self.assertEqual(th.url, '/media/test/cache/c2/76/c276ec92bcd66a435354083d580da243.jpg') + self.assertEqual(th.url, '/media/test/cache/f5/26/f52608b56718f62abc45a90ff9459f2c.jpg') def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) diff --git a/tests/thumbnail_tests/test_filters.py b/tests/thumbnail_tests/test_filters.py index 46d03d8e1..5364107fd 100644 --- a/tests/thumbnail_tests/test_filters.py +++ b/tests/thumbnail_tests/test_filters.py @@ -15,7 +15,7 @@ def test_html_filter(self): self.assertEqual( 'A image!', + 'src="/media/test/cache/2e/35/2e3517d8aa949728b1ee8b26c5a7bbc4.jpg" />', val ) @@ -25,7 +25,7 @@ def test_html_filter_local_url(self): self.assertEqual( 'A image!', + 'src="/media/test/cache/c7/f2/c7f2880b48e9f07d46a05472c22f0fde.jpg" />', val ) @@ -34,7 +34,7 @@ def test_markdown_filter(self): val = render_to_string('markdownfilter.html', {'text': text, }).strip() self.assertEqual( - '![A image!](/media/test/cache/0a/7b/0a7b030a1b690d12157b081444f6dd4d.jpg)', + '![A image!](/media/test/cache/2e/35/2e3517d8aa949728b1ee8b26c5a7bbc4.jpg)', val ) @@ -43,6 +43,6 @@ def test_markdown_filter_local_url(self): val = render_to_string('markdownfilter.html', {'text': text, }).strip() self.assertEqual( - '![A image!](/media/test/cache/6c/e5/6ce5cdfef5c75d469f7018d9eeda3acd.jpg)', + '![A image!](/media/test/cache/c7/f2/c7f2880b48e9f07d46a05472c22f0fde.jpg)', val ) From 91ace16a1a8a9e50c5514e6efad5d7ae06f62f1e Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Thu, 13 Sep 2018 20:58:46 -0400 Subject: [PATCH 21/27] revert test_storage --- tests/thumbnail_tests/test_storage.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/thumbnail_tests/test_storage.py b/tests/thumbnail_tests/test_storage.py index f32f721c9..48f061e7b 100644 --- a/tests/thumbnail_tests/test_storage.py +++ b/tests/thumbnail_tests/test_storage.py @@ -17,15 +17,15 @@ class StorageTestCase(BaseStorageTestCase): def test_new(self): get_thumbnail(self.image, '50x50') actions = [ - 'exists: test/cache/84/6b/846b022033e7872087ba5ee2e039bc4e.jpg', + 'exists: test/cache/45/bb/45bbbdab11e235a80e603aa119e8786b.jpg', # open the original for thumbnailing 'open: org.jpg', # save the file - 'save: test/cache/84/6b/846b022033e7872087ba5ee2e039bc4e.jpg', + 'save: test/cache/45/bb/45bbbdab11e235a80e603aa119e8786b.jpg', # check for filename - 'get_available_name: test/cache/84/6b/846b022033e7872087ba5ee2e039bc4e.jpg', + 'get_available_name: test/cache/45/bb/45bbbdab11e235a80e603aa119e8786b.jpg', # called by get_available_name - 'exists: test/cache/84/6b/846b022033e7872087ba5ee2e039bc4e.jpg', + 'exists: test/cache/45/bb/45bbbdab11e235a80e603aa119e8786b.jpg', ] self.assertEqual(self.log, actions) From 49c0d37ec639b7ecabc6a157c69bc1fd18ebc49d Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Thu, 13 Sep 2018 21:01:17 -0400 Subject: [PATCH 22/27] revert test_templatetags --- tests/thumbnail_tests/test_templatetags.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/thumbnail_tests/test_templatetags.py b/tests/thumbnail_tests/test_templatetags.py index 062f9e4b4..789e0ee54 100644 --- a/tests/thumbnail_tests/test_templatetags.py +++ b/tests/thumbnail_tests/test_templatetags.py @@ -29,8 +29,8 @@ def test_nested(self): item = Item.objects.get(image='500x500.jpg') val = render_to_string('thumbnail6.html', {'item': item, }).strip() self.assertEqual(val, ( - '' - '' + '' )) @@ -127,7 +127,7 @@ def test_portrait(self): 'dims': 'x66', }).strip() self.assertEqual(val, - '') def test_empty(self): @@ -178,10 +178,10 @@ def test_nested(self): self.assertEqual( val, ( - '' - '' + '' ) ) From 627ce84d33a993a7dd0181c8ddcc3a941875c64d Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Thu, 13 Sep 2018 21:03:48 -0400 Subject: [PATCH 23/27] revert test_alternative_resolutions --- .../test_alternative_resolutions.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/thumbnail_tests/test_alternative_resolutions.py b/tests/thumbnail_tests/test_alternative_resolutions.py index 197a42220..edc1dda7e 100644 --- a/tests/thumbnail_tests/test_alternative_resolutions.py +++ b/tests/thumbnail_tests/test_alternative_resolutions.py @@ -30,28 +30,28 @@ def test_retina(self): get_thumbnail(self.image, '50x50') actions = [ - 'exists: test/cache/b4/54/b45476c738bef768e3c0f8497a889248.jpg', + 'exists: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d.jpg', # save regular resolution, same as in StorageTestCase 'open: retina.jpg', - 'save: test/cache/b4/54/b45476c738bef768e3c0f8497a889248.jpg', - 'get_available_name: test/cache/b4/54/b45476c738bef768e3c0f8497a889248.jpg', - 'exists: test/cache/b4/54/b45476c738bef768e3c0f8497a889248.jpg', + 'save: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d.jpg', + 'get_available_name: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d.jpg', + 'exists: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d.jpg', # save the 1.5x resolution version - 'save: test/cache/b4/54/b45476c738bef768e3c0f8497a889248@1.5x.jpg', - 'get_available_name: test/cache/b4/54/b45476c738bef768e3c0f8497a889248@1.5x.jpg', - 'exists: test/cache/b4/54/b45476c738bef768e3c0f8497a889248@1.5x.jpg', + 'save: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@1.5x.jpg', + 'get_available_name: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@1.5x.jpg', + 'exists: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@1.5x.jpg', # save the 2x resolution version - 'save: test/cache/b4/54/b45476c738bef768e3c0f8497a889248@2x.jpg', - 'get_available_name: test/cache/b4/54/b45476c738bef768e3c0f8497a889248@2x.jpg', - 'exists: test/cache/b4/54/b45476c738bef768e3c0f8497a889248@2x.jpg' + 'save: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@2x.jpg', + 'get_available_name: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@2x.jpg', + 'exists: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@2x.jpg', ] self.assertEqual(self.log, actions) path = os.path.join(settings.MEDIA_ROOT, - 'test/cache/b4/54/b45476c738bef768e3c0f8497a889248@1.5x.jpg') + 'test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@1.5x.jpg') with open(path) as fp: engine = PILEngine() From 01886898f1a1aee6807d07308ffcb81ab942aa37 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Thu, 13 Sep 2018 22:03:14 -0400 Subject: [PATCH 24/27] empty commit --- tests/thumbnail_tests/test_alternative_resolutions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/thumbnail_tests/test_alternative_resolutions.py b/tests/thumbnail_tests/test_alternative_resolutions.py index edc1dda7e..ba723bbec 100644 --- a/tests/thumbnail_tests/test_alternative_resolutions.py +++ b/tests/thumbnail_tests/test_alternative_resolutions.py @@ -56,3 +56,4 @@ def test_retina(self): with open(path) as fp: engine = PILEngine() self.assertEqual(engine.get_image_size(engine.get_image(ImageFile(file_=fp))), (75, 75)) + From b91f190960069b27feae9abbf2306c18a3ca65d5 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Thu, 13 Sep 2018 22:03:21 -0400 Subject: [PATCH 25/27] empty commit --- tests/thumbnail_tests/test_alternative_resolutions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/thumbnail_tests/test_alternative_resolutions.py b/tests/thumbnail_tests/test_alternative_resolutions.py index ba723bbec..edc1dda7e 100644 --- a/tests/thumbnail_tests/test_alternative_resolutions.py +++ b/tests/thumbnail_tests/test_alternative_resolutions.py @@ -56,4 +56,3 @@ def test_retina(self): with open(path) as fp: engine = PILEngine() self.assertEqual(engine.get_image_size(engine.get_image(ImageFile(file_=fp))), (75, 75)) - From cb910b02799affbb134e08204208b31ee86a0a06 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Mon, 1 Jun 2020 13:07:24 -0400 Subject: [PATCH 26/27] updating underline length in readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 4b6072441..25481554e 100644 --- a/README.rst +++ b/README.rst @@ -132,7 +132,7 @@ You can use the 'get_thumbnail':: See more examples in the section `Low level API examples`_ in the Documentation Using in combination with other thumbnailers -======= +============================================ Alternatively, you load the templatetags by {% load sorl_thumbnail %} instead of traditional {% load thumbnail %}. It's especially useful in From cbeb704f9b25d8ff90d17072cfbf495b7130bd1c Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Mon, 1 Jun 2020 13:08:11 -0400 Subject: [PATCH 27/27] extending readme underline --- docs/template.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/template.rst b/docs/template.rst index 7ba994648..a840e7207 100644 --- a/docs/template.rst +++ b/docs/template.rst @@ -137,7 +137,7 @@ you are wondering, sorl-thumbnail sorts the options so the order does not matter, same options but in different order will generate only one thumbnail. ``transform`` -^^^^^^^^^^^ +^^^^^^^^^^^^^ Transform is a boolean and controls if the image will be free transformed to the dimensions provided. If set to true, the image will be forcibly resized to the supplied dimensions and stretch as needed. Default value is ``False``.