| OLD | NEW |
| (Empty) |
| 1 import base64 | |
| 2 import colorsys | |
| 3 import math | |
| 4 import mimetypes | |
| 5 import os.path | |
| 6 import sys | |
| 7 from itertools import product | |
| 8 | |
| 9 from scss import OPRT, CONV_TYPE, ELEMENTS_OF_TYPE | |
| 10 from scss.value import NumberValue, StringValue, QuotedStringValue, ColorValue,
BooleanValue, hsl_op, rgba_op | |
| 11 | |
| 12 | |
| 13 try: | |
| 14 from PIL import Image | |
| 15 except ImportError: | |
| 16 Image = None | |
| 17 | |
| 18 IMAGES = dict() | |
| 19 | |
| 20 | |
| 21 def warn(warning): | |
| 22 """ Write warning messages in stderr. | |
| 23 """ | |
| 24 print >> sys.stderr, "\nWarning: %s" % str( warning ) | |
| 25 | |
| 26 | |
| 27 def unknown(*args, **kwargs): | |
| 28 """ Unknow scss function handler. | |
| 29 Simple return 'funcname(args)' | |
| 30 """ | |
| 31 name = kwargs.get('name', '') | |
| 32 return "%s(%s)" % ( name, ', '.join(str(a) for a in args) ) | |
| 33 | |
| 34 | |
| 35 def check_pil(func): | |
| 36 """ PIL module checking decorator. | |
| 37 """ | |
| 38 def __wrapper(*args, **kwargs): | |
| 39 root = kwargs.get('root') | |
| 40 if not Image: | |
| 41 if root and root.get_opt('warn'): | |
| 42 warn("Images manipulation require PIL") | |
| 43 return 'none' | |
| 44 return func(*args, **kwargs) | |
| 45 return __wrapper | |
| 46 | |
| 47 | |
| 48 # RGB functions | |
| 49 # ============= | |
| 50 | |
| 51 def _rgb(r, g, b, **kwargs): | |
| 52 """ Converts an rgb(red, green, blue) triplet into a color. | |
| 53 """ | |
| 54 return _rgba(r, g, b, 1.0) | |
| 55 | |
| 56 def _rgba(r, g, b, a, **kwargs): | |
| 57 """ Converts an rgba(red, green, blue, alpha) quadruplet into a color. | |
| 58 """ | |
| 59 return ColorValue((float(r), float(g), float(b), float(a))) | |
| 60 | |
| 61 def _red(color, **kwargs): | |
| 62 """ Gets the red component of a color. | |
| 63 """ | |
| 64 return NumberValue(color.value[0]) | |
| 65 | |
| 66 def _green(color, **kwargs): | |
| 67 """ Gets the green component of a color. | |
| 68 """ | |
| 69 return NumberValue(color.value[1]) | |
| 70 | |
| 71 def _blue(color, **kwargs): | |
| 72 """ Gets the blue component of a color. | |
| 73 """ | |
| 74 return NumberValue(color.value[2]) | |
| 75 | |
| 76 def _mix(color1, color2, weight=0.5, **kwargs): | |
| 77 """ Mixes two colors together. | |
| 78 """ | |
| 79 weight = float(weight) | |
| 80 c1 = color1.value | |
| 81 c2 = color2.value | |
| 82 p = 0.0 if weight < 0 else 1.0 if weight > 1 else weight | |
| 83 w = p * 2 - 1 | |
| 84 a = c1[3] - c2[3] | |
| 85 | |
| 86 w1 = ((w if (w * a == -1) else (w + a) / (1 + w * a)) + 1) / 2.0 | |
| 87 w2 = 1 - w1 | |
| 88 q = [ w1, w1, w1, p ] | |
| 89 r = [ w2, w2, w2, 1 - p ] | |
| 90 return ColorValue([c1[i] * q[i] + c2[i] * r[i] for i in range(4) ]) | |
| 91 | |
| 92 | |
| 93 # HSL functions | |
| 94 # ============= | |
| 95 | |
| 96 def _hsl(h, s, l, **kwargs): | |
| 97 """ HSL color value. | |
| 98 """ | |
| 99 return _hsla(h, s, l, 1.0) | |
| 100 | |
| 101 def _hsla(h, s, l, a, **kwargs): | |
| 102 """ HSL with alpha channel color value. | |
| 103 """ | |
| 104 res = colorsys.hls_to_rgb(float(h), float(l), float(s)) | |
| 105 return ColorValue(map(lambda x: x * 255.0, res) + [float(a)]) | |
| 106 | |
| 107 def _hue(color, **kwargs): | |
| 108 """ Get hue value of HSL color. | |
| 109 """ | |
| 110 h = colorsys.rgb_to_hls(*map(lambda x: x / 255.0, color.value[:3]))[0] | |
| 111 return NumberValue(h * 360.0) | |
| 112 | |
| 113 def _lightness(color, **kwargs): | |
| 114 """ Get lightness value of HSL color. | |
| 115 """ | |
| 116 l = colorsys.rgb_to_hls( *map(lambda x: x / 255.0, color.value[:3]) )[1] | |
| 117 return NumberValue(( l * 100, '%' )) | |
| 118 | |
| 119 def _saturation(color, **kwargs): | |
| 120 """ Get saturation value of HSL color. | |
| 121 """ | |
| 122 s = colorsys.rgb_to_hls( *map(lambda x: x / 255.0, color.value[:3]) )[2] | |
| 123 return NumberValue(( s * 100, '%' )) | |
| 124 | |
| 125 def _adjust_hue(color, degrees, **kwargs): | |
| 126 return hsl_op(OPRT['+'], color, degrees, 0, 0) | |
| 127 | |
| 128 def _lighten(color, amount, **kwargs): | |
| 129 return hsl_op(OPRT['+'], color, 0, 0, amount) | |
| 130 | |
| 131 def _darken(color, amount, **kwargs): | |
| 132 return hsl_op(OPRT['-'], color, 0, 0, amount) | |
| 133 | |
| 134 def _saturate(color, amount, **kwargs): | |
| 135 return hsl_op(OPRT['+'], color, 0, amount, 0) | |
| 136 | |
| 137 def _desaturate(color, amount, **kwargs): | |
| 138 return hsl_op(OPRT['-'], color, 0, amount, 0) | |
| 139 | |
| 140 def _grayscale(color, **kwargs): | |
| 141 return hsl_op(OPRT['-'], color, 0, 100, 0) | |
| 142 | |
| 143 def _complement(color, **kwargs): | |
| 144 return hsl_op(OPRT['+'], color, 180.0, 0, 0) | |
| 145 | |
| 146 | |
| 147 # Opacity functions | |
| 148 # ================= | |
| 149 | |
| 150 def _alpha(color, **kwargs): | |
| 151 c = ColorValue(color).value | |
| 152 return NumberValue(c[3]) | |
| 153 | |
| 154 def _opacify(color, amount, **kwargs): | |
| 155 return rgba_op(OPRT['+'], color, 0, 0, 0, amount) | |
| 156 | |
| 157 def _transparentize(color, amount, **kwargs): | |
| 158 return rgba_op(OPRT['-'], color, 0, 0, 0, amount) | |
| 159 | |
| 160 | |
| 161 # String functions | |
| 162 # ================= | |
| 163 | |
| 164 def _unquote(*args, **kwargs): | |
| 165 return StringValue(' '.join(str(s).strip("\"'") for s in args)) | |
| 166 | |
| 167 def _quote(*args, **kwargs): | |
| 168 return QuotedStringValue(' '.join(str(s) for s in args)) | |
| 169 | |
| 170 | |
| 171 # Number functions | |
| 172 # ================= | |
| 173 | |
| 174 def _percentage(value, **kwargs): | |
| 175 value = NumberValue(value) | |
| 176 if not value.units == '%': | |
| 177 value.value *= 100 | |
| 178 value.units = '%' | |
| 179 return value | |
| 180 | |
| 181 def _abs(value, **kwargs): | |
| 182 return abs(float(value)) | |
| 183 | |
| 184 def _pi(**kwargs): | |
| 185 return NumberValue(math.pi) | |
| 186 | |
| 187 def _sin(value, **kwargs): | |
| 188 return math.sin(value) | |
| 189 | |
| 190 def _cos(value, **kwargs): | |
| 191 return math.cos(value) | |
| 192 | |
| 193 def _tan(value, **kwargs): | |
| 194 return math.tan(value) | |
| 195 | |
| 196 def _round(value, **kwargs): | |
| 197 return round(value) | |
| 198 | |
| 199 def _ceil(value, **kwargs): | |
| 200 return math.ceil(value) | |
| 201 | |
| 202 def _floor(value, **kwargs): | |
| 203 return math.floor(value) | |
| 204 | |
| 205 | |
| 206 # Introspection functions | |
| 207 # ======================= | |
| 208 | |
| 209 def _type_of(obj, **kwargs): | |
| 210 if isinstance(obj, BooleanValue): | |
| 211 return StringValue('bool') | |
| 212 if isinstance(obj, NumberValue): | |
| 213 return StringValue('number') | |
| 214 if isinstance(obj, QuotedStringValue): | |
| 215 return StringValue('string') | |
| 216 if isinstance(obj, ColorValue): | |
| 217 return StringValue('color') | |
| 218 if isinstance(obj, dict): | |
| 219 return StringValue('list') | |
| 220 return 'unknown' | |
| 221 | |
| 222 def _unit(value, **kwargs): | |
| 223 return NumberValue(value).units | |
| 224 | |
| 225 def _unitless(value, **kwargs): | |
| 226 if NumberValue(value).units: | |
| 227 return BooleanValue(False) | |
| 228 return BooleanValue(True) | |
| 229 | |
| 230 def _comparable(n1, n2, **kwargs): | |
| 231 n1, n2 = NumberValue(n1), NumberValue(n2) | |
| 232 type1 = CONV_TYPE.get(n1.units) | |
| 233 type2 = CONV_TYPE.get(n2.units) | |
| 234 return BooleanValue(type1 == type2) | |
| 235 | |
| 236 | |
| 237 # Color functions | |
| 238 # ================ | |
| 239 | |
| 240 def _adjust_color(color, saturation=0.0, lightness=0.0, red=0.0, green=0.0, blue
=0.0, alpha=0.0, **kwargs): | |
| 241 return __asc_color(OPRT['+'], color, saturation, lightness, red, green, blue
, alpha) | |
| 242 | |
| 243 def _scale_color(color, saturation=1.0, lightness=1.0, red=1.0, green=1.0, blue=
1.0, alpha=1.0, **kwargs): | |
| 244 return __asc_color(OPRT['*'], color, saturation, lightness, red, green, blue
, alpha) | |
| 245 | |
| 246 def _change_color(color, saturation=None, lightness=None, red=None, green=None,
blue=None, alpha=None, **kwargs): | |
| 247 return __asc_color(None, color, saturation, lightness, red, green, blue, alp
ha) | |
| 248 | |
| 249 def _invert(color, **kwargs): | |
| 250 """ Returns the inverse (negative) of a color. | |
| 251 The red, green, and blue values are inverted, while the opacity is left
alone. | |
| 252 """ | |
| 253 col = ColorValue(color) | |
| 254 c = col.value | |
| 255 c[0] = 255.0 - c[0] | |
| 256 c[1] = 255.0 - c[1] | |
| 257 c[2] = 255.0 - c[2] | |
| 258 return col | |
| 259 | |
| 260 def _adjust_lightness(color, amount, **kwargs): | |
| 261 return hsl_op(OPRT['+'], color, 0, 0, amount) | |
| 262 | |
| 263 def _adjust_saturation(color, amount, **kwargs): | |
| 264 return hsl_op(OPRT['+'], color, 0, amount, 0) | |
| 265 | |
| 266 def _scale_lightness(color, amount, **kwargs): | |
| 267 return hsl_op(OPRT['*'], color, 0, 0, amount) | |
| 268 | |
| 269 def _scale_saturation(color, amount, **kwargs): | |
| 270 return hsl_op(OPRT['*'], color, 0, amount, 0) | |
| 271 | |
| 272 | |
| 273 # Compass helpers | |
| 274 # ================ | |
| 275 | |
| 276 def _color_stops(*args, **kwargs): | |
| 277 raise NotImplementedError | |
| 278 | |
| 279 def _elements_of_type(display, **kwargs): | |
| 280 return StringValue(ELEMENTS_OF_TYPE.get(StringValue(display).value, '')) | |
| 281 | |
| 282 def _enumerate(s, b, e, **kwargs): | |
| 283 return ', '.join( | |
| 284 "%s%d" % (StringValue(s).value, x) for x in xrange(int(b.value), int(e.v
alue+1)) | |
| 285 ) | |
| 286 | |
| 287 def _font_files(*args, **kwargs): | |
| 288 raise NotImplementedError | |
| 289 | |
| 290 def _headings(a=None, b=None, **kwargs): | |
| 291 h = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] | |
| 292 if not a or StringValue(a).value == 'all': | |
| 293 a, b = 1, 6 | |
| 294 elif b is None: | |
| 295 b, a = a.value + 1, 1 | |
| 296 return ', '.join(h[int(float(a)-1):int(float(b))]) | |
| 297 | |
| 298 def _nest(*args, **kwargs): | |
| 299 return ', '.join( | |
| 300 ' '.join(s.strip() for s in p) | |
| 301 if not '&' in p[1] else p[1].replace('&', p[0].strip()) | |
| 302 for p in product( | |
| 303 *(StringValue(sel).value.split(',') for sel in args) | |
| 304 ) | |
| 305 ) | |
| 306 | |
| 307 @check_pil | |
| 308 def _image_width(image, **kwargs): | |
| 309 root = kwargs.get('root') | |
| 310 path = os.path.abspath(os.path.join(root.get_opt('path'), StringValue(image)
.value)) | |
| 311 size = __get_size(path, root=root) | |
| 312 return NumberValue([size[0], 'px']) | |
| 313 | |
| 314 @check_pil | |
| 315 def _image_height(image, **kwargs): | |
| 316 root = kwargs.get('root') | |
| 317 path = os.path.abspath(os.path.join(root.get_opt('path'), StringValue(image)
.value)) | |
| 318 size = __get_size(path, root=root) | |
| 319 return NumberValue([size[1], 'px']) | |
| 320 | |
| 321 def _image_url(image, **kwargs): | |
| 322 return QuotedStringValue(image).value | |
| 323 | |
| 324 def _url(image, **kwargs): | |
| 325 return 'url({0})'.format(QuotedStringValue(image).value) | |
| 326 | |
| 327 def _inline_image(image, mimetype=None, **kwargs): | |
| 328 root = kwargs.get('root') | |
| 329 path = os.path.abspath(os.path.join(root.get_opt('path'), StringValue(image)
.value)) | |
| 330 if os.path.exists(path): | |
| 331 mimetype = StringValue(mimetype).value or mimetypes.guess_type(path)[0] | |
| 332 f = open(path, 'rb') | |
| 333 url = 'data:' + mimetype + ';base64,' + base64.b64encode(f.read()) | |
| 334 else: | |
| 335 if root and root.get_opt('warn'): | |
| 336 warn("Not found image: %s" % path) | |
| 337 url = '%s?_=NA' % QuotedStringValue(image).value | |
| 338 inline = 'url("%s")' % url | |
| 339 return StringValue(inline) | |
| 340 | |
| 341 | |
| 342 # Misc | |
| 343 # ==== | |
| 344 | |
| 345 def _if(cond, body, els, **kwargs): | |
| 346 if BooleanValue( cond ).value: | |
| 347 return body | |
| 348 return els | |
| 349 | |
| 350 | |
| 351 def _sprite_position(*args): | |
| 352 pass | |
| 353 | |
| 354 def _sprite_file(*args): | |
| 355 pass | |
| 356 | |
| 357 def _sprite(*args): | |
| 358 pass | |
| 359 | |
| 360 def _sprite_map(*args): | |
| 361 pass | |
| 362 | |
| 363 def _sprite_map_name(*args): | |
| 364 pass | |
| 365 | |
| 366 def _sprite_url(*args): | |
| 367 pass | |
| 368 | |
| 369 def _opposite_position(*args): | |
| 370 pass | |
| 371 | |
| 372 def _grad_point(*args): | |
| 373 pass | |
| 374 | |
| 375 def _grad_color_stops(*args): | |
| 376 pass | |
| 377 | |
| 378 def _nth(*args): | |
| 379 pass | |
| 380 | |
| 381 def _join(*args): | |
| 382 pass | |
| 383 | |
| 384 def _append(*args): | |
| 385 pass | |
| 386 | |
| 387 # Layout minmax | |
| 388 def _minmax(n1, n2, **kwargs): | |
| 389 return StringValue('minmax({0},{1})'.format(n1, n2)) | |
| 390 | |
| 391 | |
| 392 FUNCTION_LIST = { | |
| 393 | |
| 394 # RGB functions | |
| 395 'rgb:3': _rgb, | |
| 396 'rgba:4': _rgba, | |
| 397 'red:1': _red, | |
| 398 'green:1': _green, | |
| 399 'blue:1': _blue, | |
| 400 'mix:2': _mix, | |
| 401 'mix:3': _mix, | |
| 402 | |
| 403 # HSL functions | |
| 404 'hsl:3': _hsl, | |
| 405 'hsla:4': _hsla, | |
| 406 'hue:1': _hue, | |
| 407 'saturation:1': _saturation, | |
| 408 'lightness:1': _lightness, | |
| 409 'adjust-hue:2': _adjust_hue, | |
| 410 'spin:2': _adjust_hue, | |
| 411 'lighten:2': _lighten, | |
| 412 'darken:2': _darken, | |
| 413 'saturate:2': _saturate, | |
| 414 'desaturate:2': _desaturate, | |
| 415 'grayscale:1': _grayscale, | |
| 416 'complement:1': _complement, | |
| 417 | |
| 418 # Opacity functions | |
| 419 'alpha:1': _alpha, | |
| 420 'opacity:1': _alpha, | |
| 421 'opacify:2': _opacify, | |
| 422 'fadein:2': _opacify, | |
| 423 'fade-in:2': _opacify, | |
| 424 'transparentize:2': _transparentize, | |
| 425 'fadeout:2': _transparentize, | |
| 426 'fade-out:2': _transparentize, | |
| 427 | |
| 428 # String functions | |
| 429 'quote:n': _quote, | |
| 430 'unquote:n': _unquote, | |
| 431 | |
| 432 # Number functions | |
| 433 'percentage:1': _percentage, | |
| 434 'sin:1': _sin, | |
| 435 'cos:1': _cos, | |
| 436 'tan:1': _tan, | |
| 437 'abs:1': _abs, | |
| 438 'round:1': _round, | |
| 439 'ceil:1': _ceil, | |
| 440 'floor:1': _floor, | |
| 441 'pi:0': _pi, | |
| 442 | |
| 443 # Introspection functions | |
| 444 'type-of:1': _type_of, | |
| 445 'unit:1': _unit, | |
| 446 'unitless:1': _unitless, | |
| 447 'comparable:2': _comparable, | |
| 448 | |
| 449 # Color functions | |
| 450 'adjust-color:n': _adjust_color, | |
| 451 'scale-color:n': _scale_color, | |
| 452 'change-color:n': _change_color, | |
| 453 'adjust-lightness:2': _adjust_lightness, | |
| 454 'adjust-saturation:2': _adjust_saturation, | |
| 455 'scale-lightness:2': _scale_lightness, | |
| 456 'scale-saturation:2': _scale_saturation, | |
| 457 'invert:1': _invert, | |
| 458 | |
| 459 # Compass helpers | |
| 460 'append-selector:2': _nest, | |
| 461 'color-stops:n': _color_stops, | |
| 462 'enumerate:3': _enumerate, | |
| 463 'elements-of-type:1': _elements_of_type, | |
| 464 'font-files:n': _font_files, | |
| 465 'headings:n': _headings, | |
| 466 'nest:n': _nest, | |
| 467 | |
| 468 # Images functions | |
| 469 'url:1': _url, | |
| 470 'image-url:1': _image_url, | |
| 471 'image-width:1': _image_width, | |
| 472 'image-height:1': _image_height, | |
| 473 'inline-image:1': _inline_image, | |
| 474 'inline-image:2': _inline_image, | |
| 475 | |
| 476 # Not implemented | |
| 477 'sprite-map:1': _sprite_map, | |
| 478 'sprite:2': _sprite, | |
| 479 'sprite:3': _sprite, | |
| 480 'sprite:4': _sprite, | |
| 481 'sprite-map-name:1': _sprite_map_name, | |
| 482 'sprite-file:2': _sprite_file, | |
| 483 'sprite-url:1': _sprite_url, | |
| 484 'sprite-position:2': _sprite_position, | |
| 485 'sprite-position:3': _sprite_position, | |
| 486 'sprite-position:4': _sprite_position, | |
| 487 | |
| 488 'opposite-position:n': _opposite_position, | |
| 489 'grad-point:n': _grad_point, | |
| 490 'grad-color-stops:n': _grad_color_stops, | |
| 491 | |
| 492 'nth:2': _nth, | |
| 493 'first-value-of:1': _nth, | |
| 494 'join:2': _join, | |
| 495 'join:3': _join, | |
| 496 'append:2': _append, | |
| 497 'append:3': _append, | |
| 498 | |
| 499 'if:3': _if, | |
| 500 'escape:1': _unquote, | |
| 501 'e:1': _unquote, | |
| 502 | |
| 503 #layout metrics functions | |
| 504 'minmax:2': _minmax, | |
| 505 } | |
| 506 | |
| 507 def __asc_color(op, color, saturation, lightness, red, green, blue, alpha): | |
| 508 if lightness or saturation: | |
| 509 color = hsl_op(op, color, 0, saturation, lightness) | |
| 510 if red or green or blue or alpha: | |
| 511 color = rgba_op(op, color, red, green, blue, alpha) | |
| 512 return color | |
| 513 | |
| 514 def __get_size(path, **kwargs): | |
| 515 root = kwargs.get('root') | |
| 516 if not IMAGES.has_key(path): | |
| 517 | |
| 518 if not os.path.exists(path): | |
| 519 | |
| 520 if root and root.get_opt('warn'): | |
| 521 warn("Not found image: %s" % path) | |
| 522 | |
| 523 return 0, 0 | |
| 524 | |
| 525 image = Image.open(path) | |
| 526 IMAGES[path] = image.size | |
| 527 return IMAGES[path] | |
| OLD | NEW |