Coverage for lib/lottie/exporters/tgs_validator.py: 39%
75 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-20 16:17 +0100
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-20 16:17 +0100
1import os
2import enum
3import json
4import inspect
6from ..parsers.tgs import parse_tgs
7from ..objects.base import ObjectVisitor
8from ..objects.animation import Animation
9from ..objects import layers
10from ..objects import shapes
11from ..objects import helpers
14class Severity(enum.Enum):
15 Note = enum.auto()
16 Warning = enum.auto()
17 Error = enum.auto()
20class TgsError:
21 def __init__(self, message, target, severity=Severity.Warning):
22 self.message = message
23 self.target = target
24 self.severity = severity
26 def target_id(self):
27 if isinstance(self.target, str):
28 return self.target
29 if getattr(self.target, "name", ""):
30 return self.target.name
31 return self.target.__class__.__name__
33 def __str__(self):
34 return "%s: on %s: %s" % (
35 self.severity.name,
36 self.target_id(),
37 self.message
38 )
41class TgsValidator(ObjectVisitor):
42 def __init__(self, severity=Severity.Note):
43 self.errors = []
44 self.severity = severity
46 def _check(self, expr, message, target, severity=Severity.Warning):
47 if severity.value >= self.severity.value and not expr:
48 self.errors.append(TgsError(message, target, severity))
50 def check_file_size(self, filename):
51 return self.check_size(os.path.getsize(filename))
53 def check_size(self, bytes, filename="file"):
54 size_k = bytes / 1024
55 self._check(
56 size_k <= 64,
57 "Invalid size (%.1fk), should be less than 64k" % size_k,
58 filename,
59 Severity.Error
60 )
62 def check_file(self, filename):
63 self.check_file_size(filename)
64 try:
65 self(parse_tgs(filename))
66 except json.decoder.JSONDecodeError as e:
67 self._check(
68 False,
69 "Invalid JSON: %s" % e,
70 filename,
71 Severity.Error
72 )
74 def visit(self, object):
75 for cls in inspect.getmro(object.__class__):
76 callback = "_visit_%s" % cls.__name__.lower()
77 if hasattr(self, callback):
78 getattr(self, callback)(object)
80 def _visit_animation(self, o: Animation):
81 self._check(
82 o.frame_rate in {30, 60},
83 "Invalid framerate %s, should be 30 or 60" % o.frame_rate,
84 o,
85 Severity.Error
86 )
87 self._check(
88 o.width == 512,
89 "Invalid width %s, should be 512" % o.width,
90 o,
91 Severity.Error
92 )
93 self._check(
94 o.height == 512,
95 "Invalid height %s, should be 512" % o.height,
96 o,
97 Severity.Error
98 )
99 self._check(
100 (o.out_point-o.in_point) <= 180,
101 "Too many frames (%s), should be less than 180" % (o.out_point-o.in_point),
102 o,
103 Severity.Error
104 )
106 def _visit_layer(self, o: layers.Layer):
107 self._check(
108 not o.has_masks and not o.masks,
109 "Masks are not officially supported",
110 o,
111 Severity.Note
112 )
113 self._check(
114 not o.effects,
115 "Effects are not supported",
116 o,
117 Severity.Warning
118 )
119 self._check(
120 not o.threedimensional,
121 "3D layers are not supported",
122 o,
123 Severity.Warning
124 )
125 self._check(
126 not isinstance(o, layers.TextLayer),
127 "Text layers are not supported",
128 o,
129 Severity.Warning
130 )
131 self._check(
132 not isinstance(o, layers.ImageLayer),
133 "Image layers are not supported",
134 o,
135 Severity.Warning
136 )
137 self._check(
138 not o.auto_orient,
139 "Auto-orient layers are not supported",
140 o,
141 Severity.Warning
142 )
143 self._check(
144 o.matte_mode in {None, layers.MatteMode.Normal},
145 "Mattes are not supported",
146 o,
147 Severity.Warning
148 )
150 def _visit_precomplayer(self, o: layers.PreCompLayer):
151 self._check(
152 o.time_remapping is None,
153 "Time remapping is not supported",
154 o,
155 Severity.Warning
156 )
158 def _visit_merge(self, o: shapes.Merge):
159 self._check(
160 False,
161 "Merge paths are not supported",
162 o,
163 Severity.Warning
164 )
166 def _visit_transform(self, o: helpers.Transform):
167 self._check(
168 o.skew is None or (
169 not o.skew.animated and o.skew.value == 0
170 ),
171 "Skew transforms are not supported",
172 o,
173 Severity.Warning
174 )
176 def _visit_gradientstroke(self, o: shapes.GradientStroke):
177 self._check(
178 False,
179 "Gradient strokes are not officially supported",
180 o,
181 Severity.Note
182 )
184 def _visit_star(self, o: shapes.Star):
185 self._check(
186 False,
187 "Star Shapes are not officially supported",
188 o,
189 Severity.Note
190 )
192 def _visit_repeater(self, o: shapes.Repeater):
193 self._check(
194 False,
195 "Repeaters are not officially supported",
196 o,
197 Severity.Note
198 )