Example #1
0
    def test_16_image_process_format(self):
        """Test the format parameter of image_process."""

        image = tools.base64_to_image(
            tools.image_process(self.base64_1920x1080_jpeg,
                                output_format='PNG'))
        self.assertEqual(image.format, 'PNG', "change format to PNG")

        image = tools.base64_to_image(
            tools.image_process(self.base64_1x1_png, output_format='JpEg'))
        self.assertEqual(image.format, 'JPEG',
                         "change format to JPEG (case insensitive)")

        image = tools.base64_to_image(
            tools.image_process(self.base64_1920x1080_jpeg,
                                output_format='BMP'))
        self.assertEqual(image.format, 'PNG',
                         "change format to BMP converted to PNG")

        self.base64_image_1080_1920_rgba = tools.image_to_base64(
            Image.new('RGBA', (108, 192)), 'PNG')
        image = tools.base64_to_image(
            tools.image_process(self.base64_image_1080_1920_rgba,
                                output_format='jpeg'))
        self.assertEqual(image.format, 'JPEG',
                         "change format PNG with RGBA to JPEG")

        # pass quality to force the image to be processed
        self.base64_image_1080_1920_tiff = tools.image_to_base64(
            Image.new('RGB', (108, 192)), 'TIFF')
        image = tools.base64_to_image(
            tools.image_process(self.base64_image_1080_1920_tiff, quality=95))
        self.assertEqual(image.format, 'JPEG', "unsupported format to JPEG")
Example #2
0
 def test_12_image_process_verify_resolution(self):
     """Test the verify_resolution parameter of image_process."""
     res = tools.image_process(self.base64_1920x1080_jpeg,
                               verify_resolution=True)
     self.assertNotEqual(res, False, "size ok")
     base64_image_excessive = tools.image_to_base64(
         Image.new('RGB', (45001, 1000)), 'PNG')
     with self.assertRaises(ValueError, msg="size excessive"):
         tools.image_process(base64_image_excessive, verify_resolution=True)
Example #3
0
    def test_10_image_process_base64_source(self):
        """Test the base64_source parameter of image_process."""
        wrong_base64 = b'oazdazpodazdpok'

        self.assertFalse(tools.image_process(False),
                         "return False if base64_source is falsy")
        self.assertEqual(tools.image_process(self.base64_svg), self.base64_svg,
                         "return base64_source if format is SVG")

        # in the following tests, pass `quality` to force the processing
        with self.assertRaises(
                UserError,
                msg=
                "This file could not be decoded as an image file. Please try with a different file."
        ):
            tools.image_process(wrong_base64, quality=95)

        with self.assertRaises(
                UserError,
                msg=
                "This file could not be decoded as an image file. Please try with a different file."
        ):
            tools.image_process(b'oazdazpodazdpokd', quality=95)

        image = tools.base64_to_image(
            tools.image_process(self.base64_1920x1080_jpeg, quality=95))
        self.assertEqual(image.size, (1920, 1080), "OK return the image")

        # test that nothing happens if no operation has been requested
        # (otherwise those would raise because of wrong base64)
        self.assertEqual(tools.image_process(wrong_base64), wrong_base64)
        self.assertEqual(tools.image_process(wrong_base64, size=False),
                         wrong_base64)
Example #4
0
    def test_11_image_process_size(self):
        """Test the size parameter of image_process."""

        # Format of `tests`: (original base64 image, size parameter, expected result, text)
        tests = [
            (self.base64_1920x1080_jpeg, (192, 108), (192, 108),
             "resize to given size"),
            (self.base64_1920x1080_jpeg, (1920, 1080), (1920, 1080),
             "same size, no change"),
            (self.base64_1920x1080_jpeg, (192, None), (192, 108),
             "set height from ratio"),
            (self.base64_1920x1080_jpeg, (0, 108), (192, 108),
             "set width from ratio"),
            (self.base64_1920x1080_jpeg, (192, 200), (192, 108),
             "adapt to width"),
            (self.base64_1920x1080_jpeg, (400, 108), (192, 108),
             "adapt to height"),
            (self.base64_1920x1080_jpeg, (3000, 2000), (1920, 1080),
             "don't resize above original, both set"),
            (self.base64_1920x1080_jpeg, (3000, False), (1920, 1080),
             "don't resize above original, width set"),
            (self.base64_1920x1080_jpeg, (None, 2000), (1920, 1080),
             "don't resize above original, height set"),
            (self.base64_1080x1920_png, (3000, 192), (108, 192),
             "vertical image, resize if below"),
        ]

        count = 0
        for test in tests:
            image = tools.base64_to_image(
                tools.image_process(test[0], size=test[1]))
            self.assertEqual(image.size, test[2], test[3])
            count = count + 1
        self.assertEqual(count, 10, "ensure the loop is ran")
Example #5
0
 def attachment_update(self,
                       attachment,
                       name=None,
                       width=0,
                       height=0,
                       quality=0,
                       copy=False,
                       **kwargs):
     if attachment.type == 'url':
         raise UserError(
             _("You cannot change the quality, the width or the name of an URL attachment."
               ))
     if copy:
         attachment = attachment.copy()
     data = {}
     if name:
         data['name'] = name
     try:
         data['datas'] = tools.image_process(attachment.datas,
                                             size=(width, height),
                                             quality=quality)
     except UserError:
         pass  # not an image
     attachment.write(data)
     return attachment._get_media_info()
Example #6
0
    def test_13_image_process_quality(self):
        """Test the quality parameter of image_process."""

        # CASE: PNG RGBA doesn't apply quality, just optimize
        image = tools.image_to_base64(Image.new('RGBA', (1080, 1920)), 'PNG')
        res = tools.image_process(image)
        self.assertLessEqual(len(res), len(image))

        # CASE: PNG RGB doesn't apply quality, just optimize
        image = tools.image_to_base64(Image.new('P', (1080, 1920)), 'PNG')
        res = tools.image_process(image)
        self.assertLessEqual(len(res), len(image))

        # CASE: JPEG optimize + reduced quality
        res = tools.image_process(self.base64_1920x1080_jpeg)
        self.assertLessEqual(len(res), len(self.base64_1920x1080_jpeg))

        # CASE: GIF doesn't apply quality, just optimize
        image = tools.image_to_base64(Image.new('RGB', (1080, 1920)), 'GIF')
        res = tools.image_process(image)
        self.assertLessEqual(len(res), len(image))
Example #7
0
    def test_15_image_process_colorize(self):
        """Test the colorize parameter of image_process."""

        # verify initial condition
        image_rgba = Image.new('RGBA', (1, 1))
        self.assertEqual(image_rgba.mode, 'RGBA')
        self.assertEqual(image_rgba.getpixel((0, 0)), (0, 0, 0, 0))
        base64_rgba = tools.image_to_base64(image_rgba, 'PNG')

        # CASE: color random, color has changed
        image = tools.base64_to_image(
            tools.image_process(base64_rgba, colorize=True))
        self.assertEqual(image.mode, 'RGB')
        self.assertNotEqual(image.getpixel((0, 0)), (0, 0, 0))
Example #8
0
    def get_user_profile_avatar(self,
                                user_id,
                                field='image_256',
                                width=0,
                                height=0,
                                crop=False,
                                **post):
        if field not in ('image_128', 'image_256'):
            return werkzeug.exceptions.Forbidden()

        can_sudo = self._check_avatar_access(user_id, **post)
        if can_sudo:
            status, headers, image_base64 = request.env['ir.http'].sudo(
            ).binary_content(model='res.users',
                             id=user_id,
                             field=field,
                             default_mimetype='image/png')
        else:
            status, headers, image_base64 = request.env[
                'ir.http'].binary_content(model='res.users',
                                          id=user_id,
                                          field=field,
                                          default_mimetype='image/png')
        if status == 301:
            return request.env['ir.http']._response_by_status(
                status, headers, image_base64)
        if status == 304:
            return werkzeug.wrappers.Response(status=304)

        if not image_base64:
            image_base64 = self._get_default_avatar()
            if not (width or height):
                width, height = tools.image_guess_size_from_field_name(field)

        image_base64 = tools.image_process(image_base64,
                                           size=(int(width), int(height)),
                                           crop=crop)

        content = base64.b64decode(image_base64)
        headers = http.set_safe_image_headers(headers, content)
        response = request.make_response(content, headers)
        response.status_code = status
        return response
Example #9
0
 def add_data(self,
              name,
              data,
              quality=0,
              width=0,
              height=0,
              res_id=False,
              res_model='ir.ui.view',
              filters=False,
              **kwargs):
     try:
         data = tools.image_process(data,
                                    size=(width, height),
                                    quality=quality,
                                    verify_resolution=True)
     except UserError:
         pass  # not an image
     attachment = self._attachment_create(name=name,
                                          data=data,
                                          res_id=res_id,
                                          res_model=res_model,
                                          filters=filters)
     return attachment._get_media_info()
Example #10
0
    def save_unsplash_url(self, unsplashurls=None, **kwargs):
        """
            unsplashurls = {
                image_id1: {
                    url: image_url,
                    download_url: download_url,
                },
                image_id2: {
                    url: image_url,
                    download_url: download_url,
                },
                .....
            }
        """
        def slugify(s):
            ''' Keeps only alphanumeric characters, hyphens and spaces from a string.
                The string will also be truncated to 1024 characters max.
                :param s: the string to be filtered
                :return: the sanitized string
            '''
            return "".join([c for c in s
                            if c.isalnum() or c in list("- ")])[:1024]

        if not unsplashurls:
            return []

        uploads = []
        Attachments = request.env['ir.attachment']

        query = kwargs.get('query', '')
        query = slugify(query)

        res_model = kwargs.get('res_model', 'ir.ui.view')
        if res_model != 'ir.ui.view' and kwargs.get('res_id'):
            res_id = int(kwargs['res_id'])
        else:
            res_id = None

        for key, value in unsplashurls.items():
            url = value.get('url')
            try:
                if not url.startswith('https://images.unsplash.com/'):
                    logger.exception("ERROR: Unknown Unsplash URL!: " + url)
                    raise Exception(_("ERROR: Unknown Unsplash URL!"))
                req = requests.get(url)
                if req.status_code != requests.codes.ok:
                    continue

                # get mime-type of image url because unsplash url dosn't contains mime-types in url
                image_base64 = base64.b64encode(req.content)
            except requests.exceptions.ConnectionError as e:
                logger.exception("Connection Error: " + str(e))
                continue
            except requests.exceptions.Timeout as e:
                logger.exception("Timeout: " + str(e))
                continue

            image_base64 = tools.image_process(image_base64,
                                               verify_resolution=True)
            mimetype = guess_mimetype(base64.b64decode(image_base64))
            # append image extension in name
            query += mimetypes.guess_extension(mimetype) or ''

            # /unsplash/5gR788gfd/lion
            url_frags = ['unsplash', key, query]

            attachment = Attachments.create({
                'name': '_'.join(url_frags),
                'url': '/' + '/'.join(url_frags),
                'mimetype': mimetype,
                'datas': image_base64,
                'public': res_model == 'ir.ui.view',
                'res_id': res_id,
                'res_model': res_model,
            })
            attachment.generate_access_token()
            uploads.append(attachment._get_media_info())

            # Notifies Unsplash from an image download. (API requirement)
            self._notify_download(value.get('download_url'))

        return uploads
Example #11
0
    def test_14_image_process_crop(self):
        """Test the crop parameter of image_process."""

        # Optimized PNG use palette, getpixel below will return palette value.
        fill = 0
        bg = 1

        # Format of `tests`: (original base64 image, size parameter, crop parameter, res size, res color (top, bottom, left, right), text)
        tests = [
            (self.base64_1920x1080_png, None, None, (1920, 1080),
             (fill, fill, bg, bg), "horizontal, verify initial"),
            (self.base64_1920x1080_png, (2000, 2000), 'center', (1080, 1080),
             (fill, fill, fill, fill), "horizontal, crop biggest possible"),
            (self.base64_1920x1080_png, (2000, 4000), 'center', (540, 1080),
             (fill, fill, fill,
              fill), "horizontal, size vertical, limit height"),
            (self.base64_1920x1080_png, (4000, 2000), 'center', (1920, 960),
             (fill, fill, bg, bg), "horizontal, size horizontal, limit width"),
            (self.base64_1920x1080_png, (512, 512), 'center', (512, 512),
             (fill, fill, fill, fill), "horizontal, type center"),
            (self.base64_1920x1080_png, (512, 512), 'top', (512, 512),
             (fill, fill, fill, fill), "horizontal, type top"),
            (self.base64_1920x1080_png, (512, 512), 'bottom', (512, 512),
             (fill, fill, fill, fill), "horizontal, type bottom"),
            (self.base64_1920x1080_png, (512, 512), 'wrong', (512, 512),
             (fill, fill, fill,
              fill), "horizontal, wrong crop value, use center"),
            (self.base64_1920x1080_png, (192, 0), None, (192, 108),
             (fill, fill, bg, bg), "horizontal, not cropped, just do resize"),
            (self.base64_1080x1920_png, None, None, (1080, 1920),
             (bg, bg, fill, fill), "vertical, verify initial"),
            (self.base64_1080x1920_png, (2000, 2000), 'center', (1080, 1080),
             (fill, fill, fill, fill), "vertical, crop biggest possible"),
            (self.base64_1080x1920_png, (2000, 4000), 'center', (960, 1920),
             (bg, bg, fill, fill), "vertical, size vertical, limit height"),
            (self.base64_1080x1920_png, (4000, 2000), 'center', (1080, 540),
             (fill, fill, fill,
              fill), "vertical, size horizontal, limit width"),
            (self.base64_1080x1920_png, (512, 512), 'center', (512, 512),
             (fill, fill, fill, fill), "vertical, type center"),
            (self.base64_1080x1920_png, (512, 512), 'top', (512, 512),
             (bg, fill, fill, fill), "vertical, type top"),
            (self.base64_1080x1920_png, (512, 512), 'bottom', (512, 512),
             (fill, bg, fill, fill), "vertical, type bottom"),
            (self.base64_1080x1920_png, (512, 512), 'wrong', (512, 512),
             (fill, fill, fill,
              fill), "vertical, wrong crop value, use center"),
            (self.base64_1080x1920_png, (108, 0), None, (108, 192),
             (bg, bg, fill, fill), "vertical, not cropped, just do resize"),
        ]

        count = 0
        for test in tests:
            count = count + 1
            # process the image, pass quality to make sure the result is palette
            image = tools.base64_to_image(
                tools.image_process(test[0],
                                    size=test[1],
                                    crop=test[2],
                                    quality=95))
            # verify size
            self.assertEqual(image.size, test[3],
                             "%s - correct size" % test[5])

            half_width, half_height = image.size[0] / 2, image.size[1] / 2
            top, bottom, left, right = 0, image.size[1] - 1, 0, image.size[
                0] - 1
            # verify top
            px = (half_width, top)
            self.assertEqual(
                image.getpixel(px), test[4][0],
                "%s - color top (%s, %s)" % (test[5], px[0], px[1]))
            # verify bottom
            px = (half_width, bottom)
            self.assertEqual(
                image.getpixel(px), test[4][1],
                "%s - color bottom (%s, %s)" % (test[5], px[0], px[1]))
            # verify left
            px = (left, half_height)
            self.assertEqual(
                image.getpixel(px), test[4][2],
                "%s - color left (%s, %s)" % (test[5], px[0], px[1]))
            # verify right
            px = (right, half_height)
            self.assertEqual(
                image.getpixel(px), test[4][3],
                "%s - color right (%s, %s)" % (test[5], px[0], px[1]))

        self.assertEqual(count, 2 * 9, "ensure the loop is ran")
Example #12
0
 def _handle_favicon(self, vals):
     if 'favicon' in vals:
         vals['favicon'] = tools.image_process(vals['favicon'],
                                               size=(256, 256),
                                               crop='center',
                                               output_format='ICO')
Example #13
0
 def resize_to_48(b64source):
     if not b64source:
         b64source = base64.b64encode(Binary().placeholder())
     return image_process(b64source, size=(48, 48))