Exemplo n.º 1
0
    def send_free_card(self,
                       postcard,
                       mock_send=False,
                       image_export=False,
                       **kwargs):
        if not postcard:
            raise PostcardCreatorException('Postcard must be set')
        postcard.validate()

        # XXX: endpoint no longer supports user specified w/h
        kwargs['image_target_width'] = 1819
        kwargs['image_quality_factor'] = 1
        kwargs['image_target_height'] = 1311
        img_base64 = base64.b64encode(
            rotate_and_scale_image(postcard.picture_stream,
                                   img_format='jpeg',
                                   image_export=image_export,
                                   enforce_size=True,
                                   **kwargs)).decode('ascii')
        img_text_base64 = base64.b64encode(
            self.create_text_cover(postcard.message)).decode('ascii')
        endpoint = '/card/upload'
        payload = {
            'lang': 'en',
            'paid': False,
            'recipient': _format_recipient(postcard.recipient),
            'sender': _format_sender(postcard.sender),
            'text': '',
            'textImage': img_text_base64,  # jpeg, JFIF standard 1.01, 720x744
            'image': img_base64,  # jpeg, JFIF standard 1.01, 1819x1311
            'stamp': None
        }

        if mock_send:
            copy = dict(payload)
            copy['textImage'] = 'omitted'
            copy['image'] = 'omitted'
            logger.info(
                f'mock_send=True, endpoint: {endpoint}, payload: {copy}')
            return False

        if not self.has_free_postcard():
            raise PostcardCreatorException(
                'Limit of free postcards exceeded. Try again tomorrow at ' +
                self.get_quota()['next'])

        payload = self._do_op('post', endpoint, json=payload).json()
        logger.debug(f'{endpoint} with response {payload}')

        self._validate_model_response(endpoint, payload)

        logger.info(
            f'postcard submitted, orderid {payload["model"].get("orderId")}')
        return payload['model']
Exemplo n.º 2
0
def create_text_image(text, image_export=False, **kwargs):
    """
    Create a jpg with given text and return in bytes format
    """
    text_canvas_w = 720
    text_canvas_h = 744
    text_canvas_bg = 'white'
    text_canvas_fg = 'black'
    text_canvas_font_name = 'open_sans_emoji.ttf'

    def load_font(size):
        return ImageFont.truetype(
            pkg_resources.resource_stream(__name__, text_canvas_font_name),
            size)

    def find_optimal_size(msg,
                          min_size=20,
                          max_size=400,
                          min_line_w=1,
                          max_line_w=80,
                          padding=0):
        """
        Find optimal font size and line width for a given text
        """

        if min_line_w >= max_line_w:
            raise Exception(
                "illegal arguments, min_line_w < max_line_w needed")

        def line_width(font_size, line_padding=70):
            l = min_line_w
            r = max_line_w
            font = load_font(font_size)
            while l < r:
                n = floor((l + r) / 2)
                t = ''.join([char * n for char in '1'])
                font_w, font_h = font.getsize(t)
                font_w = font_w + (2 * line_padding)
                if font_w >= text_canvas_w:
                    r = n - 1
                    pass
                else:
                    l = n + 1
                    pass
            return n

        size_l = min_size
        size_r = max_size
        last_line_w = 0
        last_size = 0

        while size_l < size_r:
            size = floor((size_l + size_r) / 2.0)
            last_size = size

            line_w = line_width(size)
            last_line_w = line_w

            lines = []
            for line in msg.splitlines():
                cur_lines = textwrap.wrap(line, width=line_w)
                for cur_line in cur_lines:
                    lines.append(cur_line)

            font = load_font(size)
            total_w, line_h = font.getsize(msg)
            tot_height = len(lines) * line_h

            if tot_height + (2 * padding) < text_canvas_h:
                start_y = (text_canvas_h - tot_height) / 2
            else:
                start_y = 0

            if start_y == 0:
                size_r = size - 1
            else:
                # does fit
                size_l = size + 1

        return last_size, last_line_w

    def center_y(lines, font_h):
        tot_height = len(lines) * font_h
        if tot_height < text_canvas_h:
            return (text_canvas_h - tot_height) // 2
        else:
            return 0

    size, line_w = find_optimal_size(text, padding=50)
    logger.debug(f'using font with size: {size}, width: {line_w}')

    font = load_font(size)
    font_w, font_h = font.getsize(text)

    lines = []
    for line in text.splitlines():
        cur_lines = textwrap.wrap(line, width=line_w)
        for cur_line in cur_lines:
            lines.append(cur_line)
    text_y_start = center_y(lines, font_h)

    canvas = Image.new('RGB', (text_canvas_w, text_canvas_h), text_canvas_bg)
    draw = ImageDraw.Draw(canvas)
    for line in lines:
        width, height = font.getsize(line)
        draw.text(((text_canvas_w - width) // 2, text_y_start),
                  line,
                  font=font,
                  fill=text_canvas_fg,
                  embedded_color=True)
        text_y_start += (height)

    if image_export:
        name = strftime("postcard_creator_export_%Y-%m-%d_%H-%M-%S_text.jpg",
                        gmtime())
        path = os.path.join(_get_trace_postcard_sent_dir(), name)
        logger.info('exporting image to {} (image_export=True)'.format(path))
        canvas.save(path)

    img_byte_arr = io.BytesIO()
    canvas.save(img_byte_arr, format='jpeg')
    return img_byte_arr.getvalue()
Exemplo n.º 3
0
def rotate_and_scale_image(
        file,
        image_target_width=154,
        image_target_height=111,
        image_quality_factor=20,
        image_rotate=True,
        image_export=False,
        enforce_size=False,  # = True, will not make image smaller than given w/h
        img_format='PNG',
        **kwargs):
    with Image.open(file) as image:
        if image_rotate and image.width < image.height:
            image = image.rotate(90, expand=True)
            logger.debug('rotating image by 90 degrees')

        if not enforce_size and \
                (image.width < image_quality_factor * image_target_width
                 or image.height < image_quality_factor * image_target_height):
            factor_width = image.width / image_target_width
            factor_height = image.height / image_target_height
            factor = min([factor_height, factor_width])

            logger.debug('image is smaller than default for resize/fill. '
                         'using scale factor {} instead of {}'.format(
                             factor, image_quality_factor))
            image_quality_factor = factor

        width = image_target_width * image_quality_factor
        height = image_target_height * image_quality_factor
        logger.debug('resizing image from {}x{} to {}x{}'.format(
            image.width, image.height, width, height))

        # XXX: swissid endpoint expect specific size for postcard
        # if we have an image which is too small, do not upsample but rather center image and fill
        # with boundary color which is most dominant color in image
        try:
            cover = resizeimage.resize_cover(image, [width, height])
        except Exception as e:
            logger.warning(e)
            logger.warning(
                f'resizing image from {image.width}x{image.height} to {width}x{height} failed.'
                f' using resize_contain mode as a fallback. Expect boundaries around img'
            )

            color_thief = ColorThief(file)
            (r, g, b) = color_thief.get_color(quality=1)
            color = (r, g, b, 0)
            cover = resizeimage.resize_contain(image, [width, height],
                                               bg_color=color)
            image_export = True
            logger.warning(
                f"using image boundary color {color}, exporting image for visual inspection."
            )

        cover = cover.convert("RGB")
        with io.BytesIO() as f:
            cover.save(f, img_format)
            scaled = f.getvalue()

        if image_export:
            name = strftime(
                "postcard_creator_export_%Y-%m-%d_%H-%M-%S_cover.jpg",
                gmtime())
            path = os.path.join(_get_trace_postcard_sent_dir(), name)
            logger.info(
                'exporting image to {} (image_export=True)'.format(path))
            cover.save(path)

    return scaled