def test_6_digit_hex(self):
        fil = Filter(spec='width-400|bgcolor-ffffff')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file(),
        )
        out = fil.run(image, BytesIO())

        self.assertFalse(out.has_alpha())
Пример #2
0
    def test_gif(self):
        fil = Filter(spec='width-400|format-gif')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file(),
        )
        out = fil.run(image, BytesIO())

        self.assertEqual(out.format_name, 'gif')
Пример #3
0
    def test_png(self):
        fil = Filter(spec='width-400|format-png')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file(),
        )
        out = fil.run(image, BytesIO())

        self.assertEqual(out.format_name, 'png')
Пример #4
0
    def test_original_has_alpha(self):
        # Checks that the test image we're using has alpha
        fil = Filter(spec='width-400')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file(),
        )
        out = fil.run(image, BytesIO())

        self.assertTrue(out.has_alpha())
    def test_original_has_alpha(self):
        # Checks that the test image we're using has alpha
        fil = Filter(spec='width-400')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file(),
        )
        out = fil.run(image, BytesIO())

        self.assertTrue(out.has_alpha())
Пример #6
0
    def test_override_webp_convert_to_png(self):
        """WAGTAILIMAGES_FORMAT_CONVERSIONS can be overridden to disable webp conversion"""

        fil = Filter(spec='width-400')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file_webp(),
        )
        out = fil.run(image, BytesIO())

        self.assertEqual(out.format_name, 'webp')
Пример #7
0
    def test_webp_convert_to_png(self):
        """by default, webp images will be converted to png"""

        fil = Filter(spec='width-400')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file_webp(),
        )
        out = fil.run(image, BytesIO())

        self.assertEqual(out.format_name, 'png')
Пример #8
0
    def test_webp_quality_setting(self):
        fil = Filter(spec="width-400|format-webp")
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file_jpeg(),
        )

        f = BytesIO()
        with patch("PIL.Image.Image.save") as save:
            fil.run(image, f)

        save.assert_called_with(f, "WEBP", quality=50, lossless=False)
Пример #9
0
    def test_jpeg_quality_filter_overrides_setting(self):
        fil = Filter(spec='width-400|jpegquality-40')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file_jpeg(),
        )

        f = BytesIO()
        with patch('PIL.Image.Image.save') as save:
            fil.run(image, f)

        save.assert_called_with(f, 'JPEG', quality=40, optimize=True, progressive=True)
Пример #10
0
    def test_jpeg_quality_filter_overrides_setting(self):
        fil = Filter(spec='width-400|jpegquality-40')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file_jpeg(),
        )

        f = BytesIO()
        with patch('PIL.Image.Image.save') as save:
            fil.run(image, f)

        save.assert_called_with(f, 'JPEG', quality=40, optimize=True, progressive=True)
Пример #11
0
    def test_jpeg_quality_filter_overrides_setting(self):
        fil = Filter(spec='width-400|webpquality-40|format-webp')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file_jpeg(),
        )

        f = BytesIO()
        with patch('PIL.Image.Image.save') as save:
            fil.run(image, f)

        save.assert_called_with(f, 'WEBP', quality=40, lossless=False)
Пример #12
0
    def render(self, context):
        try:
            image = self.image_expr.resolve(context)
            mode = self.mode_expr.resolve(context)
            # We call abs() just in case the user set one of the dimensions to a negative number.
            width = abs(int(
                self.width_expr.resolve(context))) if self.width_expr else 1
            height = abs(int(
                self.height_expr.resolve(context))) if self.height_expr else 1
        except template.VariableDoesNotExist:
            return ''

        if not image:
            return ''

        # Build a filter spec based on the specified mode, height, and width for the base rendition.
        if mode == 'width':
            base_spec = "width-{}".format(width)
        elif mode == 'height':
            base_spec = "height-{}".format(height)
        else:
            base_spec = "{}-{}x{}-c100".format(mode, width, height)
        base_rendition = get_rendition_or_not_found(image,
                                                    Filter(spec=base_spec))

        # Build the fallback <img> tag for browsers that don't support <picture>.
        custom_attrs = {
            attr_name: expression.resolve(context)
            for attr_name, expression in self.attrs.items()
        }
        img_tag = base_rendition.img_tag(custom_attrs)

        # If the image is wider than a phone, add an additional, smaller rendition with the same aspect ratio.
        small_width = 425
        # Two notes here:
        # 1) We used 'from __future__ import division' to make this use floating point division.
        # 2) Filter specs don't accept floats, so we need to cast back to int at the end.
        small_height = int(height * (small_width / width))
        if mode == 'width':
            small_spec = "width-{}".format(small_width)
        else:
            # TODO: If the mode is 'height', this might not look right. I'm not really sure, though.
            small_spec = "fill-{}x{}-c100".format(small_width, small_height)
        small_rendition = get_rendition_or_not_found(image,
                                                     Filter(spec=small_spec))

        return """
            <picture>
              <source srcset="{small.url}" media="(max-width: 499px)">
              <source srcset="{full.url}" media="(min-width: 500px)">
              {img_tag}
            </picture>
        """.format(img_tag=img_tag, full=base_rendition, small=small_rendition)
Пример #13
0
def check_settings(app_configs, **kwargs):
    errors = []
    default_filter = settings.WAGTAIL_GRAPHQL_DEFAULT_RENDITION_FILTER
    allowed_filters = settings.WAGTAIL_GRAPHQL_ALLOWED_RENDITION_FILTERS

    from wagtail.images.models import Filter
    from wagtail.images.exceptions import InvalidFilterSpecError

    try:
        Filter(spec=default_filter).operations
    except InvalidFilterSpecError:
        errors.append(
            checks.Error(
                'WAGTAIL_GRAPHQL_DEFAULT_RENDITION_FILTER setting must be a '
                'valid Wagtail image filter.',
                hint='http://docs.wagtail.io/en/stable/topics/images.html',
                id='wagtail_graphql.E001',
            ))

    try:
        for rendition_filter in filter(lambda x: x != '*', allowed_filters):
            Filter(spec=rendition_filter).operations
    except InvalidFilterSpecError:
        errors.append(
            checks.Error(
                'WAGTAIL_GRAPHQL_ALLOWED_RENDITION_FILTERS setting must '
                'be a collection of valid Wagtail image filters.',
                hint=(
                    f'"{rendition_filter}" is an invalid filter. See for more '
                    'information: '
                    'http://docs.wagtail.io/en/stable/topics/images.html', ),
                id='wagtail_graphql.E002',
            ))

    if not isinstance(settings.WAGTAIL_GRAPHQL_ENABLE_IMAGES, bool):
        errors.append(
            checks.Warning(
                'WAGTAIL_GRAPHQL_ENABLE_IMAGES setting must be a boolean, not '
                f'{type(settings.WAGTAIL_GRAPHQL_ENABLE_IMAGES).__name__}.',
                id='wagtail_graphql.W001',
            ))

    if not isinstance(settings.WAGTAIL_GRAPHQL_ENABLE_DOCUMENTS, bool):
        errors.append(
            checks.Warning(
                'WAGTAIL_GRAPHQL_ENABLE_DOCUMENTS setting must be a boolean, '
                'not {}.'.format(
                    type(settings.WAGTAIL_GRAPHQL_ENABLE_DOCUMENTS).__name__),
                id='wagtail_graphql.W002',
            ))

    return errors
Пример #14
0
    def test_webp_lossless(self):
        fil = Filter(spec='width-400|format-webp-lossless')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file(),
        )

        f = BytesIO()
        with patch('PIL.Image.Image.save') as save:
            fil.run(image, f)

        # quality=80 is default for Williw and PIL libs
        save.assert_called_with(f, 'WEBP', quality=80, lossless=True)
Пример #15
0
    def test_cache_key_fill_filter_with_focal_point(self):
        image = Image(
            width=1000,
            height=1000,
            focal_point_width=100,
            focal_point_height=100,
            focal_point_x=500,
            focal_point_y=500,
        )
        fil = Filter(spec='fill-100x100')
        cache_key = fil.get_cache_key(image)

        self.assertEqual(cache_key, '0bbe3b2f')
Пример #16
0
    def test_cache_key_fill_filter_with_focal_point(self):
        image = Image(
            width=1000,
            height=1000,
            focal_point_width=100,
            focal_point_height=100,
            focal_point_x=500,
            focal_point_y=500,
        )
        fil = Filter(spec='fill-100x100')
        cache_key = fil.get_cache_key(image)

        self.assertEqual(cache_key, '0bbe3b2f')
Пример #17
0
    def get_mock_rendition(self, rendition_filter):
        """Create a mock rendition object that wraps the original image.

        Using the template tag {% image image 'original' %} will return an
        <img> tag linking to the original file (instead of a file copy, as
        is default Wagtail behavior).

        Template tags with Wagtail size-related filters (width, height, max,
        and min), e.g. {% image image 'max-165x165' %}, will generate an
        <img> tag with appropriate size parameters, following logic from
        wagtail.images.image_operations.
        """
        if isinstance(rendition_filter, str):
            rendition_filter = Filter(spec=rendition_filter)

        width = self.width
        height = self.height

        for operation in rendition_filter.operations:
            if isinstance(operation, DoNothingOperation):
                continue

            if not any([
                    isinstance(operation, WidthHeightOperation),
                    isinstance(operation, MinMaxOperation),
            ]):
                raise RuntimeError('non-size operations not supported on GIFs')

            width, height = self.apply_size_operation(operation, width, height)

        return CFGOVRendition(image=self,
                              file=self.file,
                              width=width,
                              height=height)
Пример #18
0
    def test_jpeg_quality_setting(self):
        fil = Filter(spec="width-400")
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file_jpeg(),
        )

        f = BytesIO()
        with patch("PIL.Image.Image.save") as save:
            fil.run(image, f)

        save.assert_called_with(f,
                                "JPEG",
                                quality=50,
                                optimize=True,
                                progressive=True)
    def render(self, context):
        try:
            image = self.image_expr.resolve(context)
        except template.VariableDoesNotExist:
            return ''

        if not image:
            if self.output_var_name:
                context[self.output_var_name] = None
            return ''

        if not hasattr(image, 'get_rendition'):
            raise ValueError(
                f"{self.tag_name} tag expected an Image object, got {image!r}")

        filters = [Filter(spec=f) for f in self.raw_filter_specs(context)]
        renditions = get_renditions_or_not_found(image, filters)

        if self.output_var_name:
            # return the rendition object in the given variable
            context[self.output_var_name] = renditions
            return ''
        else:
            # render the rendition's image tag now
            resolved_attrs = {}
            for key in self.attrs:
                resolved_attrs[key] = self.attrs[key].resolve(context)

            return render_to_string('img_srcset_wip.html', {
                "fallback_renditions": renditions,
                "attributes": resolved_attrs
            })
Пример #20
0
    def test_runs_operations(self):
        run_mock = Mock()

        def run(willow, image, env):
            run_mock(willow, image, env)

        self.operation_instance.run = run

        fil = Filter(spec='operation1|operation2')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file(),
        )
        fil.run(image, BytesIO())

        self.assertEqual(run_mock.call_count, 2)
    def test_runs_operations(self):
        run_mock = Mock()

        def run(willow, image, env):
            run_mock(willow, image, env)

        self.operation_instance.run = run

        fil = Filter(spec='operation1|operation2')
        image = Image.objects.create(
            title="Test image",
            file=get_test_image_file(),
        )
        fil.run(image, BytesIO())

        self.assertEqual(run_mock.call_count, 2)
Пример #22
0
    def render(self, context):
        try:
            image = self.image_expr.resolve(context)
            mode = self.mode_expr.resolve(context)
            # We call abs() just in case the user set one of the dimensions to a negative number.
            width = abs(int(
                self.width_expr.resolve(context))) if self.width_expr else 0
            height = abs(int(
                self.height_expr.resolve(context))) if self.height_expr else 0
        except template.VariableDoesNotExist:
            return ''

        if not image:
            return ''

        # Build a filter spec based on the specified mode, height, and width for the base rendition.
        if mode == 'width':
            spec = "width-{}".format(width)
        elif mode == 'height':
            spec = "height-{}".format(height)
        else:
            spec = "{}-{}x{}-c100".format(mode, width, height)
        rendition = get_rendition_or_not_found(image, Filter(spec=spec))

        if self.output_var_name:
            # Save the rendition into the context, instead of outputting a tag.
            context[self.output_var_name] = rendition
            return ''
        else:
            # Resolve custom attrs to their value in this context, then print them within this rendition's <img> tag.
            custom_attrs = {
                attr_name: expression.resolve(context)
                for attr_name, expression in self.attrs.items()
            }
            return rendition.img_tag(custom_attrs)
Пример #23
0
 def test_invalid_length(self):
     fil = Filter(spec='width-400|bgcolor-1234')
     image = Image.objects.create(
         title="Test image",
         file=get_test_image_file(),
     )
     self.assertRaises(ValueError, fil.run, image, BytesIO())
Пример #24
0
 def test_invalid(self):
     fil = Filter(spec='width-400|format-foo')
     image = Image.objects.create(
         title="Test image",
         file=get_test_image_file(),
     )
     self.assertRaises(InvalidFilterSpecError, fil.run, image, BytesIO())
Пример #25
0
 def test_webp_quality_filter_too_big(self):
     fil = Filter(spec='width-400|webpquality-101|format-webp')
     image = Image.objects.create(
         title="Test image",
         file=get_test_image_file_jpeg(),
     )
     self.assertRaises(InvalidFilterSpecError, fil.run, image, BytesIO())
Пример #26
0
 def test_jpeg_quality_filter_no_value(self):
     fil = Filter(spec='width-400|jpegquality')
     image = Image.objects.create(
         title="Test image",
         file=get_test_image_file_jpeg(),
     )
     self.assertRaises(InvalidFilterSpecError, fil.run, image, BytesIO())
Пример #27
0
def get_full_image_url(request, image_id):
    image = get_object_or_404(Image, id=image_id)
    if image:
        filter = Filter(spec='original')
        orig_rendition = image.get_rendition(filter)
        return HttpResponse(orig_rendition.img_tag())
    else:
        return HttpResponse('')
Пример #28
0
def preview(request, image_id, filter_spec):
    image = get_object_or_404(get_image_model(), id=image_id)

    try:
        response = HttpResponse()
        image = Filter(spec=filter_spec).run(image, response)
        response['Content-Type'] = 'image/' + image.format_name
        return response
    except InvalidFilterSpecError:
        return HttpResponse("Invalid filter spec: " + filter_spec, content_type='text/plain', status=400)
Пример #29
0
    def get_placeholder_rendition(self, rendition_filter):
        if isinstance(rendition_filter, str):
            rendition_filter = Filter(spec=rendition_filter)

        available_methods = [
            "width", "height", "fill", "max", "min", "scale", "original"
        ]

        operation = next(filter(lambda x: x.method in available_methods, rendition_filter.operations))
        return Placeholder(spec=operation, img=self)
Пример #30
0
    def get_context(self, value, parent_context=None):
        context = super().get_context(value, parent_context=parent_context)

        if value:
            width = 1110
            img0 = value[0]['image']
            ratio = img0.width / img0.height
            filter = Filter(spec=f'fill-{width}x{int(width/ratio)}')

            context['images'] = [
                (item, get_rendition_or_not_found(item['image'], filter))
                for item in value
            ]
            context['carousel_id'] = f'carousel-{img0.pk}'

        return context
Пример #31
0
 def webp_filter(self):
     """
     Return a ``wagtail.images.models.Filter`` instance
     to use for webp ``source`` rendition.
     By default, lossy webp renditions are created, but
     you can add 'q-100' or 'quality-100' to use
     webp-lossless.
     """
     target_format = "webp"
     spec_list = self.filter_spec.split("|")
     if self.quality:
         if self.quality == "100":
             target_format = "webp-lossless"
         elif "webpquality-" not in self.filter_spec:
             spec_list.append(f"webpquality-{self.quality}")
     spec_list.append(f"format-{target_format}")
     return Filter("|".join(spec_list))
Пример #32
0
def generate_url(request, image_id, filter_spec):
    # Get the image
    Image = get_image_model()
    try:
        image = Image.objects.get(id=image_id)
    except Image.DoesNotExist:
        return JsonResponse({"error": "Cannot find image."}, status=404)

    # Check if this user has edit permission on this image
    if not permission_policy.user_has_permission_for_instance(
            request.user, "change", image):
        return JsonResponse(
            {
                "error":
                "You do not have permission to generate a URL for this image."
            },
            status=403,
        )

    # Parse the filter spec to make sure its valid
    try:
        Filter(spec=filter_spec).operations
    except InvalidFilterSpecError:
        return JsonResponse({"error": "Invalid filter spec."}, status=400)

    # Generate url
    signature = generate_signature(image_id, filter_spec)
    url = reverse("wagtailimages_serve",
                  args=(signature, image_id, filter_spec))

    # Get site root url
    try:
        site_root_url = Site.objects.get(is_default_site=True).root_url
    except Site.DoesNotExist:
        site_root_url = Site.objects.first().root_url

    # Generate preview url
    preview_url = reverse("wagtailimages:preview",
                          args=(image_id, filter_spec))

    return JsonResponse(
        {
            "url": site_root_url + url,
            "preview_url": preview_url
        }, status=200)
Пример #33
0
    def test_uniqueness_constraint(self):
        image = CFGOVImage.objects.create(title='test',
                                          file=get_test_image_file())

        filt = Filter(spec='original')

        def create_rendition(image, filt):
            return CFGOVRendition.objects.create(
                filter_spec=filt.spec,
                image=image,
                file=image.file,
                width=100,
                height=100,
                focal_point_key=filt.get_cache_key(image))

        create_rendition(image=image, filt=filt)
        with self.assertRaises(IntegrityError):
            create_rendition(image=image, filt=filt)
Пример #34
0
 def filter(self):
     """
     Return a ``wagtail.images.models.Filter`` instance
     to use for creating <img> tag rendition. This will
     serve as our IE11/Safari fallback, so needs to be
     a png, jpeg or gif (not webp).
     By default, webp images are convered to png in order
     to preserve transparency. Add 'format-jpeg' to convert
     to jpegs instead.
     """
     target_format = None
     spec_list = self.filter_spec.split("|")
     if self.specified_format:
         target_format = self.specified_format
         if target_format in ("webp", "webp-lossless"):
             target_format = "png"
     elif self.native_format == "webp":
         target_format = "png"
     if target_format:
         spec_list.append(f"format-{target_format}")
     if target_format == "jpeg":
         if "jpegquality-" not in self.filter_spec:
             spec_list.append(f"jpegquality-{self.quality}")
     return Filter("|".join(spec_list))
Пример #35
0
    def test_cache_key(self):
        image = Image(width=1000, height=1000)
        fil = Filter(spec='max-100x100')
        cache_key = fil.get_cache_key(image)

        self.assertEqual(cache_key, '')
Пример #36
0
    def test_cache_key_fill_filter(self):
        image = Image(width=1000, height=1000)
        fil = Filter(spec='fill-100x100')
        cache_key = fil.get_cache_key(image)

        self.assertEqual(cache_key, '2e16d0ba')
Пример #37
0
    def test_cache_key_fill_filter(self):
        image = Image(width=1000, height=1000)
        fil = Filter(spec='fill-100x100')
        cache_key = fil.get_cache_key(image)

        self.assertEqual(cache_key, '2e16d0ba')
Пример #38
0
    def test_cache_key(self):
        image = Image(width=1000, height=1000)
        fil = Filter(spec='max-100x100')
        cache_key = fil.get_cache_key(image)

        self.assertEqual(cache_key, '')