python-lottie  0.6.10+dev42a5126
A framework to work with lottie files and telegram animated stickers (tgs)
funky_parser.py
Go to the documentation of this file.
1 import re
2 import sys
3 import enum
4 import math
5 import typing
6 
7 from ..parsers.svg.svgdata import color_table
8 from ..parsers.svg.importer import parse_color, parse_svg_file
9 from ..objects.animation import Animation
10 from ..objects import layers
11 from ..objects import shapes
12 from ..nvector import NVector
13 from .font import FontStyle
14 
15 
16 color_words = {
17  "alice": {"blue": {"_": 1}},
18  "antique": {"white": {"_": 1}},
19  "aqua": {
20  "_": 1,
21  "marine": {"_": 1}
22  },
23  "azure": {"_": 1},
24  "beige": {"_": 1},
25  "bisque": {"_": 1},
26  "black": {"_": 1},
27  "blanched": {"almond": {"_": 1}},
28  "blue": {
29  "_": 1,
30  "navy": "navy",
31  "violet": {"_": 1},
32  },
33  "brown": {"_": 1},
34  "burly": {"wood": {"_": 1}},
35  "cadet": {"blue": {"_": 1}},
36  "chartreuse": {"_": 1},
37  "chocolate": {"_": 1},
38  "coral": {"_": 1},
39  "cornflower": {"blue": {"_": 1}},
40  "corn": {
41  "silk": {"_": 1},
42  "flower": {"blue": {"_": 1}},
43  },
44  "crimson": {"_": 1},
45  "cyan": {"_": 1},
46  "dark": {
47  "blue": {"_": 1},
48  "cyan": {"_": 1},
49  "golden": {"rod": {"_": 1}},
50  "gray": {"_": 1},
51  "green": {"_": 1},
52  "grey": {"_": 1},
53  "khaki": {"_": 1},
54  "magenta": {"_": 1},
55  "olive": {"green": {"_": 1}},
56  "orange": {"_": 1},
57  "orchid": {"_": 1},
58  "red": {"_": 1},
59  "salmon": {"_": 1},
60  "sea": {"green": {"_": 1}},
61  "slate": {
62  "blue": {"_": 1},
63  "gray": {"_": 1},
64  "grey": {"_": 1}
65  },
66  "turquoise": {"_": 1},
67  "violet": {"_": 1}
68  },
69  "deep": {
70  "pink": {"_": 1},
71  "sky": {"blue": {"_": 1}},
72  },
73  "dim": {
74  "gray": {"_": 1},
75  "grey": {"_": 1}
76  },
77  "dodger": {"blue": {"_": 1}},
78  "fire": {"brick": {"_": 1}},
79  "floral": {"white": {"_": 1}},
80  "forest": {"green": {"_": 1}},
81  "fuchsia": {"_": 1},
82  "gainsboro": {"_": 1},
83  "ghost": {"white": {"_": 1}},
84  "gold": {"_": 1},
85  "golden": {"rod": {"_": 1}},
86  "gray": {"_": 1},
87  "green": {
88  "_": 1,
89  "yellow": {"_": 1}
90  },
91  "grey": {"_": 1},
92  "honeydew": {"_": 1},
93  "hotpink": {"_": 1},
94  "indian": {"red": {"_": 1}},
95  "indigo": {"_": 1},
96  "ivory": {"_": 1},
97  "khaki": {"_": 1},
98  "lavender": {
99  "_": 1,
100  "blush": {"_": 1},
101  },
102  "lawn": {"green": {"_": 1}},
103  "lemon": {"chiffon": {"_": 1}},
104  "light": {
105  "blue": {"_": 1},
106  "coral": {"_": 1},
107  "cyan": {"_": 1},
108  "golden": {
109  "rod": {
110  "_": 1,
111  "yellow": {"_": 1},
112  }
113  },
114  "gray": {"_": 1},
115  "green": {"_": 1},
116  "grey": {"_": 1},
117  "pink": {"_": 1},
118  "salmon": {"_": 1},
119  "sea": {"green": {"_": 1}},
120  "sky": {"blue": {"_": 1}},
121  "slate": {
122  "gray": {"_": 1},
123  "grey": {"_": 1}
124  },
125  "steel": {"blue": {"_": 1}},
126  "yellow": {"_": 1},
127  },
128  "lime": {
129  "_": 1,
130  "green": {"_": 1},
131  },
132  "linen": {"_": 1},
133  "magenta": {"_": 1},
134  "maroon": {"_": 1},
135  "medium": {
136  "aquamarine": {"_": 1},
137  "blue": {"_": 1},
138  "orchid": {"_": 1},
139  "purple": {"_": 1},
140  "sea": {"green": {"_": 1}},
141  "slate": {"blue": {"_": 1}},
142  "spring": {"green": {"_": 1}},
143  "turquoise": {"_": 1},
144  "violet": {"red": {"_": 1}},
145  },
146  "midnight": {"blue": {"_": 1}},
147  "mint": {"cream": {"_": 1}},
148  "misty": {"rose": {"_": 1}},
149  "moccasin": {"_": 1},
150  "navajo": {"white": {"_": 1}},
151  "navy": {
152  "_": 1,
153  "blue": "navy",
154  },
155  "old": {"lace": {"_": 1}},
156  "olive": {
157  "_": 1,
158  "drab": {"_": 1},
159  },
160  "orange": {
161  "_": 1,
162  "red": {"_": 1},
163  },
164  "orchid": {"_": 1},
165  "pale": {
166  "golden": {"rod": {"_": 1}},
167  "green": {"_": 1},
168  "turquoise": {"_": 1},
169  "violet": {"red": {"_": 1}},
170  },
171  "papaya": {"whip": {"_": 1}},
172  "peach": {"puff": {"_": 1}},
173  "peru": {"_": 1},
174  "pink": {"_": 1},
175  "plum": {"_": 1},
176  "powder": {"blue": {"_": 1}},
177  "purple": {"_": 1},
178  "red": {"_": 1},
179  "rosy": {"brown": {"_": 1}},
180  "royal": {"blue": {"_": 1}},
181  "saddle": {"brown": {"_": 1}},
182  "salmon": {"_": 1},
183  "sandy": {"brown": {"_": 1}},
184  "sea": {
185  "green": {"_": 1},
186  "shell": {"_": 1},
187  },
188  "seashell": {"_": 1},
189  "sienna": {"_": 1},
190  "silver": {"_": 1},
191  "sky": {"blue": {"_": 1}},
192  "slate": {
193  "blue": {"_": 1},
194  "gray": {"_": 1},
195  "grey": {"_": 1},
196  },
197  "snow": {"_": 1},
198  "spring": {"green": {"_": 1}},
199  "steel": {"blue": {"_": 1}},
200  "tan": {"_": 1},
201  "teal": {"_": 1},
202  "thistle": {"_": 1},
203  "tomato": {"_": 1},
204  "turquoise": {"_": 1},
205  "violet": {"_": 1},
206  "wheat": {"_": 1},
207  "white": {
208  "_": 1,
209  "smoke": {"_": 1},
210  },
211  "yellow": {
212  "_": 1,
213  "green": {"_": 1},
214  },
215 }
216 
217 
218 class TokenType(enum.Enum):
219  Word = enum.auto()
220  Number = enum.auto()
221  Eof = enum.auto()
222  String = enum.auto()
223  Punctuation = enum.auto()
224  Color = enum.auto()
225 
226 
227 class Token:
228  def __init__(
229  self,
230  type: TokenType,
231  line: int,
232  col: int,
233  start: int,
234  end: int,
235  value: typing.Any = None
236  ):
237  self.col = col
238  self.line = line
239  self.end = end
240  self.start = start
241  self.type = type
242  self.value = value
243 
244  def __repr__(self):
245  if self.type == TokenType.Eof:
246  return "End of file"
247  return repr(self.value)
248 
249 
251  Position = enum.auto()
252  Color = enum.auto()
253  Angle = enum.auto()
254  Size = enum.auto()
255  Number = enum.auto()
256  Integer = enum.auto()
257 
258 
260  def __init__(self, name, phrase, props, type: AnimatableType):
261  self.name = name.split()
262  self.phrase = phrase
263  self.props = props
264  self.type = type
265 
266  def set_initial(self, value):
267  for prop in self.props:
268  prop.value = value
269 
270  def get_initial(self):
271  return self.props[0].get_value(0)
272 
273  def add_keyframe(self, time, value):
274  for prop in self.props:
275  if not prop.animated and time > 0:
276  prop.add_keyframe(0, prop.value)
277  prop.add_keyframe(time, value)
278 
279  def loop(self, time):
280  for prop in self.props:
281  if prop.animated:
282  prop.add_keyframe(time, prop.get_value(0))
283 
284  def get_value(self, time):
285  return self.props[0].get_value(time)
286 
287 
288 class ShapeData:
289  def __init__(self, extent):
290  self.color = [0, 0, 0]
291  self.color_explicit = False
292  self.extent = extent
294  self.portrait = False
295  self.roundness = 0
296  self.opacity = 1
297  self.count = 1
298  self.stroke = None
299  self.stroke_on_top = True
300  self.properties = {}
301 
302  def scale(self, multiplier):
303  self.size_multiplitier *= multiplier
304  self.extent *= multiplier
305 
306  def define_property(self, name, type, props, phrase=None):
307  self.properties[name] = AnimatableProperty(name, phrase, props, type)
308 
309  def add_property(self, name, prop):
310  self.properties[name].props.append(prop)
311 
312 
313 class Lexer:
314  expression = re.compile(
315  r'[\r\t ]*(?P<token>(?P<punc>[:,;.])|(?P<word>[a-zA-Z\']+)|(?P<color>#(?:[a-fA-F0-9]{3}){1,2})|' +
316  r'(?P<number>-?[0-9]+(?P<fraction>\.[0-9]+)?)|(?P<string>"(?:[^"\\]|\\["\\nbt])+"))[\r\t ]*'
317  )
318 
319  def __init__(self, text):
320  self.text = text
321  self.pos = 0
322  self.token = None
323  self.line = 1
324  self.line_pos = 0
325  self.tokens = []
326  self.token_index = 0
327 
328  def _new_token(self, token: Token):
329  self.token = token
330  self.tokens.append(token)
331  self.token_index = len(self.tokens)
332  return token
333 
334  def next(self):
335  if self.token_index < len(self.tokens):
336  self.token = self.tokens[self.token_index]
337  self.token_index += 1
338  return self.token
339 
340  while True:
341  if self.pos >= len(self.text):
342  return self._new_token(Token(TokenType.Eof, self.line, self.pos - self.line_pos, self.pos, self.pos))
343 
344  if self.text[self.pos] == '\n':
345  self.line += 1
346  self.pos += 1
347  self.line_pos = self.pos
348  else:
349  match = self.expression.match(self.text, self.pos)
350  if match:
351  break
352 
353  self.pos += 1
354 
355  if match.group("word"):
356  type = TokenType.Word
357  value = match.group("word").lower()
358  elif match.group("number"):
359  type = TokenType.Number
360  if match.group("fraction"):
361  value = float(match.group("number"))
362  else:
363  value = int(match.group("number"))
364  elif match.group("string"):
365  type = TokenType.String
366  value = match.group("string")[1:-1]
367  elif match.group("punc"):
368  value = match.group("punc")
369  type = TokenType.Punctuation
370  elif match.group("color"):
371  value = match.group("color")
372  type = TokenType.Color
373 
374  self.pos = match.end("token")
375  return self._new_token(Token(type, self.line, self.pos - self.line_pos, match.start("token"), self.pos, value))
376 
377  def back(self):
378  if self.token_index > 0:
379  self.restore(self.token_index - 1)
380 
381  def save(self):
382  return self.token_index
383 
384  def restore(self, index):
385  self.token_index = index
386  if index > 0:
387  self.token = self.tokens[self.token_index-1]
388  else:
389  self.token = self.tokens[0]
390 
391 
392 class Logger:
393  def warn(self, message):
394  sys.stderr.write(message)
395  sys.stderr.write("\n")
396 
397 
399  def warn(self, message):
400  pass
401 
402 
403 class StorageLogger(Logger):
404  def __init__(self):
405  self.messages = []
406 
407  def warn(self, message):
408  self.messages.append(message)
409 
410 
411 class Parser:
412  sides = {
413  "penta": 5,
414  "hexa": 6,
415  "hepta": 7,
416  "octa": 8,
417  "ennea": 9,
418  "deca": 10,
419  }
420 
421  def __init__(self, text, logger: Logger):
422  self.lexer = Lexer(text)
423  self.lexer.next()
424  self.logger = logger
425  self.allow_resize = True
426  self.max_duration = None
427  self.svg_shapes = []
428  self.font = FontStyle("Ubuntu", 80)
429 
430  def next(self):
431  return self.lexer.next()
432 
433  @property
434  def token(self):
435  return self.lexer.token
436 
437  def color(self):
438  return self.get_color(color_words, "")
439 
440  def get_color_value(self, value, complete_word: str):
441  if isinstance(value, str):
442  return NVector(*color_table[value])
443  elif isinstance(value, (list, tuple)):
444  return NVector(*value)
445  elif isinstance(value, NVector):
446  return value
447  else:
448  return NVector(*color_table[complete_word])
449 
450  def complete_color(self, word_dict: dict, complete_word: str):
451  if "_" in word_dict:
452  return self.get_color_value(complete_word)
453  else:
454  next_item = next(iter(word_dict.item()))
455  return self.complete_color(next_item[1], complete_word + next_item[0])
456 
457  def get_color(self, word_dict: dict, complete_word: str):
458  if self.token.type == TokenType.Color:
459  color = parse_color(self.token.value)
460  self.next()
461  return color
462 
463  if self.token.type != TokenType.Word:
464  return None
465 
466  value = word_dict.get(self.token.value, None)
467  if not value:
468  return None
469 
470  if isinstance(value, dict):
471  next_word = complete_word + self.token.value
472  self.next()
473  color = self.get_color(value, next_word)
474  if color is not None:
475  return color
476 
477  if "_" in value:
478  return self.get_color_value(value["_"], next_word)
479 
480  self.warn("Incomplete color name")
481  else:
482  return self.get_color_value(value, complete_word)
483 
484  def warn(self, message):
485  token = self.token
486  self.logger.warn("At line %s column %s, near %r: %s" % (token.line, token.col, token, message))
487 
488  def parse(self):
489  self.lottie = Animation(180, 60)
490  if self.article():
491  if self.check_words("animation", "composition"):
492  self.next()
493  self.animation()
494  else:
495  self.lexer.back()
496  self.layers(self.lottie)
497  if self.token.type != TokenType.Eof:
498  self.warn("Extra tokens")
499  return self.lottie
500 
501  def article(self):
502  if self.check_words("a", "an", "the"):
503  self.next()
504  return True
505  return False
506 
507  def check_words(self, *words):
508  if self.token.type != TokenType.Word:
509  return False
510 
511  return self.token.value in words
512 
513  def skip_words(self, *words):
514  if self.check_words(*words):
515  self.next()
516  return True
517  return False
518 
519  def require_one_of(self, *words):
520  if self.check_words(*words):
521  return True
522 
523  self.warn("Expected " + repr(words[0]))
524  return False
525 
526  def check_word_sequence(self, words):
527  token = self.token
528  index = self.lexer.save()
529  for word in words:
530  if token.type != TokenType.Word or token.value != word:
531  break
532  token = self.next()
533  else:
534  return True
535 
536  self.lexer.restore(index)
537  return False
538 
539  def possesive(self):
540  return self.skip_words("its", "his", "her", "their")
541 
542  def properties(self, shape_data, callback, callback_args=[], words=["with"]):
543  lexind_and = -1
544  while True:
545  must_have_property = self.skip_words(*words)
546 
547  self.article() or self.possesive()
548 
549  lexind = self.lexer.save()
550 
551  if not callback(*callback_args):
552  self.lexer.restore(lexind)
553 
554  if shape_data and not self.shape_common_property(shape_data):
555  if must_have_property:
556  self.warn("Unknown property")
557  break
558  if lexind_and != -1:
559  self.lexer.restore(lexind_and)
560  break
561 
562  lexind_and = self.lexer.save()
563  if not self.skip_and():
564  break
565 
566  def simple_properties_callback(self, object, properties):
567  if self.check_words(*properties.keys()):
568  prop = self.token.value
569  self.next()
570  if self.check_words("of"):
571  self.next()
572 
573  value = properties[prop](getattr(object, prop))
574  setattr(object, prop, value)
575  return True
576  return False
577 
578  def animation(self):
579  while True:
580  if self.check_words("lasts", "lasting"):
581  self.next()
582  if self.check_words("for"):
583  self.next()
584  self.lottie.out_point = self.time(self.lottie.out_point)
585  elif self.check_words("stops", "stopping", "loops", "looping"):
586  if self.check_words("for", "after"):
587  self.next()
588  self.lottie.out_point = self.time(self.lottie.out_point)
589  if self.max_duration and self.lottie.out_point > self.max_duration:
590  self.lottie.out_point = self.max_duration
591  elif self.check_words("with", "has"):
592  props = {
593  "width": self.integer,
594  "height": self.integer,
595  "name": self.string
596  }
597  if not self.allow_resize:
598  props.pop("width")
599  props.pop("height")
600  self.properties(None, self.simple_properties_callback, [self.lottie, props], ["with", "has"])
601  elif self.skip_and():
602  pass
603  else:
604  return
605 
606  def time(self, default):
607  if self.token.type != TokenType.Number:
608  self.warn("Expected time")
609  return default
610 
611  amount = self.token.value
612 
613  self.next()
614  if self.check_words("seconds", "second"):
615  amount *= self.lottie.frame_rate
616  elif self.check_words("milliseconds", "millisecond"):
617  amount *= self.lottie.frame_rate / 1000
618  elif not self.check_words("frames", "frame"):
619  self.warn("Missing time unit")
620  return amount
621 
622  self.next()
623  return amount
624 
625  def integer(self, default, warn=True):
626  if self.token.type != TokenType.Number or not isinstance(self.token.value, int):
627  if warn:
628  self.warn("Expected integer")
629  return default
630 
631  val = self.token.value
632  self.next()
633  return val
634 
635  def number(self, default):
636  if self.token.type != TokenType.Number:
637  self.warn("Expected number")
638  return default
639 
640  val = self.token.value
641  self.next()
642 
643  return val
644 
645  def string(self, default):
646  if self.token.type != TokenType.String:
647  self.warn("Expected string")
648  return default
649 
650  val = self.token.value
651  self.next()
652  return val
653 
654  def layers(self, composition):
655  while True:
656  if self.token.type == TokenType.Punctuation and self.token.value in ";.":
657  self.next()
658  self.skip_and()
659  self.skip_words("then") or self.skip_words("finally")
660 
661  if self.check_words("there's"):
662  self.next()
663  self.layer(composition)
664  elif self.check_words("there"):
665  self.next()
666  if self.check_words("is", "are"):
667  self.next()
668  self.layer(composition)
669  elif self.article():
670  self.lexer.back()
671  self.layer(composition)
672  elif self.token.type == TokenType.Number and isinstance(self.token.value, int):
673  self.layer(composition)
674  else:
675  break
676 
677  def count(self, default=1):
678  if self.article():
679  return 1
680  return self.integer(default)
681 
682  def layer(self, composition):
683  if self.token.type != TokenType.Word:
684  self.warn("Expected shape")
685  return
686 
687  layer = layers.ShapeLayer()
688  layer.in_point = self.lottie.in_point
689  layer.out_point = self.lottie.out_point
690  composition.insert_layer(0, layer)
691 
692  self.shape_list(layer)
693 
694  if self.token.type == TokenType.Punctuation and self.token.value in ",;.":
695  self.next()
696 
697  def skip_and(self):
698  if self.token.type == TokenType.Punctuation and self.token.value == ",":
699  self.next()
700  self.skip_words("and")
701  return True
702  return self.skip_words("and")
703 
704  def shape_list(self, parent):
705  extent = min(self.lottie.width, self.lottie.height) * 0.4
706  shape = ShapeData(extent)
707  shape.count = self.count()
708 
709  while True:
710  ok = False
711 
712  color = self.color()
713  if color:
714  ok = True
715  shape.color = color
716  shape.color_explicit = True
717 
718  if self.check_words("transparent", "invisible"):
719  self.next()
720  shape.color = None
721  ok = True
722 
723  size_mult = self.size_multiplitier()
724  if size_mult:
725  ok = True
726  shape.scale(size_mult)
727 
728  if self.check_words("portrait"):
729  self.next()
730  shape.portrait = True
731  ok = True
732 
733  if self.check_words("landscape"):
734  self.next()
735  shape.portrait = False
736  ok = True
737 
738  lexind = self.lexer.save()
739  qualifier = self.size_qualifier()
740  if self.check_words("rounded"):
741  shape.roundness = qualifier
742  self.next()
743  ok = True
744  elif self.check_words("transparent"):
745  shape.opacity = (1 / qualifier)
746  self.next()
747  ok = True
748  elif lexind < self.lexer.token_index:
749  self.lexer.restore(lexind)
750 
751  if self.check_words("star", "polygon", "ellipse", "rectangle", "circle", "square", "text"):
752  shape_type = self.token.value
753  function = getattr(self, "shape_" + shape_type)
754  self.next()
755  shape_object = function(shape)
756  self.add_shape(parent, shape_object, shape)
757  return
758 
759  if self.token.type == TokenType.Word:
760  for name, sides in self.sides.items():
761  if self.token.value.startswith(name):
762  if self.token.value.endswith("gon"):
763  self.next()
764  shape_object = self.shape_polygon(shape, sides)
765  self.add_shape(parent, shape_object, shape)
766  return
767  elif self.token.value.endswith("gram"):
768  self.next()
769  shape_object = self.shape_star(shape, sides)
770  self.add_shape(parent, shape_object, shape)
771  return
772 
773  if self.check_words("triangle"):
774  self.next()
775  shape_object = self.shape_polygon(shape, 3)
776  self.add_shape(parent, shape_object, shape)
777  return
778 
779  sides = self.integer(None, False)
780  if sides is not None:
781  if self.check_words("sided", "pointed"):
782  self.next()
783  if self.check_words("polygon", "star"):
784  shape_type = self.token.value
785  function = getattr(self, "shape_" + shape_type)
786  self.next()
787  shape_object = function(shape, sides)
788  self.add_shape(parent, shape_object, shape)
789  return
790  else:
791  self.warn("Expected 'star' or 'polygon'")
792  return
793  else:
794  continue
795 
796  for svg_shape in self.svg_shapes:
797  if svg_shape.match(parent, self, shape):
798  return
799 
800  if not ok:
801  self.next()
802  break
803 
804  self.warn("Expected shape")
805 
806  def size_qualifier(self):
807  base = 1
808 
809  while True:
810  if self.check_words("very", "much"):
811  self.next()
812  base *= 1.33
813  elif self.check_words("extremely"):
814  self.next()
815  base *= 1.5
816  elif self.check_words("incredibly"):
817  self.next()
818  base *= 2
819  else:
820  break
821 
822  return base
823 
824  def size_multiplitier(self):
825  lexind = self.lexer.save()
826  base = self.size_qualifier()
827 
828  if self.check_words("small"):
829  self.next()
830  return 0.8 / base
831  elif self.check_words("large", "big"):
832  self.next()
833  return 1.2 * base
834  elif self.check_words("tiny"):
835  self.next()
836  return 0.5 / base
837  elif self.check_words("huge"):
838  self.next()
839  return 1.6 * base
840  else:
841  self.lexer.restore(lexind)
842  return None
843 
844  def add_shape(self, parent, shape_object, shape_data):
845  g = shapes.Group()
846  g.add_shape(shape_object)
847 
848  if shape_data.stroke and shape_data.stroke_on_top:
849  g.add_shape(shape_data.stroke)
850 
851  if shape_data.color:
852  fill = shapes.Fill(shape_data.color)
853  g.add_shape(fill)
854  shape_data.define_property("color", AnimatableType.Color, [fill.color])
855 
856  if shape_data.stroke and not shape_data.stroke_on_top:
857  g.add_shape(shape_data.stroke)
858 
859  if shape_data.opacity != 1:
860  g.transform.opacity.value = 100 * shape_data.opacity
861 
862  if "position" in shape_data.properties:
863  center = shape_data.properties["position"].get_initial()
864  else:
865  center = g.bounding_box(0).center()
866  g.transform.position.value = self.position(g, 0) + center
867  g.transform.anchor_point.value = NVector(*center)
868  shape_data.define_property("position", AnimatableType.Position, [g.transform.position], ["moves"])
869  shape_data.define_property("rotation", AnimatableType.Angle, [g.transform.rotation], ["rotates"])
870 
871  if self.check_words("rotated"):
872  self.next()
873  g.transform.rotation.value = self.angle(0)
874 
875  parent.insert_shape(0, g)
876 
877  if self.skip_words("that"):
878  self.animated_properties(shape_data)
879 
880  return g
881 
882  def position(self, shape: shapes.Group, time: float):
883  px = 0
884  py = 0
885 
886  qual = self.size_qualifier()
887 
888  if self.check_words("to", "in", "towards", "on", "at"):
889  self.next()
890  if self.check_words("the"):
891  self.next()
892  if self.token.type != TokenType.Word:
893  self.warn("Expected position")
894  return
895 
896  while True:
897  if self.check_words("left"):
898  px = -1
899  elif self.check_words("right"):
900  px = 1
901  elif self.check_words("top"):
902  py = -1
903  elif self.check_words("bottom"):
904  py = 1
905  elif self.check_words("center", "middle"):
906  pass
907  else:
908  break
909 
910  self.next()
911 
912  if self.check_words("side", "corner"):
913  self.next()
914 
915  if px == 0 and py == 0:
916  return shape.transform.position.get_value(time)
917 
918  box = shape.bounding_box(time)
919  center = box.center()
920  left = box.width / 2
921  right = self.lottie.width - box.width / 2
922  top = box.height / 2
923  bottom = self.lottie.height - box.height / 2
924 
925  pos = shape.transform.position.get_value(time)
926  x = pos.x
927  y = pos.y
928  dx = dy = 0
929 
930  if px < 0:
931  dx = left - center.x
932  elif px > 0:
933  dx = right - center.y
934 
935  if py < 0:
936  dy = top - center.y
937  elif py > 0:
938  dy = bottom - center.y
939 
940  return NVector(
941  x + dx * qual,
942  y + dy * qual,
943  )
944 
945  def animation_time(self, time, required):
946  if self.skip_words("at"):
947  if self.skip_words("the"):
948  self.require_one_of("end")
949  self.next()
950  return self.lottie.out_point
951  return self.time(time)
952  if self.skip_words("after"):
953  return self.time(0) + time
954  if required:
955  return time
956  return None
957 
958  def animated_properties(self, shape_data: ShapeData):
959  time = 0
960 
961  while True:
962  prop_time = self.animation_time(time, False)
963  changing = self.skip_words("changing", "changes")
964  possesive = self.possesive()
965  found_property = None
966  value = None
967  loop = False
968 
969  if possesive or changing:
970  for property in shape_data.properties.values():
971  if self.check_word_sequence(property.name):
972  found_property = property
973  if possesive and not changing:
974  if self.skip_words("loops"):
975  self.skip_words("back")
976  loop = True
977  break
978  else:
979  self.skip_words("changes")
980  value = self.animated_property(shape_data, property, time)
981  break
982  else:
983  for property in shape_data.properties.values():
984  if property.phrase and self.check_word_sequence(property.phrase):
985  found_property = property
986  value = self.animated_property(shape_data, property, time)
987  break
988 
989  if not found_property:
990  self.warn("Unknown property")
991  break
992 
993  if prop_time is None:
994  self.prop_time = self.animation_time(time, True)
995  time = prop_time
996 
997  if loop:
998  found_property.loop(time)
999  else:
1000  if value is None:
1001  break
1002 
1003  found_property.add_keyframe(time, value)
1004 
1005  cont = self.skip_and()
1006  cont = self.skip_words("then") or cont
1007  if not cont:
1008  break
1009 
1010  def animated_property_value(self, property: AnimatableProperty, time):
1011  if property.type == AnimatableType.Angle:
1012  return self.angle(None)
1013  elif property.type == AnimatableType.Number:
1014  return self.number(None)
1015  elif property.type == AnimatableType.Integer:
1016  return self.integer(None)
1017  elif property.type == AnimatableType.Color:
1018  value = self.color()
1019  if not value:
1020  self.warn("Expected color")
1021  return value
1022  elif property.type == AnimatableType.Position:
1023  return self.position_value(property.get_value(time))
1024  elif property.type == AnimatableType.Size:
1025  return self.vector_value()
1026  return None
1027 
1028  def animated_property(self, shape_data: ShapeData, property: AnimatableProperty, time):
1029  relative = False
1030  if not self.skip_words("to"):
1031  relative = self.skip_words("by")
1032 
1033  value = self.animated_property_value(property, time)
1034 
1035  if value is not None and relative:
1036  value += property.get_value(time)
1037 
1038  return value
1039 
1040  def vector_value(self):
1041  x = self.number(0)
1042  if self.token.type == TokenType.Punctuation and self.token.value == ",":
1043  self.next()
1044  y = self.number(0)
1045 
1046  return NVector(x, y)
1047 
1048  def position_value(self, start: NVector):
1049 
1050  if self.token.type == TokenType.Word:
1051  direction = None
1052 
1053  if self.check_words("left") or self.check_word_sequence("the", "left"):
1054  direction = NVector(-1, 0)
1055  elif self.check_words("right") or self.check_word_sequence("the", "right"):
1056  direction = NVector(1, 0)
1057  elif self.check_words("up", "upwards", "upward"):
1058  direction = NVector(0, -1)
1059  elif self.check_words("down", "downwards", "downward"):
1060  direction = NVector(0, 1)
1061 
1062  if direction is None:
1063  self.warn("Expected direction or position")
1064  return start
1065 
1066  self.skip_words("by")
1067 
1068  return start + direction * self.number()
1069 
1070  return self.vector_value()
1071 
1072  def shape_common_property(self, shape_data: ShapeData):
1073  color = NVector(0, 0, 0)
1074  width = 4
1075 
1076  while True:
1077  got_color = self.color()
1078  if got_color:
1079  color = got_color
1080  continue
1081 
1082  quant = self.size_qualifier()
1083  if self.check_words("thick"):
1084  self.next()
1085  width *= 1.5 * quant
1086  continue
1087  elif self.check_words("thin"):
1088  self.next()
1089  width *= 0.6 / quant
1090  continue
1091  else:
1092  break
1093 
1094  if self.check_words("stroke", "border", "outline", "edge", "borders", "edges"):
1095  self.next()
1096  shape_data.stroke = shapes.Stroke(color, width)
1097  return True
1098 
1099  return False
1100 
1101  def animated_properties_callback(self, shape_data: ShapeData):
1102  for property in shape_data.properties.values():
1103  if self.check_word_sequence(property.name):
1104  self.skip_words("to", "is", "are")
1105  property.set_initial(self.animated_property_value(property, 0))
1106  return True
1107  return False
1108 
1109  def shape_square(self, shape_data: ShapeData):
1110  pos = NVector(self.lottie.width / 2, self.lottie.height / 2)
1111  size = NVector(shape_data.extent, shape_data.extent)
1112  round_base = shape_data.extent / 5
1113  shape = shapes.Rect(pos, size, shape_data.roundness * round_base)
1114  shape_data.define_property("position", AnimatableType.Position, [shape.position])
1115  shape_data.define_property("size", AnimatableType.Size, [shape.position])
1116  self.properties(shape_data, self.animated_properties_callback, [shape_data], ["with", "of"])
1117  return shape
1118 
1119  def shape_circle(self, shape_data: ShapeData):
1120  pos = NVector(self.lottie.width / 2, self.lottie.height / 2)
1121  size = NVector(shape_data.extent, shape_data.extent)
1122  shape = shapes.Ellipse(pos, size)
1123  shape_data.define_property("position", AnimatableType.Position, [shape.position])
1124  shape_data.define_property("size", AnimatableType.Size, [shape.position])
1125  self.properties(shape_data, self.animated_properties_callback, [shape_data], ["with", "of"])
1126  return shape
1127 
1128  def shape_star(self, shape_data: ShapeData, sides: int = None):
1129  pos = NVector(self.lottie.width / 2, self.lottie.height / 2)
1130  round_base = shape_data.extent / 5
1131  roundness = shape_data.roundness * round_base
1132  shape = shapes.Star()
1133  shape.position.value = pos
1134  shape.inner_roundness.value = roundness
1135  shape.outer_radius.value = shape_data.extent / 2
1136  shape.outer_roundness.value = roundness
1137  shape.star_type = shapes.StarType.Star
1138  shape.points.value = sides or 5
1139 
1140  def callback():
1141  if self.animated_properties_callback(shape_data):
1142  return True
1143 
1144  if self.check_words("diameter"):
1145  shape.outer_radius.value = self.number(shape.outer_radius.value) / 2
1146  return True
1147  elif self.token.type == TokenType.Number:
1148  if sides:
1149  self.warn("Number of sides already specified")
1150  shape.points.value = self.integer(shape.points.value)
1151  if self.require_one_of("points", "sides", "point", "side"):
1152  self.next()
1153  return True
1154  return False
1155 
1156  shape.inner_radius.value = shape.outer_radius.value / 2
1157 
1158  shape_data.define_property("position", AnimatableType.Position, [shape.position])
1159  shape_data.define_property("outer radius", AnimatableType.Number, [shape.outer_radius])
1160  shape_data.define_property("radius", AnimatableType.Number, [shape.outer_radius])
1161  shape_data.define_property("inner radius", AnimatableType.Number, [shape.inner_radius])
1162  self.properties(shape_data, callback, [], ["with", "of", "has"])
1163 
1164  return shape
1165 
1166  def shape_polygon(self, shape_data: ShapeData, sides: int = None):
1167  shape = self.shape_star(shape_data, sides)
1168  shape.inner_radius = None
1169  shape.inner_roundness = None
1170  shape.star_type = shapes.StarType.Polygon
1171  return shape
1172 
1173  def angle_direction(self):
1174  if self.check_words("clockwise"):
1175  self.next()
1176  return 1
1177  elif self.check_words("counter"):
1178  self.next()
1179  if self.check_words("clockwise"):
1180  self.next()
1181  return -1
1182  return 0
1183 
1184  def fraction(self):
1185  if self.article():
1186  amount = 1
1187  else:
1188  amount = self.number(1)
1189 
1190  if self.check_words("full", "entire"):
1191  self.next()
1192  return amount, True
1193  elif self.check_words("half", "halfs"):
1194  self.next()
1195  return amount / 2, True
1196  elif self.check_words("third", "thirds"):
1197  self.next()
1198  return amount / 3, True
1199  elif self.check_words("quarter", "quarters"):
1200  self.next()
1201  return amount / 3, True
1202 
1203  return amount, False
1204 
1205  def angle(self, default):
1206  direction = self.angle_direction()
1207 
1208  amount, has_fraction = self.fraction()
1209 
1210  if self.skip_and():
1211  more_frac = self.fraction()[0]
1212  if has_fraction:
1213  amount += amount * more_frac
1214  else:
1215  amount += more_frac
1216 
1217  if self.check_words("turns"):
1218  self.next()
1219  amount *= 360
1220  elif self.require_one_of("degrees"):
1221  self.next()
1222  elif self.check_word_sequence(["pi", "radians"]):
1223  amount *= 180
1224  if direction == 0:
1225  direction = -1
1226 
1227  if direction == 0:
1228  direction = 1
1229  return amount * direction
1230 
1231  def rect_properties(self, shape_data: ShapeData, shape):
1232  extent = shape_data.extent
1233  parse_data = {
1234  "width": None,
1235  "height": None,
1236  "ratio": math.sqrt(2),
1237  "size_specified": False
1238  }
1239  handle_orientation = True
1240 
1241  shape_data.define_property("position", AnimatableType.Position, [shape.position])
1242  shape_data.define_property("size", AnimatableType.Size, [shape.size])
1243 
1244  def callback():
1245  if self.animated_properties_callback(shape_data):
1246  return True
1247 
1248  if self.check_words("ratio"):
1249  self.next()
1250  ratio = self.fraction()[0]
1251  if ratio <= 0:
1252  self.warn("Ratio must be positive")
1253  else:
1254  parse_data["ratio"] = ratio
1255  return True
1256  elif self.check_words("width"):
1257  self.next()
1258  parse_data["width"] = self.number(0)
1259  return True
1260  elif self.check_words("height"):
1261  self.next()
1262  parse_data["height"] = self.number(0)
1263  return True
1264  elif self.check_words("size"):
1265  parse_data["size_specified"] = True
1266 
1267  return False
1268 
1269  self.properties(shape_data, callback, [], ["with", "of", "has"])
1270 
1271  if not parse_data["size_specified"]:
1272  width = parse_data["width"]
1273  height = parse_data["height"]
1274  ratio = parse_data["ratio"]
1275 
1276  if width is None and height is None:
1277  width = extent
1278  height = width / ratio
1279  elif width is None:
1280  width = height * ratio
1281  elif height is None:
1282  height = width / ratio
1283  else:
1284  handle_orientation = False
1285 
1286  if handle_orientation:
1287  if (width > height and shape_data.portrait) or (width < height and not shape_data.portrait):
1288  width, height = height, width
1289 
1290  shape.size.value = NVector(width, height)
1291 
1292  def shape_rectangle(self, shape_data: ShapeData):
1293  pos = NVector(self.lottie.width / 2, self.lottie.height / 2)
1294  round_base = shape_data.extent / 5
1295 
1296  shape = shapes.Rect(pos, NVector(0, 0), shape_data.roundness * round_base)
1297  shape_data.define_property("roundness", AnimatableType.Number, [shape.rounded])
1298  self.rect_properties(shape_data, shape)
1299  return shape
1300 
1301  def shape_ellipse(self, shape_data: ShapeData):
1302  pos = NVector(self.lottie.width / 2, self.lottie.height / 2)
1303  shape = shapes.Ellipse(pos, NVector(0, 0))
1304  self.rect_properties(shape_data, shape)
1305  return shape
1306 
1307  def shape_text(self, shape_data: ShapeData):
1308  text = self.string("")
1309  font = self.font.clone()
1310  shape = font.render(text)
1311 
1312  box = shape.bounding_box()
1313 
1314  center = box.center()
1315  anim_center = NVector(self.lottie.width / 2, self.lottie.height / 2)
1316  shape.transform.anchor_point.value = anim_center
1317  shape.transform.position.value.y = self.lottie.height - center.y
1318  shape.transform.position.value.x = self.lottie.width - center.x
1319 
1320  scale = shape_data.size_multiplitier
1321  if box.width > self.lottie.width > 0:
1322  scale *= self.lottie.width / box.width
1323 
1324  if scale != 1:
1325  wrapper = shapes.Group()
1326  wrapper.transform.position.value = wrapper.transform.anchor_point.value = anim_center
1327  wrapper.transform.scale.value *= scale
1328  wrapper.add_shape(shape)
1329  return wrapper
1330 
1331  return shape
1332 
1333 
1335  def __init__(self):
1336  self.cache = {}
1337 
1338  def load(self, filename):
1339  if filename in self.cache:
1340  return self.cache[filename]
1341 
1342  anim = parse_svg_file(filename)
1343  self.cache[filename] = anim
1344  return anim
1345 
1346 
1348  def __init__(self, layer_names, colors):
1349  self.layer_names = layer_names
1350  self.colors = [parse_color(color) for color in colors]
1351 
1352  def iter_stylers(self, lottie_object):
1353  objects = []
1354  if self.layer_names:
1355  for layer_name in self.layer_names:
1356  found = lottie_object.find(layer_name)
1357  if found:
1358  objects.append(found)
1359  else:
1360  objects = [lottie_object]
1361 
1362  for object in objects:
1363  for styler in object.find_all((shapes.Fill, shapes.Stroke)):
1364  if len(self.colors) == 0 or styler.color.value in self.colors:
1365  yield styler
1366 
1367  def process(self, lottie_object, new_color):
1368  for styler in self.iter_stylers(lottie_object):
1369  styler.color.value = new_color
1370 
1371 
1372 class SvgShape:
1373  def __init__(self, file, phrase, feature_map, main_feature, facing_direction, svg_loader: SvgLoader):
1374  self.file = file
1375  self.phrase = phrase
1376  self.feature_map = feature_map
1377  if isinstance(main_feature, str):
1378  self.main_feature = feature_map[main_feature]
1379  else:
1380  self.main_feature = main_feature
1381  self.facing_direction = facing_direction
1382  self.svg_loader = svg_loader
1383 
1384  def callback(self, parser: Parser, shape_data: ShapeData):
1385  lexind = parser.lexer.save()
1386  color = parser.color()
1387  if color:
1388  if parser.check_words(*self.feature_map.keys()):
1389  feature_name = parser.lexer.token.value
1390  shape_data.properties[feature_name].set_initial(color)
1391  parser.next()
1392  return True
1393  else:
1394  parser.lexer.restore(lexind)
1395 
1396  return False
1397 
1398  def match(self, parent, parser: Parser, shape_data: ShapeData):
1399  if not parser.check_word_sequence(self.phrase):
1400  return False
1401 
1402  shape_data.stroke_on_top = False
1403  svg_anim = parse_svg_file(self.file, 0, parser.lottie.out_point, parser.lottie.frame_rate)
1404  layer = svg_anim.layers[0].clone()
1405  group = shapes.Group()
1406  group.name = layer.name
1407  group.shapes = layer.shapes + group.shapes
1408  layer.transform.clone_into(group.transform)
1409 
1410  wrapper = shapes.Group()
1411  wrapper.add_shape(group)
1412 
1413  delta_ap = group.bounding_box(0).center()
1414  wrapper.transform.anchor_point.value += delta_ap
1415  wrapper.transform.position.value += delta_ap
1416  wrapper.transform.scale.value *= shape_data.size_multiplitier
1417 
1418  for name, feature in self.feature_map.items():
1419  singular = name[:-1] if name.endswith("s") else name
1420  shape_data.properties[name] = AnimatableProperty(singular + " color", None, [], AnimatableType.Color)
1421  for styler in feature.iter_stylers(group):
1422  shape_data.add_property(name, styler.color)
1423 
1424  if self.main_feature:
1425  shape_data.define_property("color", AnimatableType.Color, [])
1426 
1427  for styler in self.main_feature.iter_stylers(group):
1428  shape_data.add_property("color", styler.color)
1429 
1430  if shape_data.color and shape_data.color_explicit:
1431  shape_data.properties["color"].set_initial(shape_data.color)
1432 
1433  parser.properties(shape_data, self.callback, [parser, shape_data], ["with"])
1434 
1435  if self.facing_direction != 0 and parser.check_words("facing", "looking"):
1436  parser.next()
1437  if parser.skip_words("to"):
1438  parser.skip_words("the")
1439 
1440  if parser.check_words("left", "right"):
1441  direction = -1 if parser.token.value == "left" else 1
1442  if direction != self.facing_direction:
1443  wrapper.transform.scale.value.x *= -1
1444  else:
1445  parser.warn("Missing facing direction")
1446 
1447  parser.next()
1448 
1449  shape_data.color = None
1450  parser.add_shape(parent, wrapper, shape_data)
1451  return True
lottie.utils.funky_parser.Parser.lottie
lottie
Definition: funky_parser.py:489
lottie.utils.funky_parser.Parser.string
def string(self, default)
Definition: funky_parser.py:645
lottie.utils.funky_parser.Parser.animated_properties
def animated_properties(self, ShapeData shape_data)
Definition: funky_parser.py:958
lottie.utils.funky_parser.Parser.size_qualifier
def size_qualifier(self)
Definition: funky_parser.py:806
lottie.utils.funky_parser.Parser.logger
logger
Definition: funky_parser.py:424
lottie.utils.funky_parser.AnimatableProperty.type
type
Definition: funky_parser.py:264
lottie.utils.funky_parser.SvgFeature.layer_names
layer_names
Definition: funky_parser.py:1349
lottie.utils.funky_parser.Parser.layer
def layer(self, composition)
Definition: funky_parser.py:682
lottie.utils.funky_parser.ShapeData.__init__
def __init__(self, extent)
Definition: funky_parser.py:289
lottie.utils.funky_parser.Logger
Definition: funky_parser.py:392
lottie.utils.funky_parser.StorageLogger.__init__
def __init__(self)
Definition: funky_parser.py:404
lottie.utils.funky_parser.ShapeData.roundness
roundness
Definition: funky_parser.py:295
lottie.utils.funky_parser.Parser.skip_words
def skip_words(self, *words)
Definition: funky_parser.py:513
lottie.utils.funky_parser.DummyLogger.warn
def warn(self, message)
Definition: funky_parser.py:399
lottie.utils.funky_parser.ShapeData
Definition: funky_parser.py:288
lottie.utils.funky_parser.Parser.animated_property_value
def animated_property_value(self, AnimatableProperty property, time)
Definition: funky_parser.py:1010
lottie.utils.funky_parser.Parser.check_word_sequence
def check_word_sequence(self, words)
Definition: funky_parser.py:526
lottie.utils.funky_parser.Parser.add_shape
def add_shape(self, parent, shape_object, shape_data)
Definition: funky_parser.py:844
lottie.utils.funky_parser.Parser.shape_ellipse
def shape_ellipse(self, ShapeData shape_data)
Definition: funky_parser.py:1301
lottie.utils.funky_parser.Parser.position
def position(self, shapes.Group shape, float time)
Definition: funky_parser.py:882
lottie.utils.funky_parser.ShapeData.portrait
portrait
Definition: funky_parser.py:294
lottie.utils.funky_parser.SvgShape.callback
def callback(self, Parser parser, ShapeData shape_data)
Definition: funky_parser.py:1384
lottie.utils.funky_parser.Lexer.__init__
def __init__(self, text)
Definition: funky_parser.py:319
lottie.utils.funky_parser.Parser.vector_value
def vector_value(self)
Definition: funky_parser.py:1040
lottie.utils.funky_parser.SvgShape.file
file
Definition: funky_parser.py:1374
lottie.utils.funky_parser.Parser.token
def token(self)
Definition: funky_parser.py:434
lottie.utils.funky_parser.ShapeData.define_property
def define_property(self, name, type, props, phrase=None)
Definition: funky_parser.py:306
lottie.utils.funky_parser.Lexer._new_token
def _new_token(self, Token token)
Definition: funky_parser.py:328
lottie.utils.funky_parser.SvgShape.phrase
phrase
Definition: funky_parser.py:1375
lottie.utils.funky_parser.Parser.animation_time
def animation_time(self, time, required)
Definition: funky_parser.py:945
lottie.utils.funky_parser.Parser.next
def next(self)
Definition: funky_parser.py:430
lottie.utils.funky_parser.AnimatableProperty.loop
def loop(self, time)
Definition: funky_parser.py:279
lottie.utils.funky_parser.ShapeData.add_property
def add_property(self, name, prop)
Definition: funky_parser.py:309
lottie.utils.funky_parser.Parser.animated_property
def animated_property(self, ShapeData shape_data, AnimatableProperty property, time)
Definition: funky_parser.py:1028
lottie.utils.funky_parser.ShapeData.color_explicit
color_explicit
Definition: funky_parser.py:291
lottie.utils.funky_parser.DummyLogger
Definition: funky_parser.py:398
lottie.utils.funky_parser.Parser.warn
def warn(self, message)
Definition: funky_parser.py:484
lottie.utils.funky_parser.Parser.properties
def properties(self, shape_data, callback, callback_args=[], words=["with"])
Definition: funky_parser.py:542
lottie.utils.funky_parser.ShapeData.size_multiplitier
size_multiplitier
Definition: funky_parser.py:293
lottie.utils.funky_parser.Lexer
Definition: funky_parser.py:313
lottie.utils.funky_parser.Parser.get_color
def get_color(self, dict word_dict, str complete_word)
Definition: funky_parser.py:457
lottie.objects.animation.Animation
Top level object, describing the animation.
Definition: animation.py:62
lottie.utils.funky_parser.AnimatableProperty.__init__
def __init__(self, name, phrase, props, AnimatableType type)
Definition: funky_parser.py:260
lottie.parsers.svg.importer.parse_svg_file
def parse_svg_file(file, layer_frames=0, *args, **kwargs)
Definition: importer.py:1310
lottie.utils.funky_parser.ShapeData.color
color
Definition: funky_parser.py:290
lottie.utils.funky_parser.Parser.lexer
lexer
Definition: funky_parser.py:422
lottie.utils.funky_parser.Parser.check_words
def check_words(self, *words)
Definition: funky_parser.py:507
lottie.utils.funky_parser.Parser.shape_common_property
def shape_common_property(self, ShapeData shape_data)
Definition: funky_parser.py:1072
lottie.utils.funky_parser.Parser
Definition: funky_parser.py:411
lottie.utils.funky_parser.Parser.angle_direction
def angle_direction(self)
Definition: funky_parser.py:1173
lottie.utils.funky_parser.Parser.allow_resize
allow_resize
Definition: funky_parser.py:425
lottie.utils.funky_parser.Parser.rect_properties
def rect_properties(self, ShapeData shape_data, shape)
Definition: funky_parser.py:1231
lottie.utils.funky_parser.Parser.__init__
def __init__(self, text, Logger logger)
Definition: funky_parser.py:421
lottie.utils.funky_parser.Parser.shape_circle
def shape_circle(self, ShapeData shape_data)
Definition: funky_parser.py:1119
lottie.utils.funky_parser.Lexer.next
def next(self)
Definition: funky_parser.py:334
lottie.utils.funky_parser.Lexer.text
text
Definition: funky_parser.py:320
lottie.utils.funky_parser.SvgShape.main_feature
main_feature
Definition: funky_parser.py:1378
lottie.utils.font.FontStyle
Definition: font.py:747
lottie.utils.funky_parser.Token.__init__
def __init__(self, TokenType type, int line, int col, int start, int end, typing.Any value=None)
Definition: funky_parser.py:228
lottie.utils.funky_parser.ShapeData.scale
def scale(self, multiplier)
Definition: funky_parser.py:302
lottie.utils.funky_parser.Lexer.restore
def restore(self, index)
Definition: funky_parser.py:384
lottie.utils.funky_parser.Parser.color
def color(self)
Definition: funky_parser.py:437
lottie.utils.funky_parser.SvgLoader.load
def load(self, filename)
Definition: funky_parser.py:1338
lottie.utils.funky_parser.ShapeData.count
count
Definition: funky_parser.py:297
lottie.utils.funky_parser.Parser.fraction
def fraction(self)
Definition: funky_parser.py:1184
lottie.utils.funky_parser.SvgShape.feature_map
feature_map
Definition: funky_parser.py:1376
lottie.utils.funky_parser.ShapeData.properties
properties
Definition: funky_parser.py:300
lottie.utils.funky_parser.SvgFeature.__init__
def __init__(self, layer_names, colors)
Definition: funky_parser.py:1348
lottie.utils.funky_parser.Parser.font
font
Definition: funky_parser.py:428
lottie.utils.funky_parser.Lexer.line
line
Definition: funky_parser.py:323
lottie.utils.funky_parser.Parser.animated_properties_callback
def animated_properties_callback(self, ShapeData shape_data)
Definition: funky_parser.py:1101
lottie.utils.funky_parser.SvgShape.svg_loader
svg_loader
Definition: funky_parser.py:1382
lottie.utils.funky_parser.AnimatableProperty.props
props
Definition: funky_parser.py:263
lottie.utils.funky_parser.Parser.animation
def animation(self)
Definition: funky_parser.py:578
lottie.utils.funky_parser.Parser.integer
def integer(self, default, warn=True)
Definition: funky_parser.py:625
lottie.utils.funky_parser.Parser.parse
def parse(self)
Definition: funky_parser.py:488
lottie.utils.funky_parser.AnimatableProperty.name
name
Definition: funky_parser.py:261
lottie.utils.funky_parser.SvgLoader.__init__
def __init__(self)
Definition: funky_parser.py:1335
lottie.utils.funky_parser.Parser.shape_list
def shape_list(self, parent)
Definition: funky_parser.py:704
lottie.utils.funky_parser.Lexer.line_pos
line_pos
Definition: funky_parser.py:324
lottie.utils.funky_parser.AnimatableProperty.phrase
phrase
Definition: funky_parser.py:262
lottie.utils.funky_parser.AnimatableProperty.add_keyframe
def add_keyframe(self, time, value)
Definition: funky_parser.py:273
lottie.utils.funky_parser.SvgLoader.cache
cache
Definition: funky_parser.py:1336
lottie.utils.funky_parser.StorageLogger.messages
messages
Definition: funky_parser.py:405
lottie.utils.funky_parser.Parser.simple_properties_callback
def simple_properties_callback(self, object, properties)
Definition: funky_parser.py:566
lottie.utils.funky_parser.Parser.number
def number(self, default)
Definition: funky_parser.py:635
lottie.utils.funky_parser.SvgLoader
Definition: funky_parser.py:1334
lottie.utils.funky_parser.TokenType
Definition: funky_parser.py:218
lottie.utils.funky_parser.AnimatableProperty.get_value
def get_value(self, time)
Definition: funky_parser.py:284
lottie.utils.funky_parser.Lexer.token_index
token_index
Definition: funky_parser.py:326
lottie.parsers.svg.importer.parse_color
def parse_color(color, current_color=Color(0, 0, 0, 1))
Parses CSS colors.
Definition: importer.py:134
lottie.utils.funky_parser.Parser.count
def count(self, default=1)
Definition: funky_parser.py:677
lottie.utils.funky_parser.Lexer.pos
pos
Definition: funky_parser.py:321
lottie.utils.funky_parser.ShapeData.extent
extent
Definition: funky_parser.py:292
lottie.utils.funky_parser.SvgShape.match
def match(self, parent, Parser parser, ShapeData shape_data)
Definition: funky_parser.py:1398
lottie.utils.funky_parser.AnimatableProperty.set_initial
def set_initial(self, value)
Definition: funky_parser.py:266
lottie.utils.funky_parser.SvgFeature
Definition: funky_parser.py:1347
lottie.utils.funky_parser.Parser.article
def article(self)
Definition: funky_parser.py:501
lottie.utils.funky_parser.Lexer.back
def back(self)
Definition: funky_parser.py:377
lottie.utils.funky_parser.Parser.prop_time
prop_time
Definition: funky_parser.py:994
lottie.utils.funky_parser.SvgFeature.iter_stylers
def iter_stylers(self, lottie_object)
Definition: funky_parser.py:1352
lottie.utils.funky_parser.Parser.get_color_value
def get_color_value(self, value, str complete_word)
Definition: funky_parser.py:440
lottie.utils.funky_parser.Logger.warn
def warn(self, message)
Definition: funky_parser.py:393
lottie.utils.funky_parser.SvgFeature.colors
colors
Definition: funky_parser.py:1350
lottie.utils.funky_parser.Parser.size_multiplitier
def size_multiplitier(self)
Definition: funky_parser.py:824
lottie.utils.funky_parser.Token.line
line
Definition: funky_parser.py:230
lottie.utils.funky_parser.AnimatableProperty.get_initial
def get_initial(self)
Definition: funky_parser.py:270
lottie.utils.funky_parser.Parser.position_value
def position_value(self, NVector start)
Definition: funky_parser.py:1048
lottie.utils.funky_parser.Token.__repr__
def __repr__(self)
Definition: funky_parser.py:244
lottie.utils.funky_parser.Token.value
value
Definition: funky_parser.py:234
lottie.utils.funky_parser.Parser.require_one_of
def require_one_of(self, *words)
Definition: funky_parser.py:519
lottie.utils.funky_parser.Parser.complete_color
def complete_color(self, dict word_dict, str complete_word)
Definition: funky_parser.py:450
lottie.utils.funky_parser.SvgShape.facing_direction
facing_direction
Definition: funky_parser.py:1381
lottie.utils.funky_parser.Lexer.save
def save(self)
Definition: funky_parser.py:381
lottie.utils.funky_parser.Parser.shape_star
def shape_star(self, ShapeData shape_data, int sides=None)
Definition: funky_parser.py:1128
lottie.utils.funky_parser.SvgShape
Definition: funky_parser.py:1372
lottie.utils.funky_parser.Lexer.tokens
tokens
Definition: funky_parser.py:325
lottie.utils.funky_parser.ShapeData.opacity
opacity
Definition: funky_parser.py:296
lottie.utils.funky_parser.Parser.svg_shapes
svg_shapes
Definition: funky_parser.py:427
lottie.utils.funky_parser.Parser.shape_text
def shape_text(self, ShapeData shape_data)
Definition: funky_parser.py:1307
lottie.utils.funky_parser.AnimatableProperty
Definition: funky_parser.py:259
lottie.utils.funky_parser.Parser.possesive
def possesive(self)
Definition: funky_parser.py:539
lottie.utils.funky_parser.Lexer.expression
expression
Definition: funky_parser.py:314
lottie.utils.funky_parser.Token
Definition: funky_parser.py:227
lottie.utils.funky_parser.StorageLogger.warn
def warn(self, message)
Definition: funky_parser.py:407
lottie.utils.funky_parser.SvgFeature.process
def process(self, lottie_object, new_color)
Definition: funky_parser.py:1367
lottie.utils.funky_parser.AnimatableType
Definition: funky_parser.py:250
lottie.utils.funky_parser.Parser.layers
def layers(self, composition)
Definition: funky_parser.py:654
lottie.utils.funky_parser.Token.start
start
Definition: funky_parser.py:232
lottie.utils.funky_parser.Parser.angle
def angle(self, default)
Definition: funky_parser.py:1205
lottie.utils.funky_parser.Parser.shape_rectangle
def shape_rectangle(self, ShapeData shape_data)
Definition: funky_parser.py:1292
lottie.utils.funky_parser.Token.col
col
Definition: funky_parser.py:229
lottie.utils.funky_parser.Lexer.token
token
Definition: funky_parser.py:322
lottie.utils.funky_parser.Parser.skip_and
def skip_and(self)
Definition: funky_parser.py:697
lottie.utils.funky_parser.Token.type
type
Definition: funky_parser.py:233
lottie.utils.funky_parser.Parser.time
def time(self, default)
Definition: funky_parser.py:606
lottie.utils.funky_parser.ShapeData.stroke
stroke
Definition: funky_parser.py:298
lottie.utils.funky_parser.Parser.shape_square
def shape_square(self, ShapeData shape_data)
Definition: funky_parser.py:1109
lottie.utils.funky_parser.Parser.sides
dictionary sides
Definition: funky_parser.py:412
lottie.utils.funky_parser.Parser.max_duration
max_duration
Definition: funky_parser.py:426
lottie.utils.funky_parser.SvgShape.__init__
def __init__(self, file, phrase, feature_map, main_feature, facing_direction, SvgLoader svg_loader)
Definition: funky_parser.py:1373
lottie.nvector.NVector
Definition: nvector.py:9
lottie.utils.funky_parser.ShapeData.stroke_on_top
stroke_on_top
Definition: funky_parser.py:299
lottie.utils.funky_parser.Parser.shape_polygon
def shape_polygon(self, ShapeData shape_data, int sides=None)
Definition: funky_parser.py:1166
lottie.utils.funky_parser.Token.end
end
Definition: funky_parser.py:231