docs: def iiif_image_api(request, identifier_param, region_param, size_param,
                     rotation_param, quality_param, format_param):
      """ Image repurposing endpoint for IIIF Image API 2.1 """
      ik_image, image = _get_image_or_404(identifier_param, load_image=True)
  
      is_transparent = et_utils.is_transparent(image)
      is_grayscale = image.mode in ('L', 'LA')
  
      # Map format names used for IIIF URL path extension to proper name
      format_mapping = {
          'jpg': 'jpeg',
          'tif': 'tiff',
      }
  
      try:
          # Parse region
          x, y, r_width, r_height = parse_region(
              region_param, image.width, image.height)
  
          # Parse size
          s_width, s_height = parse_size(size_param, r_width, r_height)
  
          # Parse rotation
          is_mirrored, rotation_degrees = \
              parse_rotation(rotation_param, s_width, s_height)
  
          # Parse quality
          quality = parse_quality(quality_param)
  
          # Parse format
          # TODO Add support for unsupported formats (see `parse_format`)
          image_format = os.path.splitext(ik_image.image.name)[1][1:].lower()
          output_format = parse_format(format_param, image_format)
          corrected_format = format_mapping.get(output_format, output_format)
  
          # Redirect to canonical URL if appropriate, per
          # http://iiif.io/api/image/2.1/#canonical-uri-syntax
          canonical_path = make_canonical_path(
              identifier_param, image.width, image.height,
              (x, y, r_width, r_height),  # Region
              (s_width, s_height),  # Size
              (is_mirrored, rotation_degrees),  # Rotation
              quality,
              output_format
          )
          if request.path != canonical_path:
              return HttpResponseRedirect(canonical_path)
  
          # Determine storage file name for item
          if iiif_storage:
              storage_path = build_iiif_file_storage_path(
                  canonical_path, ik_image, iiif_storage)
          else:
              storage_path = None
  
          # Load pre-generated image from storage if one exists and is up-to-date
          # with the original image (per timestampt info embedded in the storage
          # path)
          # TODO The exists lookup is slow for S3 storage, cache metadata?
          # TODO Detect when original image would be unchanged & use it directly?
          if (
              storage_path and
              iiif_storage.exists(storage_path)
          ):
              if is_remote_storage(iiif_storage, storage_path):
                  return HttpResponseRedirect(iiif_storage.url(storage_path))
              else:
                  return FileResponse(
                      iiif_storage.open(storage_path),
                      content_type='image/%s' % corrected_format,
                  )
  
          ##################
          # Generate image #
          ##################
  
          # Apply region
          if x or y or r_width != image.width or r_height != image.height:
              box = (x, y, x + r_width, y + r_height)
              image = image.crop(box)
  
          # Apply size
          if s_width != r_width or s_height != r_height:
              size = (s_width, s_height)
              image = image.resize(size)
  
          # TODO Apply rotation
  
          # Apply quality
          # Much of this is cribbed from easythumbnails' `colorspace` processor
          # TODO Replace with glamkit-imagetools' sRGB colour space converter?
          if quality in ('default', 'color') and not is_grayscale:
              if is_transparent:
                  new_mode = 'RGBA'
              else:
                  new_mode = 'RGB'
          elif is_grayscale or quality == 'gray':
              if is_transparent:
                  new_mode = 'LA'
              else:
                  new_mode = 'L'
          if new_mode != image.mode:
              image = image.convert(new_mode)
  
          # Apply format and "save"
          result_image = BytesIO()
          image.save(result_image, format=corrected_format)
  
          # Save generated image to storage if possible
          if storage_path:
              iiif_storage.save(storage_path, result_image)
  
          if iiif_storage and is_remote_storage(iiif_storage, storage_path):
              return HttpResponseRedirect(iiif_storage.url(storage_path))
          else:
              result_image.seek(0)  # Reset image file in case it's just created
              return FileResponse(
                  result_image.read(),
                  content_type='image/%s' % corrected_format,
              )
      # Handle error conditions per iiif.io/api/image/2.1/#server-responses
      except ClientError, ex:
          return HttpResponseBadRequest(ex.message)  # 400 response
      except UnsupportedError, ex:
          return HttpResponseNotImplemented(ex.message)