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)
def variable_exists(namespace, name): expect_type(name, String) try: namespace.variable('$' + name.value) except KeyError: return Boolean(False) else: return Boolean(True)
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)
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)
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)
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)
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)
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))
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))
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
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
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
def test_background_noise(): libextra.background_noise(Number(0.5), Number(0.5), Number(100), Boolean(True), color=Color.from_name('green'))
def evaluate(self, calculator, divide=False): operand = self.operand.evaluate(calculator, divide=True) return Boolean(not(operand))
def unitless(value): if not isinstance(value, Number): raise TypeError("Expected number, got %r" % (value,)) return Boolean(value.is_unitless)
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)
def map_has_key(map, key): return Boolean(key in map.to_dict())
def index(lst, val): for i in xrange(len(lst)): if lst.value[i] == val: return Number(i + 1) return Boolean(False)
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)
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)