def create_persona_preview_images(src, full_dst, **kw):
    """
    Creates a 680x100 thumbnail used for the Persona preview and
    a 32x32 thumbnail used for search suggestions/detail pages.
    """
    log.info('[1@None] Resizing persona images: %s' % full_dst)
    preview, full = amo.PERSONA_IMAGE_SIZES['header']
    preview_w, preview_h = preview
    orig_w, orig_h = full
    with storage.open(src) as fp:
        i_orig = i = Image.open(fp)

        # Crop image from the right.
        i = i.crop((orig_w - (preview_w * 2), 0, orig_w, orig_h))

        # Resize preview.
        i = i.resize(preview, Image.ANTIALIAS)
        i.load()
        with storage.open(full_dst[0], 'wb') as fp:
            i.save(fp, 'png')

        _, icon_size = amo.PERSONA_IMAGE_SIZES['icon']
        icon_w, icon_h = icon_size

        # Resize icon.
        i = i_orig
        i.load()
        i = i.crop((orig_w - (preview_h * 2), 0, orig_w, orig_h))
        i = i.resize(icon_size, Image.ANTIALIAS)
        i.load()
        with storage.open(full_dst[1], 'wb') as fp:
            i.save(fp, 'png')
    pngcrush_image(full_dst[0])
    pngcrush_image(full_dst[1])
    return True
Exemple #2
0
def generate_static_theme_preview(theme_manifest, version_pk):
    tmpl = loader.get_template(
        'devhub/addons/includes/static_theme_preview_svg.xml')
    file_ = File.objects.filter(version_id=version_pk).first()
    if not file_:
        return
    context = _build_static_theme_preview_context(theme_manifest, file_)
    sizes = sorted(
        amo.THEME_PREVIEW_SIZES.values(),
        lambda x, y: x['position'] - y['position'])
    for size in sizes:
        # Create a Preview for this size.
        preview = VersionPreview.objects.create(
            version_id=version_pk, position=size['position'])
        # Add the size to the context and render
        context.update(svg_render_size=size['full'])
        svg = tmpl.render(context).encode('utf-8')
        if write_svg_to_png(svg, preview.image_path):
            resize_image(
                preview.image_path, preview.thumbnail_path, size['thumbnail'])
            pngcrush_image(preview.image_path)
            preview_sizes = {}
            preview_sizes['image'] = size['full']
            preview_sizes['thumbnail'] = size['thumbnail']
            preview.update(sizes=preview_sizes)
    addon_id = Version.objects.values_list(
        'addon_id', flat=True).get(id=version_pk)
    index_addons.delay([addon_id])
Exemple #3
0
def pngcrush_existing_preview(preview_id):
    """
    Call pngcrush_image() on the images of a given add-on Preview object.
    """
    log.info('Crushing images for Preview %s', preview_id)
    preview = Preview.objects.get(pk=preview_id)
    pngcrush_image(preview.thumbnail_path)
    pngcrush_image(preview.image_path)
Exemple #4
0
def render_to_png(template, context, preview, thumbnail_dimensions):
    svg = template.render(context).encode('utf-8')
    if write_svg_to_png(svg, preview.image_path):
        resize_image(
            preview.image_path,
            preview.thumbnail_path,
            thumbnail_dimensions,
            format=preview.get_format('thumbnail'),
            quality=35,  # It's ignored for png format, so it's fine to always set.
        )
        pngcrush_image(preview.image_path)
        return True
def save_persona_image(src, full_dst, **kw):
    """Creates a PNG of a Persona header image."""
    log.info('[1@None] Saving persona image: %s' % full_dst)
    img = ImageCheck(storage.open(src))
    if not img.is_image():
        log.error('Not an image: %s' % src, exc_info=True)
        return
    with storage.open(src, 'rb') as fp:
        i = Image.open(fp)
        with storage.open(full_dst, 'wb') as fp:
            i.save(fp, 'png')
    pngcrush_image(full_dst)
    return True
def save_persona_image(src, full_dst, **kw):
    """Creates a PNG of a Persona header image."""
    log.info('[1@None] Saving persona image: %s' % full_dst)
    img = ImageCheck(storage.open(src))
    if not img.is_image():
        log.error('Not an image: %s' % src, exc_info=True)
        return
    with storage.open(src, 'rb') as fp:
        i = Image.open(fp)
        with storage.open(full_dst, 'wb') as fp:
            i.save(fp, 'png')
    pngcrush_image(full_dst)
    return True
Exemple #7
0
def generate_static_theme_preview(theme_manifest, header_root, preview):
    tmpl = loader.get_template(
        'devhub/addons/includes/static_theme_preview_svg.xml')
    context = {'amo': amo}
    context.update({
        process_color_value(prop, color)
        for prop, color in theme_manifest.get('colors', {}).items()
    })
    images_dict = theme_manifest.get('images', {})
    header_url = images_dict.get('headerURL',
                                 images_dict.get('theme_frame', ''))

    header_src, header_width, header_height = encode_header_image(
        os.path.join(header_root, header_url))
    meet_or_slice = ('meet' if header_width < amo.THEME_PREVIEW_SIZE.width else
                     'slice')
    preserve_aspect_ratio = '%s %s' % ('xMaxYMin', meet_or_slice)
    context.update(header_src=header_src,
                   header_src_height=header_height,
                   preserve_aspect_ratio=preserve_aspect_ratio)

    # Limit the srcs rendered to 15 to ameliorate DOSing somewhat.
    # https://bugzilla.mozilla.org/show_bug.cgi?id=1435191 for background.
    additional_srcs = images_dict.get('additional_backgrounds', [])[:15]
    additional_alignments = (theme_manifest.get('properties', {}).get(
        'additional_backgrounds_alignment', []))
    additional_tiling = (theme_manifest.get('properties', {}).get(
        'additional_backgrounds_tiling', []))
    additional_backgrounds = [
        AdditionalBackground(path, alignment, tiling, header_root)
        for (path, alignment, tiling) in izip_longest(
            additional_srcs, additional_alignments, additional_tiling)
        if path is not None
    ]
    context.update(additional_backgrounds=additional_backgrounds)

    svg = tmpl.render(context).encode('utf-8')
    image_size = write_svg_to_png(svg, preview.image_path)
    if image_size:
        pngcrush_image(preview.image_path)
        sizes = {
            # We mimic what resize_preview() does, but in our case, 'image'
            # dimensions are not amo.ADDON_PREVIEW_SIZES[1] but something
            # specific to static themes automatic preview.
            'image':
            image_size,
            'thumbnail':
            resize_image(preview.image_path, preview.thumbnail_path,
                         amo.ADDON_PREVIEW_SIZES[0])[0]
        }
        preview.update(sizes=sizes)
Exemple #8
0
def generate_static_theme_preview(theme_manifest, version_pk):
    # Make sure we import `index_addons` late in the game to avoid having
    # a "copy" of it here that won't get mocked by our ESTestCase
    from olympia.addons.tasks import index_addons

    tmpl = loader.get_template(
        'devhub/addons/includes/static_theme_preview_svg.xml')
    file_ = File.objects.filter(version_id=version_pk).first()
    if not file_:
        return
    context = _build_static_theme_preview_context(theme_manifest, file_)
    renderings = sorted(amo.THEME_PREVIEW_RENDERINGS.values(),
                        key=operator.itemgetter('position'))
    colors = None
    for rendering in renderings:
        # Create a Preview for this size.
        preview = VersionPreview.objects.create(
            version_id=version_pk,
            position=rendering['position'],
            sizes={'thumbnail_format': rendering['thumbnail_format']},
        )
        # Add the size to the context and render
        context.update(svg_render_size=rendering['full'])
        svg = tmpl.render(context).encode('utf-8')
        if write_svg_to_png(svg, preview.image_path):
            resize_image(
                preview.image_path,
                preview.thumbnail_path,
                rendering['thumbnail'],
                format=rendering['thumbnail_format'],
                quality=
                35,  # It's ignored for png format, so it's fine to always set.
            )
            pngcrush_image(preview.image_path)
            # Extract colors once and store it for all previews.
            # Use the thumbnail for extra speed, we don't need to be super accurate.
            if colors is None:
                colors = extract_colors_from_image(preview.thumbnail_path)
            data = {
                'sizes': {
                    'image': rendering['full'],
                    'thumbnail': rendering['thumbnail'],
                    'thumbnail_format': rendering['thumbnail_format'],
                },
                'colors': colors,
            }
            preview.update(**data)
    addon_id = Version.objects.values_list('addon_id',
                                           flat=True).get(id=version_pk)
    index_addons.delay([addon_id])
Exemple #9
0
def test_pngcrush_image(subprocess_mock):
    subprocess_mock.Popen.return_value.communicate.return_value = ('', '')
    subprocess_mock.Popen.return_value.returncode = 0  # success
    assert pngcrush_image('/tmp/some_file.png')
    assert subprocess_mock.Popen.call_count == 1
    assert subprocess_mock.Popen.call_args_list[0][0][0] == [
        settings.PNGCRUSH_BIN, '-q', '-reduce', '-ow',
        '/tmp/some_file.png', '/tmp/some_file.crush.png',
    ]
    assert subprocess_mock.Popen.call_args_list[0][1] == {
        'stdout': subprocess_mock.PIPE,
        'stderr': subprocess_mock.PIPE,
    }

    # Make sure that exceptions for this are silent.
    subprocess_mock.Popen.side_effect = Exception
    assert not pngcrush_image('/tmp/some_other_file.png')
Exemple #10
0
def test_pngcrush_image(subprocess_mock):
    subprocess_mock.Popen.return_value.communicate.return_value = ('', '')
    subprocess_mock.Popen.return_value.returncode = 0  # success
    assert pngcrush_image('/tmp/some_file.png')
    assert subprocess_mock.Popen.call_count == 1
    assert subprocess_mock.Popen.call_args_list[0][0][0] == [
        settings.PNGCRUSH_BIN, '-q', '-reduce', '-ow',
        '/tmp/some_file.png', '/tmp/some_file.crush.png',
    ]
    assert subprocess_mock.Popen.call_args_list[0][1] == {
        'stdout': subprocess_mock.PIPE,
        'stderr': subprocess_mock.PIPE,
    }

    # Make sure that exceptions for this are silent.
    subprocess_mock.Popen.side_effect = Exception
    assert not pngcrush_image('/tmp/some_other_file.png')
def pngcrush_existing_icons(addon_id):
    """
    Call pngcrush_image() on the icons of a given add-on.
    """
    log.info('Crushing icons for add-on %s', addon_id)
    addon = Addon.objects.get(pk=addon_id)
    if addon.icon_type != 'image/png':
        log.info('Aborting icon crush for add-on %s, icon type is not a PNG.', addon_id)
        return
    icon_dir = addon.get_icon_dir()
    pngcrush_image(os.path.join(icon_dir, '%s-64.png' % addon_id))
    pngcrush_image(os.path.join(icon_dir, '%s-32.png' % addon_id))
    # Return an icon hash that set_modified_on decorator will set on the add-on
    # after a small delay. This is normally done with the true md5 hash of the
    # original icon, but we don't necessarily have it here. We could read one
    # of the icons we modified but it does not matter just fake a hash to
    # indicate it was "manually" crushed.
    return {'icon_hash': 'mcrushed'}
Exemple #12
0
def generate_static_theme_preview(theme_manifest, header_root, version_pk):
    tmpl = loader.get_template(
        'devhub/addons/includes/static_theme_preview_svg.xml')
    context = _build_static_theme_preview_context(theme_manifest, header_root)
    for size in sorted(amo.THEME_PREVIEW_SIZES.values()):
        # Create a Preview for this size.
        preview = VersionPreview.objects.create(
            version_id=version_pk, position=size['position'])
        # Add the size to the context and render
        context.update(svg_render_size=size['full'])
        svg = tmpl.render(context).encode('utf-8')
        if write_svg_to_png(svg, preview.image_path):
            resize_image(
                preview.image_path, preview.thumbnail_path, size['thumbnail'])
            pngcrush_image(preview.image_path)
            preview_sizes = {}
            preview_sizes['image'] = size['full']
            preview_sizes['thumbnail'] = size['thumbnail']
            preview.update(sizes=preview_sizes)
def generate_static_theme_preview(theme_manifest, header_root, version_pk):
    tmpl = loader.get_template(
        'devhub/addons/includes/static_theme_preview_svg.xml')
    context = _build_static_theme_preview_context(theme_manifest, header_root)
    for size in sorted(amo.THEME_PREVIEW_SIZES.values()):
        # Create a Preview for this size.
        preview = VersionPreview.objects.create(version_id=version_pk,
                                                position=size['position'])
        # Add the size to the context and render
        context.update(svg_render_size=size['full'])
        svg = tmpl.render(context).encode('utf-8')
        if write_svg_to_png(svg, preview.image_path):
            resize_image(preview.image_path, preview.thumbnail_path,
                         size['thumbnail'])
            pngcrush_image(preview.image_path)
            preview_sizes = {}
            preview_sizes['image'] = size['full']
            preview_sizes['thumbnail'] = size['thumbnail']
            preview.update(sizes=preview_sizes)
Exemple #14
0
def generate_static_theme_preview(theme_manifest, version_pk):
    # Make sure we import `index_addons` late in the game to avoid having
    # a "copy" of it here that won't get mocked by our ESTestCase
    from olympia.addons.tasks import index_addons

    tmpl = loader.get_template(
        'devhub/addons/includes/static_theme_preview_svg.xml')
    file_ = File.objects.filter(version_id=version_pk).first()
    if not file_:
        return
    context = _build_static_theme_preview_context(theme_manifest, file_)
    sizes = sorted(
        amo.THEME_PREVIEW_SIZES.values(), key=operator.itemgetter('position'))
    colors = None
    for size in sizes:
        # Create a Preview for this size.
        preview = VersionPreview.objects.create(
            version_id=version_pk, position=size['position'])
        # Add the size to the context and render
        context.update(svg_render_size=size['full'])
        svg = tmpl.render(context).encode('utf-8')
        if write_svg_to_png(svg, preview.image_path):
            resize_image(
                preview.image_path, preview.thumbnail_path, size['thumbnail'])
            pngcrush_image(preview.image_path)
            # Extract colors once and store it for all previews.
            # Use the thumbnail for extra speed, we don't need to be super
            # accurate.
            if colors is None:
                colors = extract_colors_from_image(preview.thumbnail_path)
            data = {
                'sizes': {
                    'image': size['full'],
                    'thumbnail': size['thumbnail'],
                },
                'colors': colors,
            }
            preview.update(**data)
    addon_id = Version.objects.values_list(
        'addon_id', flat=True).get(id=version_pk)
    index_addons.delay([addon_id])
Exemple #15
0
def pngcrush_existing_icons(addon_id):
    """
    Call pngcrush_image() on the icons of a given add-on.
    """
    log.info('Crushing icons for add-on %s', addon_id)
    addon = Addon.objects.get(pk=addon_id)
    if addon.icon_type != 'image/png':
        log.info('Aborting icon crush for add-on %s, icon type is not a PNG.',
                 addon_id)
        return
    icon_dir = addon.get_icon_dir()
    pngcrush_image(os.path.join(icon_dir, '%s-64.png' % addon_id))
    pngcrush_image(os.path.join(icon_dir, '%s-32.png' % addon_id))
    # Return an icon hash that set_modified_on decorator will set on the add-on
    # after a small delay. This is normally done with the true md5 hash of the
    # original icon, but we don't necessarily have it here. We could read one
    # of the icons we modified but it does not matter just fake a hash to
    # indicate it was "manually" crushed.
    return {
        'icon_hash': 'mcrushed'
    }
Exemple #16
0
def generate_static_theme_preview(theme_manifest, header_root, preview_pk):
    preview = VersionPreview.objects.get(pk=preview_pk)
    tmpl = loader.get_template(
        'devhub/addons/includes/static_theme_preview_svg.xml')
    context = _build_static_theme_preview_context(theme_manifest, header_root)

    # Then add the size and render
    context.update(svg_render_size=amo.THEME_PREVIEW_SIZES['full'])
    svg = tmpl.render(context).encode('utf-8')
    preview_sizes = {}
    if write_svg_to_png(svg, preview.image_path):
        pngcrush_image(preview.image_path)
        preview_sizes['image'] = amo.THEME_PREVIEW_SIZES['full']

    # Then rerender at a different size
    context.update(svg_render_size=amo.THEME_PREVIEW_SIZES['thumb'])
    svg = tmpl.render(context).encode('utf-8')
    if write_svg_to_png(svg, preview.thumbnail_path):
        pngcrush_image(preview.thumbnail_path)
        preview_sizes['thumbnail'] = amo.THEME_PREVIEW_SIZES['thumb']

    if preview_sizes:
        preview.update(sizes=preview_sizes)
Exemple #17
0
def generate_static_theme_preview(theme_manifest, version_pk):
    tmpl = loader.get_template(
        'devhub/addons/includes/static_theme_preview_svg.xml')
    file_ = File.objects.filter(version_id=version_pk).first()
    if not file_:
        return
    context = _build_static_theme_preview_context(theme_manifest, file_)
    sizes = sorted(amo.THEME_PREVIEW_SIZES.values(),
                   lambda x, y: x['position'] - y['position'])
    colors = None
    for size in sizes:
        # Create a Preview for this size.
        preview = VersionPreview.objects.create(version_id=version_pk,
                                                position=size['position'])
        # Add the size to the context and render
        context.update(svg_render_size=size['full'])
        svg = tmpl.render(context).encode('utf-8')
        if write_svg_to_png(svg, preview.image_path):
            resize_image(preview.image_path, preview.thumbnail_path,
                         size['thumbnail'])
            pngcrush_image(preview.image_path)
            # Extract colors once and store it for all previews.
            # Use the thumbnail for extra speed, we don't need to be super
            # accurate.
            if colors is None:
                colors = extract_colors_from_image(preview.thumbnail_path)
            data = {
                'sizes': {
                    'image': size['full'],
                    'thumbnail': size['thumbnail'],
                },
                'colors': colors,
            }
            preview.update(**data)
    addon_id = Version.objects.values_list('addon_id',
                                           flat=True).get(id=version_pk)
    index_addons.delay([addon_id])
Exemple #18
0
def pngcrush_existing_theme(persona_id):
    """
    Call pngcrush_image() on the images of a given Persona object.
    """
    log.info('Crushing images for Persona %s', persona_id)
    persona = Persona.objects.get(pk=persona_id)
    # Only do this on "new" Personas with persona_id = 0, the older ones (with
    # a persona_id) have jpeg and not pngs.
    if not persona.is_new():
        log.info('Aborting images crush for Persona %s (too old).', persona_id)
        return
    pngcrush_image(persona.preview_path)
    # No need to crush thumb_path, it's the same as preview_path for "new"
    # Personas.
    pngcrush_image(persona.icon_path)
    if persona.header:
        pngcrush_image(persona.header_path)
    if persona.footer:
        pngcrush_image(persona.footer_path)
Exemple #19
0
def pngcrush_existing_theme(persona_id):
    """
    Call pngcrush_image() on the images of a given Persona object.
    """
    log.info('Crushing images for Persona %s', persona_id)
    persona = Persona.objects.get(pk=persona_id)
    # Only do this on "new" Personas with persona_id = 0, the older ones (with
    # a persona_id) have jpeg and not pngs.
    if not persona.is_new():
        log.info('Aborting images crush for Persona %s (too old).', persona_id)
        return
    pngcrush_image(persona.preview_path)
    # No need to crush thumb_path, it's the same as preview_path for "new"
    # Personas.
    pngcrush_image(persona.icon_path)
    if persona.header:
        pngcrush_image(persona.header_path)
    if persona.footer:
        pngcrush_image(persona.footer_path)