Exemple #1
0
def mixin_exists(namespace, name):
    expect_type(name, String)
    # TODO invasive, but there's no other way to ask for this at the moment
    for fname, arity in namespace._mixins.keys():
        if name.value == fname:
            return Boolean(True)
    return Boolean(False)
Exemple #2
0
def variable_exists(namespace, name):
    expect_type(name, String)
    try:
        namespace.variable('$' + name.value)
    except KeyError:
        return Boolean(False)
    else:
        return Boolean(True)
Exemple #3
0
def parse_bareword(word):
    if word in COLOR_NAMES:
        return Color.from_name(word)
    elif word == 'null':
        return Null()
    elif word == 'undefined':
        return Undefined()
    elif word == 'true':
        return Boolean(True)
    elif word == 'false':
        return Boolean(False)
    else:
        return String(word, quotes=None)
Exemple #4
0
def global_variable_exists(namespace, name):
    expect_type(name, String)

    # TODO this is...  imperfect and invasive, but should be a good
    # approximation
    scope = namespace._variables
    while len(scope.maps) > 1:
        scope = scope.maps[-1]

    try:
        scope['$' + name.value]
    except KeyError:
        return Boolean(False)
    else:
        return Boolean(True)
Exemple #5
0
    def from_bareword(cls, word):
        if word in COLOR_NAMES:
            value = Color.from_name(word)
        elif word == 'null':
            value = Null()
        elif word == 'undefined':
            value = Undefined()
        elif word == 'true':
            value = Boolean(True)
        elif word == 'false':
            value = Boolean(False)
        else:
            value = String(word, quotes=None)

        return cls(value)
Exemple #6
0
    def style_scss(self, *path):
        css_namespace = Namespace()
        for key, value in self.settings['keys'].items():
            if isinstance(value, LCText):
                css_value = String(value)
            elif isinstance(value, LCColour):
                css_value = Color.from_hex(value)
            elif isinstance(value, LCBool):
                css_value = Boolean(value.simple())
            elif isinstance(value, LCSpin):
                css_value = Number(value.simple())
            else:
                raise ValueError("Unable to find comparable values")
            css_namespace.set_variable('${}'.format(key), css_value)

        cherrypy.response.headers['Content-Type'] = 'text/css'
        with open(os.path.join(self.settings['location'], *path), 'r') as css:
            css_content = css.read()
            compiler = Compiler(namespace=css_namespace, output_style='nested')
            # Something wrong with PyScss,
            #  Syntax error: Found u'100%' but expected one of ADD.
            # Doesn't happen on next attempt, so we are doing bad thing
            attempts = 0
            while attempts < 100:
                try:
                    attempts += 1
                    ret_string = compiler.compile_string(css_content)
                    return ret_string
                except Exception as exc:
                    if attempts == 100:
                        log.debug(exc)
Exemple #7
0
def blank(*objs):
    """Returns true when the object is false, an empty string, or an empty list"""
    for o in objs:
        if isinstance(o, Boolean):
            is_blank = not o
        elif isinstance(o, String):
            is_blank = not len(o.value.strip())
        elif isinstance(o, List):
            is_blank = all(blank(el) for el in o)
        else:
            is_blank = False

        if not is_blank:
            return Boolean(False)

    return Boolean(True)
Exemple #8
0
def has_sprite(map, sprite):
    map = map.render()
    sprite_map = sprite_maps.get(map)
    sprite_name = String.unquoted(sprite).value
    sprite = sprite_map and sprite_map.get(sprite_name)
    if not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    return Boolean(bool(sprite))
Exemple #9
0
def has_glyph(sheet, glyph):
    sheet = sheet.render()
    font_sheet = font_sheets.get(sheet)
    glyph_name = String.unquoted(glyph).value
    glyph = font_sheet and font_sheet.get(glyph_name)
    if not font_sheet:
        log.error("No font sheet found: %s", sheet, extra={'stack': True})
    return Boolean(bool(glyph))
Exemple #10
0
def font_sheet(g, **kwargs):
    if not fontforge:
        raise SassMissingDependency('fontforge', 'font manipulation')

    font_sheets = _get_cache('font_sheets')

    now_time = time.time()

    globs = String(g, quotes=None).value
    globs = sorted(g.strip() for g in globs.split(','))

    _k_ = ','.join(globs)

    files = None
    rfiles = None
    tfiles = None
    base_name = None
    glob_path = None
    glyph_name = None

    if _k_ in font_sheets:
        font_sheets[_k_]['*'] = now_time
    else:
        files = []
        rfiles = []
        tfiles = []
        for _glob in globs:
            if '..' not in _glob:  # Protect against going to prohibited places...
                if callable(config.STATIC_ROOT):
                    _glob_path = _glob
                    _rfiles = _files = sorted(config.STATIC_ROOT(_glob))
                else:
                    _glob_path = os.path.join(config.STATIC_ROOT, _glob)
                    _files = glob.glob(_glob_path)
                    _files = sorted((f, None) for f in _files)
                    _rfiles = [(rf[len(config.STATIC_ROOT):], s)
                               for rf, s in _files]
                if _files:
                    files.extend(_files)
                    rfiles.extend(_rfiles)
                    base_name = os.path.basename(os.path.dirname(_glob))
                    _glyph_name, _, _glyph_type = base_name.partition('.')
                    if _glyph_type:
                        _glyph_type += '-'
                    if not glyph_name:
                        glyph_name = _glyph_name
                    tfiles.extend([_glyph_type] * len(_files))
                else:
                    glob_path = _glob_path

    if files is not None:
        if not files:
            log.error("Nothing found at '%s'", glob_path)
            return String.unquoted('')

        key = [f for (f, s) in files] + [repr(kwargs), config.ASSETS_URL]
        key = glyph_name + '-' + make_filename_hash(key)
        asset_files = {
            'eot': key + '.eot',
            'woff': key + '.woff',
            'ttf': key + '.ttf',
            'svg': key + '.svg',
        }
        ASSETS_ROOT = _assets_root()
        asset_paths = dict((type_, os.path.join(ASSETS_ROOT, asset_file))
                           for type_, asset_file in asset_files.items())
        cache_path = os.path.join(config.CACHE_ROOT or ASSETS_ROOT,
                                  key + '.cache')

        inline = Boolean(kwargs.get('inline', False))

        font_sheet = None
        asset = None
        file_assets = {}
        inline_assets = {}
        if all(
                os.path.exists(asset_path)
                for asset_path in asset_paths.values()) or inline:
            try:
                save_time, file_assets, inline_assets, font_sheet, codepoints = pickle.load(
                    open(cache_path))
                if file_assets:
                    file_asset = List(
                        [file_asset for file_asset in file_assets.values()],
                        separator=",")
                    font_sheets[file_asset.render()] = font_sheet
                if inline_assets:
                    inline_asset = List([
                        inline_asset
                        for inline_asset in inline_assets.values()
                    ],
                                        separator=",")
                    font_sheets[inline_asset.render()] = font_sheet
                if inline:
                    asset = inline_asset
                else:
                    asset = file_asset
            except:
                pass

            if font_sheet:
                for file_, storage in files:
                    _time = getmtime(file_, storage)
                    if save_time < _time:
                        if _time > now_time:
                            log.warning(
                                "File '%s' has a date in the future (cache ignored)"
                                % file_)
                        font_sheet = None  # Invalidate cached custom font
                        break

        if font_sheet is None or asset is None:
            cache_buster = Boolean(kwargs.get('cache_buster', True))
            autowidth = Boolean(kwargs.get('autowidth', False))
            autohint = Boolean(kwargs.get('autohint', True))

            font = fontforge.font()
            font.encoding = 'UnicodeFull'
            font.design_size = 16
            font.em = GLYPH_HEIGHT
            font.ascent = GLYPH_ASCENT
            font.descent = GLYPH_DESCENT
            font.fontname = glyph_name
            font.familyname = glyph_name
            font.fullname = glyph_name

            def glyphs(f=lambda x: x):
                for file_, storage in f(files):
                    if storage is not None:
                        _file = storage.open(file_)
                    else:
                        _file = open(file_)
                    svgtext = _file.read()
                    svgtext = svgtext.replace('<switch>', '')
                    svgtext = svgtext.replace('</switch>', '')
                    svgtext = svgtext.replace(
                        '<svg>', '<svg xmlns="http://www.w3.org/2000/svg">')
                    m = GLYPH_WIDTH_RE.search(svgtext)
                    if m:
                        width = float(m.group(1))
                    else:
                        width = None
                    m = GLYPH_HEIGHT_RE.search(svgtext)
                    if m:
                        height = float(m.group(1))
                    else:
                        height = None
                    _glyph = tempfile.NamedTemporaryFile(delete=False,
                                                         suffix=".svg")
                    _glyph.file.write(svgtext)
                    _glyph.file.close()
                    yield _glyph.name, width, height

            names = tuple(
                os.path.splitext(os.path.basename(file_))[0]
                for file_, storage in files)
            tnames = tuple(tfiles[i] + n for i, n in enumerate(names))

            codepoints = []
            for i, (glyph_filename, glyph_width,
                    glyph_height) in enumerate(glyphs()):
                if glyph_height and glyph_height != GLYPH_HEIGHT:
                    warnings.warn("Glyphs should be %spx-high" % GLYPH_HEIGHT)
                codepoint = i + GLYPH_START
                name = names[i]
                codepoints.append(codepoint)
                glyph = font.createChar(codepoint, name)
                glyph.importOutlines(glyph_filename)
                os.unlink(glyph_filename)
                glyph.width = glyph_width or GLYPH_WIDTH
                if autowidth:
                    # Autowidth removes side bearings
                    glyph.left_side_bearing = glyph.right_side_bearing = 0
                glyph.round()

            filetime = int(now_time)

            # Generate font files
            if not inline:
                urls = {}
                for type_ in reversed(FONT_TYPES):
                    asset_path = asset_paths[type_]
                    try:
                        if type_ == 'eot':
                            ttf_path = asset_paths['ttf']
                            with open(ttf_path) as ttf_fh:
                                contents = ttf2eot(ttf_fh.read())
                                if contents is not None:
                                    with open(asset_path, 'wb') as asset_fh:
                                        asset_fh.write(contents)
                        else:
                            font.generate(asset_path)
                            if type_ == 'ttf':
                                contents = None
                                if autohint:
                                    with open(asset_path) as asset_fh:
                                        contents = ttfautohint(asset_fh.read())
                                if contents is not None:
                                    with open(asset_path, 'wb') as asset_fh:
                                        asset_fh.write(contents)
                        asset_file = asset_files[type_]
                        url = '%s%s' % (config.ASSETS_URL, asset_file)
                        params = []
                        if not urls:
                            params.append('#iefix')
                        if cache_buster:
                            params.append('v=%s' % filetime)
                        if type_ == 'svg':
                            params.append('#' + glyph_name)
                        if params:
                            url += '?' + '&'.join(params)
                        urls[type_] = url
                    except IOError:
                        inline = False

            if inline:
                urls = {}
                for type_ in reversed(FONT_TYPES):
                    contents = None
                    if type_ == 'eot':
                        ttf_path = asset_paths['ttf']
                        with open(ttf_path) as ttf_fh:
                            contents = ttf2eot(ttf_fh.read())
                            if contents is None:
                                continue
                    else:
                        _tmp = tempfile.NamedTemporaryFile(delete=False,
                                                           suffix='.' + type_)
                        _tmp.file.close()
                        font.generate(_tmp.name)
                        with open(_tmp.name) as asset_fh:
                            if autohint:
                                if type_ == 'ttf':
                                    _contents = asset_fh.read()
                                    contents = ttfautohint(_contents)
                            if contents is None:
                                contents = _contents
                    os.unlink(_tmp.name)
                    mime_type = FONT_MIME_TYPES[type_]
                    url = make_data_url(mime_type, contents)
                    urls[type_] = url

            assets = {}
            for type_, url in urls.items():
                format_ = FONT_FORMATS[type_]
                if inline:
                    assets[type_] = inline_assets[type_] = List(
                        [Url.unquoted(url),
                         String.unquoted(format_)])
                else:
                    assets[type_] = file_assets[type_] = List(
                        [Url.unquoted(url),
                         String.unquoted(format_)])
            asset = List(
                [assets[type_] for type_ in FONT_TYPES if type_ in assets],
                separator=",")

            # Add the new object:
            font_sheet = dict(zip(tnames, zip(rfiles, codepoints)))
            font_sheet['*'] = now_time
            font_sheet['*f*'] = asset_files
            font_sheet['*k*'] = key
            font_sheet['*n*'] = glyph_name
            font_sheet['*t*'] = filetime

            codepoints = zip(files, codepoints)
            cache_tmp = tempfile.NamedTemporaryFile(delete=False,
                                                    dir=ASSETS_ROOT)
            pickle.dump(
                (now_time, file_assets, inline_assets, font_sheet, codepoints),
                cache_tmp)
            cache_tmp.close()
            os.rename(cache_tmp.name, cache_path)

            # Use the sorted list to remove older elements (keep only 500 objects):
            if len(font_sheets) > MAX_FONT_SHEETS:
                for a in sorted(font_sheets,
                                key=lambda a: font_sheets[a]['*'],
                                reverse=True)[KEEP_FONT_SHEETS:]:
                    del font_sheets[a]
                log.warning("Exceeded maximum number of font sheets (%s)" %
                            MAX_FONT_SHEETS)
            font_sheets[asset.render()] = font_sheet
        font_sheet_cache = _get_cache('font_sheet_cache')
        for file_, codepoint in codepoints:
            font_sheet_cache[file_] = codepoint
    # TODO this sometimes returns an empty list, or is never assigned to
    return asset
Exemple #11
0
def font_sheet(g, **kwargs):
    if not fontforge:
        raise Exception("Fonts manipulation require fontforge")

    now_time = time.time()

    g = String(g, quotes=None).value

    if g in font_sheets:
        font_sheets[glob]['*'] = now_time
    elif '..' not in g:  # Protect against going to prohibited places...
        if callable(config.STATIC_ROOT):
            glob_path = g
            rfiles = files = sorted(config.STATIC_ROOT(g))
        else:
            glob_path = os.path.join(config.STATIC_ROOT, g)
            files = glob.glob(glob_path)
            files = sorted((f, None) for f in files)
            rfiles = [(rf[len(config.STATIC_ROOT):], s) for rf, s in files]

        if not files:
            log.error("Nothing found at '%s'", glob_path)
            return String.unquoted('')

        glyph_name = os.path.normpath(os.path.dirname(g)).replace('\\',
                                                                  '_').replace(
                                                                      '/', '_')
        key = [f for (f, s) in files] + [repr(kwargs), config.ASSETS_URL]
        key = glyph_name + '-' + make_filename_hash(key)
        asset_files = {
            'ttf': key + '.ttf',
            'svg': key + '.svg',
            'woff': key + '.woff',
            'eot': key + '.eot',
        }
        ASSETS_ROOT = config.ASSETS_ROOT or os.path.join(
            config.STATIC_ROOT, 'assets')
        asset_paths = dict((type_, os.path.join(ASSETS_ROOT, asset_file))
                           for type_, asset_file in asset_files.items())
        cache_path = os.path.join(config.CACHE_ROOT or ASSETS_ROOT,
                                  key + '.cache')

        inline = Boolean(kwargs.get('inline', False))

        font_sheet = None
        asset = None
        file_assets = {}
        inline_assets = {}
        if all(
                os.path.exists(asset_path)
                for asset_path in asset_paths.values()) or inline:
            try:
                save_time, file_assets, inline_assets, font_sheet, codepoints = pickle.load(
                    open(cache_path))
                if file_assets:
                    file_asset = List(
                        [file_asset for file_asset in file_assets.values()],
                        separator=",")
                    font_sheets[file_asset.render()] = font_sheet
                if inline_assets:
                    inline_asset = List([
                        inline_asset
                        for inline_asset in inline_assets.values()
                    ],
                                        separator=",")
                    font_sheets[inline_asset.render()] = font_sheet
                if inline:
                    asset = inline_asset
                else:
                    asset = file_asset
            except:
                pass

            if font_sheet:
                for file_, storage in files:
                    _time = getmtime(file_, storage)
                    if save_time < _time:
                        if _time > now_time:
                            log.warning(
                                "File '%s' has a date in the future (cache ignored)"
                                % file_)
                        font_sheet = None  # Invalidate cached custom font
                        break

        if font_sheet is None or asset is None:
            cache_buster = Boolean(kwargs.get('cache_buster', True))
            autowidth = Boolean(kwargs.get('autowidth', True))
            autohint = Boolean(kwargs.get('autohint', True))

            font = fontforge.font()
            font.encoding = 'UnicodeFull'
            font.design_size = 16
            font.em = 512
            font.ascent = 448
            font.descent = 64
            font.fontname = glyph_name
            font.familyname = glyph_name
            font.fullname = glyph_name
            if autowidth:
                font.autoWidth(0, 0, 512)
            if autohint:
                font.autoHint()

            def glyphs(f=lambda x: x):
                for file_, storage in f(files):
                    if storage is not None:
                        _file = storage.open(file_)
                    else:
                        _file = open(file_)
                    svgtext = _file.read()
                    svgtext = svgtext.replace('<switch>', '')
                    svgtext = svgtext.replace('</switch>', '')
                    _glyph = tempfile.NamedTemporaryFile(delete=False,
                                                         suffix=".svg")
                    _glyph.file.write(svgtext)
                    _glyph.file.close()
                    yield _glyph.name

            names = tuple(
                os.path.splitext(os.path.basename(file_))[0]
                for file_, storage in files)

            codepoints = []
            for i, glyph_filename in enumerate(glyphs()):
                codepoint = i + GLYPH_START
                codepoints.append(codepoint)
                glyph = font.createChar(codepoint, names[i])
                glyph.importOutlines(glyph_filename)
                os.unlink(glyph_filename)
                if autowidth:
                    glyph.left_side_bearing = glyph.right_side_bearing = 0
                    glyph.round()
                else:
                    glyph.width = 512

            filetime = int(now_time)

            # Generate font files
            if not inline:
                urls = {}
                for type_ in ('ttf', 'svg', 'woff', 'eot'):
                    asset_path = asset_paths[type_]
                    try:
                        font.generate(asset_path)
                        # svgtext.replace('<svg>', '<svg xmlns="http://www.w3.org/2000/svg">')
                        asset_file = asset_files[type_]
                        url = '%s%s' % (config.ASSETS_URL, asset_file)
                        params = []
                        if type_ == 'eot':
                            params.append('#iefix')
                        if cache_buster:
                            params.append('v=%s' % filetime)
                        if type_ == 'svg':
                            params.append('#' + glyph_name)
                        if params:
                            url += '?' + '&'.join(params)
                        urls[type_] = url
                    except IOError:
                        inline = False
            if inline:
                urls = {}
                for type_ in ('ttf', 'svg', 'woff', 'eot'):
                    _tmp = tempfile.NamedTemporaryFile(delete=False,
                                                       suffix='.' + type_)
                    _tmp.file.close()
                    font.generate(_tmp.name)
                    with open(_tmp.name) as fh:
                        contents = fh.read()
                    os.unlink(_tmp.name)
                    # contents.replace('<svg>', '<svg xmlns="http://www.w3.org/2000/svg">')
                    mime_type = FONT_MIME_TYPES[type_]
                    url = make_data_url(mime_type, contents)
                    urls[type_] = url

            assets = {}
            for type_, url in urls.items():
                format_ = FONT_FORMATS[type_]
                url = "url('%s')" % escape(url)
                if inline:
                    assets[type_] = inline_assets[type_] = List(
                        [String.unquoted(url),
                         String.unquoted(format_)])
                else:
                    assets[type_] = file_assets[type_] = List(
                        [String.unquoted(url),
                         String.unquoted(format_)])
            asset = List([a for a in assets.values()], separator=",")

            # Add the new object:
            font_sheet = dict(zip(names, zip(rfiles, codepoints)))
            font_sheet['*'] = now_time
            font_sheet['*f*'] = asset_files
            font_sheet['*k*'] = key
            font_sheet['*n*'] = glyph_name
            font_sheet['*t*'] = filetime

            codepoints = zip(files, codepoints)
            cache_tmp = tempfile.NamedTemporaryFile(delete=False,
                                                    dir=ASSETS_ROOT)
            pickle.dump(
                (now_time, file_assets, inline_assets, font_sheet, codepoints),
                cache_tmp)
            cache_tmp.close()
            os.rename(cache_tmp.name, cache_path)

            # Use the sorted list to remove older elements (keep only 500 objects):
            if len(font_sheets) > MAX_FONT_SHEETS:
                for a in sorted(font_sheets,
                                key=lambda a: font_sheets[a]['*'],
                                reverse=True)[KEEP_FONT_SHEETS:]:
                    del font_sheets[a]
                log.warning("Exceeded maximum number of font sheets (%s)" %
                            MAX_FONT_SHEETS)
            font_sheets[asset.render()] = font_sheet
        for file_, codepoint in codepoints:
            _font_sheet_cache[file_] = codepoint
    # TODO this sometimes returns an empty list, or is never assigned to
    return asset
Exemple #12
0
def sprite_map(g, **kwargs):
    """
    Generates a sprite map from the files matching the glob pattern.
    Uses the keyword-style arguments passed in to control the placement.

    $direction - Sprite map layout. Can be `vertical` (default), `horizontal`, `diagonal` or `smart`.

    $position - For `horizontal` and `vertical` directions, the position of the sprite. (defaults to `0`)
    $<sprite>-position - Position of a given sprite.

    $padding, $spacing - Adds paddings to sprites (top, right, bottom, left). (defaults to `0, 0, 0, 0`)
    $<sprite>-padding, $<sprite>-spacing - Padding for a given sprite.

    $dst-color - Together with `$src-color`, forms a map of source colors to be converted to destiny colors (same index of `$src-color` changed to `$dst-color`).
    $<sprite>-dst-color - Destiny colors for a given sprite. (defaults to `$dst-color`)

    $src-color - Selects source colors to be converted to the corresponding destiny colors. (defaults to `black`)
    $<sprite>-dst-color - Source colors for a given sprite. (defaults to `$src-color`)

    $collapse - Collapses every image in the sprite map to a fixed size (`x` and `y`).
    $collapse-x  - Collapses a size for `x`.
    $collapse-y  - Collapses a size for `y`.
    """
    if not Image:
        raise SassMissingDependency('PIL', 'image manipulation')

    sprite_maps = _get_cache('sprite_maps')

    now_time = time.time()

    globs = String(g, quotes=None).value
    globs = sorted(g.strip() for g in globs.split(','))

    _k_ = ','.join(globs)

    files = None
    rfiles = None
    tfiles = None
    map_name = None

    if _k_ in sprite_maps:
        sprite_maps[_k_]['*'] = now_time
    else:
        files = []
        rfiles = []
        tfiles = []
        for _glob in globs:
            if '..' not in _glob:  # Protect against going to prohibited places...
                if callable(config.STATIC_ROOT):
                    _glob_path = _glob
                    _rfiles = _files = sorted(config.STATIC_ROOT(_glob))
                else:
                    _glob_path = os.path.join(config.STATIC_ROOT, _glob)
                    _files = glob.glob(_glob_path)
                    _files = sorted((f, None) for f in _files)
                    _rfiles = [(rf[len(config.STATIC_ROOT):], s)
                               for rf, s in _files]
                if _files:
                    files.extend(_files)
                    rfiles.extend(_rfiles)
                    base_name = os.path.normpath(
                        os.path.dirname(_glob)).replace('\\',
                                                        '_').replace('/', '_')
                    _map_name, _, _map_type = base_name.partition('.')
                    if _map_type:
                        _map_type += '-'
                    if not map_name:
                        map_name = _map_name
                    tfiles.extend([_map_type] * len(_files))
                else:
                    glob_path = _glob_path

    if files is not None:
        if not files:
            log.error("Nothing found at '%s'", glob_path)
            return String.unquoted('')

        key = [f for (f, s) in files] + [repr(kwargs), config.ASSETS_URL]
        key = map_name + '-' + make_filename_hash(key)
        asset_file = key + '.png'
        ASSETS_ROOT = _assets_root()
        asset_path = os.path.join(ASSETS_ROOT, asset_file)
        cache_path = os.path.join(config.CACHE_ROOT or ASSETS_ROOT,
                                  asset_file + '.cache')

        inline = Boolean(kwargs.get('inline', False))

        sprite_map = None
        asset = None
        file_asset = None
        inline_asset = None
        if os.path.exists(asset_path) or inline:
            try:
                save_time, file_asset, inline_asset, sprite_map, sizes = pickle.load(
                    open(cache_path))
                if file_asset:
                    sprite_maps[file_asset.render()] = sprite_map
                if inline_asset:
                    sprite_maps[inline_asset.render()] = sprite_map
                if inline:
                    asset = inline_asset
                else:
                    asset = file_asset
            except:
                pass

            if sprite_map:
                for file_, storage in files:
                    _time = getmtime(file_, storage)
                    if save_time < _time:
                        if _time > now_time:
                            log.warning(
                                "File '%s' has a date in the future (cache ignored)"
                                % file_)
                        sprite_map = None  # Invalidate cached sprite map
                        break

        if sprite_map is None or asset is None:
            cache_buster = Boolean(kwargs.get('cache_buster', True))
            direction = String.unquoted(
                kwargs.get('direction', config.SPRTE_MAP_DIRECTION)).value
            repeat = String.unquoted(kwargs.get('repeat', 'no-repeat')).value
            collapse = kwargs.get('collapse', Number(0))
            if isinstance(collapse, List):
                collapse_x = int(Number(collapse[0]).value)
                collapse_y = int(Number(collapse[-1]).value)
            else:
                collapse_x = collapse_y = int(Number(collapse).value)
            if 'collapse_x' in kwargs:
                collapse_x = int(Number(kwargs['collapse_x']).value)
            if 'collapse_y' in kwargs:
                collapse_y = int(Number(kwargs['collapse_y']).value)

            position = Number(kwargs.get('position', 0))
            if not position.is_simple_unit('%') and position.value > 1:
                position = position.value / 100.0
            else:
                position = position.value
            if position < 0:
                position = 0.0
            elif position > 1:
                position = 1.0

            padding = kwargs.get('padding', kwargs.get('spacing', Number(0)))
            padding = [int(Number(v).value) for v in List.from_maybe(padding)]
            padding = (padding * 4)[:4]

            dst_colors = kwargs.get('dst_color')
            dst_colors = [
                list(Color(v).value[:3]) for v in List.from_maybe(dst_colors)
                if v
            ]
            src_colors = kwargs.get('src_color', Color.from_name('black'))
            src_colors = [
                tuple(Color(v).value[:3]) for v in List.from_maybe(src_colors)
            ]
            len_colors = max(len(dst_colors), len(src_colors))
            dst_colors = (dst_colors * len_colors)[:len_colors]
            src_colors = (src_colors * len_colors)[:len_colors]

            def images(f=lambda x: x):
                for file_, storage in f(files):
                    if storage is not None:
                        _file = storage.open(file_)
                    else:
                        _file = file_
                    _image = Image.open(_file)
                    yield _image

            names = tuple(
                os.path.splitext(os.path.basename(file_))[0]
                for file_, storage in files)
            tnames = tuple(tfiles[i] + n for i, n in enumerate(names))

            has_dst_colors = False
            all_dst_colors = []
            all_src_colors = []
            all_positions = []
            all_paddings = []

            for name in names:
                name = name.replace('-', '_')

                _position = kwargs.get(name + '_position')
                if _position is None:
                    _position = position
                else:
                    _position = Number(_position)
                    if not _position.is_simple_unit(
                            '%') and _position.value > 1:
                        _position = _position.value / 100.0
                    else:
                        _position = _position.value
                    if _position < 0:
                        _position = 0.0
                    elif _position > 1:
                        _position = 1.0
                all_positions.append(_position)

                _padding = kwargs.get(name + '_padding',
                                      kwargs.get(name + '_spacing'))
                if _padding is None:
                    _padding = padding
                else:
                    _padding = [
                        int(Number(v).value) for v in List.from_maybe(_padding)
                    ]
                    _padding = (_padding * 4)[:4]
                all_paddings.append(_padding)

                _dst_colors = kwargs.get(name + '_dst_color')
                if _dst_colors is None:
                    _dst_colors = dst_colors
                    if dst_colors:
                        has_dst_colors = True
                else:
                    has_dst_colors = True
                    _dst_colors = [
                        list(Color(v).value[:3])
                        for v in List.from_maybe(_dst_colors) if v
                    ]
                _src_colors = kwargs.get(name + '_src_color',
                                         Color.from_name('black'))
                if _src_colors is None:
                    _src_colors = src_colors
                else:
                    _src_colors = [
                        tuple(Color(v).value[:3])
                        for v in List.from_maybe(_src_colors)
                    ]
                _len_colors = max(len(_dst_colors), len(_src_colors))
                _dst_colors = (_dst_colors * _len_colors)[:_len_colors]
                _src_colors = (_src_colors * _len_colors)[:_len_colors]
                all_dst_colors.append(_dst_colors)
                all_src_colors.append(_src_colors)

            sizes = tuple((collapse_x or i.size[0], collapse_y or i.size[1])
                          for i in images())

            if direction == 'horizontal':
                layout = HorizontalSpritesLayout(sizes,
                                                 all_paddings,
                                                 position=all_positions)
            elif direction == 'vertical':
                layout = VerticalSpritesLayout(sizes,
                                               all_paddings,
                                               position=all_positions)
            elif direction == 'diagonal':
                layout = DiagonalSpritesLayout(sizes, all_paddings)
            elif direction == 'smart':
                layout = PackedSpritesLayout(sizes, all_paddings)
            else:
                raise Exception("Invalid direction %r" % (direction, ))
            layout_positions = list(layout)

            new_image = Image.new(mode='RGBA',
                                  size=(layout.width, layout.height),
                                  color=(0, 0, 0, 0))

            useless_dst_color = has_dst_colors

            offsets_x = []
            offsets_y = []
            for i, image in enumerate(images()):
                x, y, width, height, cssx, cssy, cssw, cssh = layout_positions[
                    i]
                iwidth, iheight = image.size

                if has_dst_colors:
                    pixdata = image.load()
                    for _y in xrange(iheight):
                        for _x in xrange(iwidth):
                            pixel = pixdata[_x, _y]
                            a = pixel[3] if len(pixel) == 4 else 255
                            if a:
                                rgb = pixel[:3]
                                for j, dst_color in enumerate(
                                        all_dst_colors[i]):
                                    if rgb == all_src_colors[i][j]:
                                        new_color = tuple(
                                            [int(c) for c in dst_color] + [a])
                                        if pixel != new_color:
                                            pixdata[_x, _y] = new_color
                                            useless_dst_color = False
                                        break

                if iwidth != width or iheight != height:
                    cy = 0
                    while cy < iheight:
                        cx = 0
                        while cx < iwidth:
                            new_image = alpha_composite(
                                new_image, image, (x, y),
                                (cx, cy, cx + width, cy + height))
                            cx += width
                        cy += height
                else:
                    new_image.paste(image, (x, y))
                offsets_x.append(cssx)
                offsets_y.append(cssy)

            if useless_dst_color:
                log.warning(
                    "Useless use of $dst-color in sprite map for files at '%s' (never used for)"
                    % glob_path)

            filetime = int(now_time)

            if not inline:
                try:
                    new_image.save(asset_path)
                    url = '%s%s' % (config.ASSETS_URL, asset_file)
                    if cache_buster:
                        url += '?_=%s' % filetime
                except IOError:
                    log.exception("Error while saving image")
                    inline = True
            if inline:
                output = six.BytesIO()
                new_image.save(output, format='PNG')
                contents = output.getvalue()
                output.close()
                mime_type = 'image/png'
                url = make_data_url(mime_type, contents)

            url = 'url(%s)' % escape(url)
            if inline:
                asset = inline_asset = List(
                    [String.unquoted(url),
                     String.unquoted(repeat)])
            else:
                asset = file_asset = List(
                    [String.unquoted(url),
                     String.unquoted(repeat)])

            # Add the new object:
            sprite_map = dict(
                zip(tnames, zip(sizes, rfiles, offsets_x, offsets_y)))
            sprite_map['*'] = now_time
            sprite_map['*f*'] = asset_file
            sprite_map['*k*'] = key
            sprite_map['*n*'] = map_name
            sprite_map['*t*'] = filetime

            sizes = zip(files, sizes)
            cache_tmp = tempfile.NamedTemporaryFile(delete=False,
                                                    dir=ASSETS_ROOT)
            pickle.dump(
                (now_time, file_asset, inline_asset, sprite_map, sizes),
                cache_tmp)
            cache_tmp.close()
            if sys.platform == 'win32' and os.path.isfile(cache_path):
                # on windows, cannot rename a file to a path that matches
                # an existing file, we have to remove it first
                os.remove(cache_path)
            os.rename(cache_tmp.name, cache_path)

            # Use the sorted list to remove older elements (keep only 500 objects):
            if len(sprite_maps) > MAX_SPRITE_MAPS:
                for a in sorted(sprite_maps,
                                key=lambda a: sprite_maps[a]['*'],
                                reverse=True)[KEEP_SPRITE_MAPS:]:
                    del sprite_maps[a]
                log.warning("Exceeded maximum number of sprite maps (%s)" %
                            MAX_SPRITE_MAPS)
            sprite_maps[asset.render()] = sprite_map
        image_size_cache = _get_cache('image_size_cache')
        for file_, size in sizes:
            image_size_cache[file_] = size
    # TODO this sometimes returns an empty list, or is never assigned to
    return asset
Exemple #13
0
def test_background_noise():
    libextra.background_noise(Number(0.5),
                              Number(0.5),
                              Number(100),
                              Boolean(True),
                              color=Color.from_name('green'))
Exemple #14
0
 def evaluate(self, calculator, divide=False):
     operand = self.operand.evaluate(calculator, divide=True)
     return Boolean(not(operand))
Exemple #15
0
def unitless(value):
    if not isinstance(value, Number):
        raise TypeError("Expected number, got %r" % (value,))

    return Boolean(value.is_unitless)
Exemple #16
0
def comparable(number1, number2):
    left = number1.to_base_units()
    right = number2.to_base_units()
    return Boolean(
        left.unit_numer == right.unit_numer
        and left.unit_denom == right.unit_denom)
Exemple #17
0
def map_has_key(map, key):
    return Boolean(key in map.to_dict())
Exemple #18
0
def index(lst, val):
    for i in xrange(len(lst)):
        if lst.value[i] == val:
            return Number(i + 1)
    return Boolean(False)
Exemple #19
0
def prefixed(prefix, *args):
    to_fnct_str = 'to_' + to_str(prefix).replace('-', '_')
    for arg in List.from_maybe_starargs(args):
        if hasattr(arg, to_fnct_str):
            return Boolean(True)
    return Boolean(False)
Exemple #20
0
    def handle_import(self, name, compilation, rule):
        """Implementation of Compass's "magic" imports, which generate
        spritesheets on the fly, given either a wildcard or the name of a
        directory.
        """
        from .sprites import sprite_map

        # TODO check that the found file is actually under the root
        if callable(config.STATIC_ROOT):
            files = sorted(config.STATIC_ROOT(name))
        else:
            glob_path = os.path.join(config.STATIC_ROOT, name)
            files = glob.glob(glob_path)
            files = sorted(
                (fn[len(config.STATIC_ROOT):], None) for fn in files)

        if not files:
            return

        # Build magic context
        calculator = compilation._make_calculator(rule.namespace)
        map_name = os.path.normpath(os.path.dirname(name)).replace(
            '\\', '_').replace('/', '_')
        kwargs = {}

        # TODO this is all kinds of busted.  rule.context hasn't existed for
        # ages.
        def setdefault(var, val):
            _var = '$' + map_name + '-' + var
            if _var in rule.context:
                kwargs[var] = calculator.interpolate(rule.context[_var], rule,
                                                     self._library)
            else:
                rule.context[_var] = val
                kwargs[var] = calculator.interpolate(val, rule, self._library)
            return rule.context[_var]

        setdefault('sprite-base-class',
                   String('.' + map_name + '-sprite', quotes=None))
        setdefault('sprite-dimensions', Boolean(False))
        position = setdefault('position', Number(0, '%'))
        spacing = setdefault('spacing', Number(0))
        repeat = setdefault('repeat', String('no-repeat', quotes=None))
        names = tuple(
            os.path.splitext(os.path.basename(file))[0]
            for file, storage in files)
        for n in names:
            setdefault(n + '-position', position)
            setdefault(n + '-spacing', spacing)
            setdefault(n + '-repeat', repeat)
        rule.context['$' + map_name + '-' + 'sprites'] = sprite_map(
            name, **kwargs)
        generated_code = '''
            @import "compass/utilities/sprites/base";

            // All sprites should extend this class
            // The %(map_name)s-sprite mixin will do so for you.
            #{$%(map_name)s-sprite-base-class} {
                background: $%(map_name)s-sprites;
            }

            // Use this to set the dimensions of an element
            // based on the size of the original image.
            @mixin %(map_name)s-sprite-dimensions($name) {
                @include sprite-dimensions($%(map_name)s-sprites, $name);
            }

            // Move the background position to display the sprite.
            @mixin %(map_name)s-sprite-position($name, $offset-x: 0, $offset-y: 0) {
                @include sprite-position($%(map_name)s-sprites, $name, $offset-x, $offset-y);
            }

            // Extends the sprite base class and set the background position for the desired sprite.
            // It will also apply the image dimensions if $dimensions is true.
            @mixin %(map_name)s-sprite($name, $dimensions: $%(map_name)s-sprite-dimensions, $offset-x: 0, $offset-y: 0) {
                @extend #{$%(map_name)s-sprite-base-class};
                @include sprite($%(map_name)s-sprites, $name, $dimensions, $offset-x, $offset-y);
            }

            @mixin %(map_name)s-sprites($sprite-names, $dimensions: $%(map_name)s-sprite-dimensions) {
                @include sprites($%(map_name)s-sprites, $sprite-names, $%(map_name)s-sprite-base-class, $dimensions);
            }

            // Generates a class for each sprited image.
            @mixin all-%(map_name)s-sprites($dimensions: $%(map_name)s-sprite-dimensions) {
                @include %(map_name)s-sprites(%(sprites)s, $dimensions);
            }
        ''' % {
            'map_name': map_name,
            'sprites': ' '.join(names)
        }

        return SourceFile.from_string(generated_code)
def using_compass_compiler():
    # TODO
    return Boolean(False)