Coverage for lib/lottie/parsers/sif/ast_impl/base.py: 94%

85 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-20 16:17 +0100

1from xml.dom import minidom 

2import enum 

3from lottie.parsers.sif.sif.core import TypeDescriptor, ObjectRegistry, SifNodeMeta, FrameTime 

4from lottie.parsers.sif.xml.utils import xml_child_elements, xml_first_element_child 

5 

6 

7class SifAstNode: 

8 _subclasses = None 

9 _tag = None 

10 

11 @staticmethod 

12 def from_dom(xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry): 

13 if xml.tagName == param.typename: 

14 return SifValue.from_dom(xml, param, registry) 

15 

16 xmltype = xml.getAttribute("type") 

17 if xmltype != param.typename and xmltype != "weighted_" + param.typename: 17 ↛ 18line 17 didn't jump to line 18, because the condition on line 17 was never true

18 raise ValueError("Invalid type %s (should be %s)" % (xmltype, param.typename)) 

19 

20 return SifAstNode.ast_node_types()[xml.tagName].from_dom(xml, param, registry) 

21 

22 @staticmethod 

23 def ast_node_types(): 

24 if SifAstNode._subclasses is None: 

25 from . import nodes 

26 SifAstNode._subclasses = {} 

27 SifAstNode._gather_ast_types(SifAstNode) 

28 return SifAstNode._subclasses 

29 

30 @staticmethod 

31 def _gather_ast_types(cls): 

32 for subcls in cls.__subclasses__(): 

33 if subcls._tag: 

34 SifAstNode._subclasses[subcls._tag] = subcls 

35 SifAstNode._gather_ast_types(subcls) 

36 

37 def _prepare_to_dom(self, dom: minidom.Document, param: TypeDescriptor): 

38 element = dom.createElement(self._tag) 

39 element.setAttribute("type", param.typename) 

40 return element 

41 

42 

43class SifValue(SifAstNode): 

44 def __init__(self, value=None): 

45 self.value = value 

46 

47 def __repr__(self): 

48 return "<%s %r>" % (self.__class__.__name__, self.value) 

49 

50 @classmethod 

51 def from_dom(cls, xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry): 

52 return SifValue(param.value_from_xml_element(xml, registry)) 

53 

54 def to_dom(self, dom: minidom.Document, param: TypeDescriptor): 

55 return param.value_to_xml_element(self.value, dom) 

56 

57 

58class Interpolation(enum.Enum): 

59 Auto = "auto" 

60 Linear = "linear" 

61 Clamped = "clamped" 

62 Ease = "halt" 

63 Constant = "constant" 

64 

65 

66class SifKeyframe: 

67 def __init__(self, value, time: FrameTime, before=Interpolation.Clamped, after=Interpolation.Clamped): 

68 self.value = value 

69 self.time = time 

70 self.before = before 

71 self.after = after 

72 

73 @classmethod 

74 def from_dom(cls, xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry): 

75 return cls( 

76 param.value_from_xml_element(xml_first_element_child(xml), registry), 

77 FrameTime.parse_string(xml.getAttribute("time"), registry), 

78 Interpolation(xml.getAttribute("before")), 

79 Interpolation(xml.getAttribute("after")) 

80 ) 

81 

82 def to_dom(self, dom: minidom.Document, param: TypeDescriptor): 

83 element = dom.createElement("waypoint") 

84 element.setAttribute("time", str(self.time)) 

85 element.setAttribute("before", self.before.value) 

86 element.setAttribute("after", self.after.value) 

87 element.appendChild(param.value_to_xml_element(self.value, dom)) 

88 return element 

89 

90 def __repr__(self): 

91 return "<SifKeyframe %s %s>" % (self.time, self.value) 

92 

93 

94class SifAnimated(SifAstNode): 

95 _tag = "animated" 

96 

97 def __init__(self): 

98 self.keyframes = [] 

99 

100 @classmethod 

101 def from_dom(cls, xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry): 

102 obj = SifAnimated() 

103 for waypoint in xml_child_elements(xml, "waypoint"): 

104 obj.keyframes.append(SifKeyframe.from_dom(waypoint, param, registry)) 

105 return obj 

106 

107 def to_dom(self, dom: minidom.Document, param: TypeDescriptor): 

108 element = self._prepare_to_dom(dom, param) 

109 for kf in self.keyframes: 

110 element.appendChild(kf.to_dom(dom, param)) 

111 return element 

112 

113 def add_keyframe(self, *args, **kwargs): 

114 if not kwargs and len(args) == 1 and isinstance(args[0], SifKeyframe): 114 ↛ 115line 114 didn't jump to line 115, because the condition on line 114 was never true

115 keyframe = args[0] 

116 else: 

117 keyframe = SifKeyframe(*args, **kwargs) 

118 

119 self.keyframes.append(keyframe) 

120 return keyframe