Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: include dpr parameter when generating fixed-width srcset #50

Merged
merged 5 commits into from
Sep 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Will produce the following attribute value, which can then be served to the clie
https://demos.imgix.net/image.png?w=7400&s=ad671301ed4663c3ce6e84cb646acb96 7400w,
https://demos.imgix.net/image.png?w=8192&s=a0fed46e2bbcc70ded13dc629aee5398 8192w

In cases where enough information is provided about an image's dimensions, :code:`create_srcset()` will instead build a srcset that will allow for an image to be served at different resolutions. The parameters taken into consideration when determining if an image is fixed-width are :code:`w`, :code:`h`, and :code:`ar`. By invoking :code:`create_srcset()` with either a width **or** the height and aspect ratio (along with fit=crop, typically) provided, a different srcset will be generated for a fixed-size image instead.
In cases where enough information is provided about an image's dimensions, :code:`create_srcset()` will instead build a srcset that will allow for an image to be served at different resolutions. The parameters taken into consideration when determining if an image is fixed-width are :code:`w`, :code:`h`, and :code:`ar`. By invoking :code:`create_srcset()` with either a width **or** the height and aspect ratio (along with :code:`fit=crop`, typically) provided, a different srcset will be generated for a fixed-size image instead.

.. code-block:: python

Expand All @@ -96,11 +96,11 @@ Will produce the following attribute value:

.. code-block:: html

https://demos.imgix.net/image.png?ar=3%3A2&fit=crop&h=800&s=333a2140375016c2a6d2cf53e189ed90 1x,
https://demos.imgix.net/image.png?ar=3%3A2&fit=crop&h=800&s=333a2140375016c2a6d2cf53e189ed90 2x,
https://demos.imgix.net/image.png?ar=3%3A2&fit=crop&h=800&s=333a2140375016c2a6d2cf53e189ed90 3x,
https://demos.imgix.net/image.png?ar=3%3A2&fit=crop&h=800&s=333a2140375016c2a6d2cf53e189ed90 4x,
https://demos.imgix.net/image.png?ar=3%3A2&fit=crop&h=800&s=333a2140375016c2a6d2cf53e189ed90 5x
https://demos.imgix.net/image.png?ar=3%3A2&dpr=1&fit=crop&h=800&s=6cf5c443d1eb98bc3d96ea569fcef088 1x,
https://demos.imgix.net/image.png?ar=3%3A2&dpr=2&fit=crop&h=800&s=d60a61a5f34545922bd8dff4e53a0555 2x,
https://demos.imgix.net/image.png?ar=3%3A2&dpr=3&fit=crop&h=800&s=590f96aa426f8589eb7e449ebbeb66e7 3x,
https://demos.imgix.net/image.png?ar=3%3A2&dpr=4&fit=crop&h=800&s=c89c2fd3148957647e86cfc32ba20517 4x,
https://demos.imgix.net/image.png?ar=3%3A2&dpr=5&fit=crop&h=800&s=3d73af69d78d49eef0f81b4b5d718a2c 5x

For more information to better understand srcset, we highly recommend `Eric Portis' "Srcset and sizes" article <https://ericportis.com/posts/2014/srcset-sizes/>`_ which goes into depth about the subject.

Expand Down
20 changes: 20 additions & 0 deletions imgix/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,23 @@
r'(?:[a-z\d](?:\-(?=\-*[a-z\d])|[a-z]|\d){0,62}\.)'
r'[a-z\d]{1,63}$'
)
SRCSET_INCREMENT_PERCENTAGE = 8
SRCSET_MAX_SIZE = 8192


def _target_widths():
resolutions = []
prev = 100

def ensure_even(n):
return 2 * round(n/2.0)

while prev <= SRCSET_MAX_SIZE:
resolutions.append(int(ensure_even(prev)))
prev *= 1 + (SRCSET_INCREMENT_PERCENTAGE / 100.0) * 2

resolutions.append(SRCSET_MAX_SIZE)
return resolutions


SRCSET_TARGET_WIDTHS = _target_widths()
29 changes: 7 additions & 22 deletions imgix/urlbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@

from .urlhelper import UrlHelper

from .constants import DOMAIN_PATTERN
from .constants import DOMAIN_PATTERN, SRCSET_TARGET_WIDTHS


SRCSET_INCREMENT_PERCENTAGE = 8
SRCSET_MAX_SIZE = 8192
SRCSET_DPR_TARGET_RATIOS = range(1, 6)


Expand Down Expand Up @@ -151,26 +149,11 @@ def create_srcset(self, path, params=None):
else:
return self._build_srcset_pairs(path, params)

def _target_widths(self):
resolutions = []
prev = 100

def ensure_even(n):
return 2 * round(n/2.0)

while prev <= SRCSET_MAX_SIZE:
resolutions.append(int(ensure_even(prev)))
prev *= 1 + (SRCSET_INCREMENT_PERCENTAGE / 100.0) * 2

resolutions.append(SRCSET_MAX_SIZE)
return resolutions

def _build_srcset_pairs(self, path, params):
srcset = ''
widths = self._target_widths()

for i in range(len(widths)):
current_width = widths[i]
for i in range(len(SRCSET_TARGET_WIDTHS)):
current_width = SRCSET_TARGET_WIDTHS[i]
current_params = params
current_params['w'] = current_width
srcset += self.create_url(path, current_params) \
Expand All @@ -180,10 +163,12 @@ def _build_srcset_pairs(self, path, params):

def _build_srcset_DPR(self, path, params):
srcset = ''
url = self.create_url(path, params)

for i in range(len(SRCSET_DPR_TARGET_RATIOS)):
current_ratio = SRCSET_DPR_TARGET_RATIOS[i]
srcset += url + ' ' + str(current_ratio) + 'x,\n'
current_params = params
current_params['dpr'] = i+1
srcset += self.create_url(path, current_params) \
+ ' ' + str(current_ratio) + 'x,\n'

return srcset[0:-2]
66 changes: 55 additions & 11 deletions tests/test_srcset.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,17 @@ def test_given_width_srcset_is_DPR():
device_pixel_ratio += 1


def test_given_width_srcset_has_dpr_params():
srcset = _default_srcset({'w': 100})
srclist = srcset.split(',')

for i in range(len(srclist)):
src = srclist[i].split(' ')[0]
assert(src.index("dpr=" + str(i+1)))


def test_given_width_signs_urls():
srcset = _default_srcset({'w': 100})
expected_signature = 'b95cfd915f4a198442bff4ce5befe5b8'
srclist = srcset.split(',')

for src in srclist:
Expand All @@ -64,6 +72,12 @@ def test_given_width_signs_urls():

# extract the sign parameter
generated_signature = url[url.index('s=')+2:len(url)]

params = url[url.index('?'):url.index('s=')-1]
signature_base = 'MYT0KEN' + '/image.jpg' + params
expected_signature = hashlib.md5(signature_base
.encode('utf-8')).hexdigest()

assert(expected_signature == generated_signature)


Expand Down Expand Up @@ -94,8 +108,9 @@ def test_given_height_srcset_pairs_within_bounds():
assert(max <= 8192)


def test_given_height_srcset_iterates_18_percent():
increment_allowed = 0.18
# a 17% testing threshold is used to account for rounding
def test_given_height_srcset_iterates_17_percent():
sherwinski marked this conversation as resolved.
Show resolved Hide resolved
increment_allowed = 0.17
srcset = _default_srcset({'h': 100})
srcslist = srcset.split(',')
widths_list = [src.split(' ')[1] for src in srcslist]
Expand Down Expand Up @@ -128,7 +143,7 @@ def test_given_height_srcset_signs_urls():
assert(expected_signature == generated_signature)


def test_given_width_and_height_DPR():
def test_given_width_and_height_is_DPR():
srcset = _default_srcset({'w': 100, 'h': 100})
device_pixel_ratio = 1
srclist = srcset.split(',')
Expand All @@ -140,9 +155,17 @@ def test_given_width_and_height_DPR():
device_pixel_ratio += 1


def test_given_width_and_height_srcset_has_dpr_params():
srcset = _default_srcset({'w': 100, 'h': 100})
srclist = srcset.split(',')

for i in range(len(srclist)):
src = srclist[i].split(' ')[0]
assert(src.index("dpr=" + str(i+1)))


def test_given_width_and_height_signs_urls():
srcset = _default_srcset({'w': 100, 'h': 100})
expected_signature = '1bd495fdddc25957838a8f844b84d3a3'
srclist = srcset.split(',')

for src in srclist:
Expand All @@ -151,6 +174,12 @@ def test_given_width_and_height_signs_urls():

# extract the sign parameter
generated_signature = url[url.index('s=')+2:len(url)]

params = url[url.index('?'):url.index('s=')-1]
signature_base = 'MYT0KEN' + '/image.jpg' + params
expected_signature = hashlib.md5(signature_base
.encode('utf-8')).hexdigest()

assert(expected_signature == generated_signature)


Expand All @@ -173,8 +202,9 @@ def test_given_aspect_ratio_srcset_pairs_within_bounds():
assert(max <= 8192)


def test_given_aspect_ratio_srcset_iterates_18_percent():
increment_allowed = 0.18
# a 17% testing threshold is used to account for rounding
def test_given_aspect_ratio_srcset_iterates_17_percent():
increment_allowed = 0.17
srcset = _default_srcset({'ar': '3:2'})
srcslist = srcset.split(',')
widths_list = [src.split(' ')[1] for src in srcslist]
Expand Down Expand Up @@ -208,7 +238,7 @@ def test_given_aspect_ratio_srcset_signs_urls():


def test_given_aspect_ratio_and_height_srcset_is_DPR():
srcset = _default_srcset({'w': 100})
srcset = _default_srcset({'ar': '3:2', 'h': 500})
device_pixel_ratio = 1
srclist = srcset.split(',')

Expand All @@ -219,9 +249,17 @@ def test_given_aspect_ratio_and_height_srcset_is_DPR():
device_pixel_ratio += 1


def test_given_ratio_and_height_srcset_signs_urls():
srcset = _default_srcset({'w': 100})
expected_signature = 'b95cfd915f4a198442bff4ce5befe5b8'
def test_given_aspect_ratio_and_height_srcset_has_dpr_params():
srcset = _default_srcset({'ar': '3:2', 'h': 500})
srclist = srcset.split(',')

for i in range(len(srclist)):
src = srclist[i].split(' ')[0]
assert(src.index("dpr=" + str(i+1)))


def test_given_aspect_ratio_and_height_srcset_signs_urls():
srcset = _default_srcset({'ar': '3:2', 'h': 500})
srclist = srcset.split(',')

for src in srclist:
Expand All @@ -230,4 +268,10 @@ def test_given_ratio_and_height_srcset_signs_urls():

# extract the sign parameter
generated_signature = url[url.index('s=')+2:len(url)]

params = url[url.index('?'):url.index('s=')-1]
signature_base = 'MYT0KEN' + '/image.jpg' + params
expected_signature = hashlib.md5(signature_base
.encode('utf-8')).hexdigest()

assert(expected_signature == generated_signature)