4 from xml.etree
import ElementTree
5 from ...
import objects
6 from ...nvector
import NVector
7 from .svgdata
import color_table, css_atrrs
8 from .handler
import SvgHandler, NameMode
9 from ...utils.ellipse
import Ellipse
10 from ...utils.transform
import TransformMatrix
11 from ...utils.color
import Color
14 from ...utils
import font
23 def __init__(self, name, comp, value, percent):
30 if self.
valuevalue
is None:
34 return self.
valuevalue
36 if self.
compcomp ==
"w":
37 return (bbox.x2 - bbox.x1) * self.
valuevalue
39 if self.
compcomp ==
"x":
40 return bbox.x1 + (bbox.x2 - bbox.x1) * self.
valuevalue
42 return bbox.y1 + (bbox.y2 - bbox.y1) * self.
valuevalue
44 def parse(self, attr, default_percent):
47 if attr.endswith(
"%"):
51 self.
percentpercent = default_percent
62 self.
colorscolors.append((offset, color[:4]))
66 @param gradient_shape Should be a GradientFill or GradientStroke
67 @param shape ShapeElement to apply the gradient to
68 @param time Time to fetch properties from @p shape
71 for off, col
in self.
colorscolors:
72 gradient_shape.colors.add_color(off, col)
75 setattr(self, value.name, value)
76 self.
coordscoords.append(value)
79 relunits = attrib.get(
"gradientUnits",
"") !=
"userSpaceOnUse"
80 for c
in self.
coordscoords:
81 c.parse(attrib.get(c.name,
None), relunits)
93 bbox = shape.bounding_box(time)
94 gradient_shape.start_point.value = self.
matrixmatrix.apply(
NVector(
95 self.x1.to_value(bbox),
96 self.y1.to_value(bbox),
98 gradient_shape.end_point.value = self.
matrixmatrix.apply(
NVector(
99 self.x2.to_value(bbox),
100 self.y2.to_value(bbox),
102 gradient_shape.gradient_type = objects.GradientType.Linear
104 super().
to_lottie(gradient_shape, shape, time)
117 bbox = shape.bounding_box(time)
118 cx = self.cx.to_value(bbox)
119 cy = self.cy.to_value(bbox)
120 gradient_shape.start_point.value = self.
matrixmatrix.apply(
NVector(cx, cy))
121 r = self.r.to_value(bbox)
122 gradient_shape.end_point.value = self.
matrixmatrix.apply(
NVector(cx+r, cy))
124 fx = self.fx.to_value(bbox, cx) - cx
125 fy = self.fy.to_value(bbox, cy) - cy
126 gradient_shape.highlight_angle.value = math.atan2(fy, fx) * 180 / math.pi
127 gradient_shape.highlight_length.value = math.hypot(fx, fy)
129 gradient_shape.gradient_type = objects.GradientType.Radial
131 super().
to_lottie(gradient_shape, shape, time)
138 @see https://www.w3.org/wiki/CSS/Properties/color
141 if re.match(
r"^#[0-9a-fA-F]{6}$", color):
142 return Color(
int(color[1:3], 16) / 0xff,
int(color[3:5], 16) / 0xff,
int(color[5:7], 16) / 0xff, 1)
144 if re.match(
r"^#[0-9a-fA-F]{3}$", color):
145 return Color(
int(color[1], 16) / 0xf,
int(color[2], 16) / 0xf,
int(color[3], 16) / 0xf, 1)
147 match = re.match(
r"^rgba\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.eE]+)\s*\)$", color)
151 match = re.match(
r"^rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)$", color)
153 return Color(
int(match[1])/255,
int(match[2])/255,
int(match[3])/255, 1)
155 match = re.match(
r"^rgb\s*\(\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*\)$", color)
159 match = re.match(
r"^rgba\s*\(\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)\s*\)$", color)
163 if color ==
"transparent":
164 return Color(0, 0, 0, 0)
166 match = re.match(
r"^hsl\s*\(\s*([0-9.eE]+)\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*\)$", color)
168 return Color(*(colorsys.hls_to_rgb(
float(match[1])/360,
float(match[3])/100,
float(match[2])/100) + (1,)))
170 match = re.match(
r"^hsla\s*\(\s*([0-9.eE]+)\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)\s*\)$", color)
174 if color
in {
"currentColor",
"inherit"}:
175 return current_color.clone()
177 return Color(*color_table[color])
185 self.
itemsitems[shape.name] = shape
188 return self.
itemsitems[key]
191 self.
itemsitems[key] = value
194 return key
in self.
itemsitems
211 def _get_name(self, element, inkscapequal):
212 if self.
name_modename_mode == NameMode.Inkscape:
213 return element.attrib.get(inkscapequal, element.attrib.get(
"id"))
214 return self.
_get_id_get_id(element)
216 def _get_id(self, element):
217 if self.
name_modename_mode != NameMode.NoName:
218 return element.attrib.get(
"id")
221 def _parse_viewbox(self, attrib):
222 return map(float, attrib.replace(
",",
" ").split())
230 svg = etree.getroot()
234 if "width" in svg.attrib
and "height" in svg.attrib:
235 animation.width =
int(round(self.
_parse_unit_parse_unit(svg.attrib[
"width"])))
236 animation.height =
int(round(self.
_parse_unit_parse_unit(svg.attrib[
"height"])))
238 _, _, animation.width, animation.height = self.
_parse_viewbox_parse_viewbox(svg.attrib[
"viewBox"])
239 animation.name = self.
_get_name_get_name(svg, self.
qualifiedqualified(
"sodipodi",
"docname"))
245 layer.in_point = self.
max_timemax_time
246 animation.add_layer(layer)
248 self.
max_timemax_time += layer_frames
249 layer.out_point = self.
max_timemax_time
254 animation.out_point = self.
max_timemax_time
256 self.
_fix_viewbox_fix_viewbox(svg, (layer
for layer
in animation.layers
if not layer.parent_index))
261 svg = etree.getroot()
267 def _get_dpi(self, svg):
268 self.
dpidpi =
float(svg.attrib.get(self.
qualifiedqualified(
"inkscape",
"export-xdpi"), self.
dpidpi))
270 def _svg_to_layer(self, animation, svg):
273 animation.add_layer(layer)
277 sublayer.out_point = self.
max_timemax_time
280 def _fix_viewbox(self, svg, layers):
281 if "viewBox" in svg.attrib:
282 vbx, vby, vbw, vbh = self.
_parse_viewbox_parse_viewbox(svg.attrib[
"viewBox"])
283 if vbx != 0
or vby != 0
or vbw != self.
animationanimation.width
or vbh != self.
animationanimation.height:
285 layer.transform.position.value = -
NVector(vbx, vby)
288 def _parse_unit(self, value):
289 if not isinstance(value, str):
294 if value.endswith(
"px"):
296 elif value.endswith(
"vw"):
298 mult = self.
animationanimation.width * 0.01
299 elif value.endswith(
"vh"):
301 mult = self.
animationanimation.height * 0.01
302 elif value.endswith(
"vmin"):
305 elif value.endswith(
"vmax"):
308 elif value.endswith(
"in"):
311 elif value.endswith(
"pc"):
313 mult = self.
dpidpi / 6
314 elif value.endswith(
"pt"):
316 mult = self.
dpidpi / 72
317 elif value.endswith(
"cm"):
319 mult = self.
dpidpi / cmin
320 elif value.endswith(
"mm"):
322 mult = self.
dpidpi / cmin / 10
323 elif value.endswith(
"Q"):
325 mult = self.
dpidpi / cmin / 40
327 return float(value) * mult
333 bb = group.bounding_box()
335 itcx = self.
qualifiedqualified(
"inkscape",
"transform-center-x")
336 if itcx
in element.attrib:
337 cx =
float(element.attrib[itcx])
338 cy =
float(element.attrib[self.
qualifiedqualified(
"inkscape",
"transform-center-y")])
339 bbx, bby = bb.center()
342 dest_trans.anchor_point.value =
NVector(cx, cy)
343 dest_trans.position.value =
NVector(cx, cy)
349 if "transform" not in element.attrib:
355 for t
in re.finditer(
r"([a-zA-Z]+)\s*\(([^\)]*)\)", element.attrib[
"transform"]):
357 params = list(map(float, t[2].strip().replace(
",",
" ").split()))
358 if name ==
"translate":
359 dest_trans.position.value +=
NVector(
361 (params[1]
if len(params) > 1
else 0),
363 elif name ==
"scale":
365 dest_trans.scale.value[0] = (dest_trans.scale.value[0] / 100 * xfac) * 100
366 yfac = params[1]
if len(params) > 1
else xfac
367 dest_trans.scale.value[1] = (dest_trans.scale.value[1] / 100 * yfac) * 100
368 elif name ==
"rotate":
375 dap = ap - dest_trans.position.value
376 dest_trans.position.value += dap
377 dest_trans.anchor_point.value += dap
378 dest_trans.rotation.value = ang
384 dest_trans.position.value -= dest_trans.anchor_point.value
385 dest_trans.anchor_point.value =
NVector(0, 0)
386 trans = matrix.extract_transform()
387 dest_trans.skew_axis.value = math.degrees(trans[
"skew_axis"])
388 dest_trans.skew.value = -math.degrees(trans[
"skew_angle"])
389 dest_trans.position.value += trans[
"translation"]
390 dest_trans.rotation.value -= math.degrees(trans[
"angle"])
391 dest_trans.scale.value *= trans[
"scale"]
394 style = parent_style.copy()
395 for att
in css_atrrs & set(element.attrib.keys()):
396 if att
in element.attrib:
397 style[att] = element.attrib[att]
398 if "style" in element.attrib:
399 style.update(**dict(map(
400 lambda x: map(
lambda y: y.strip(), x.split(
":")),
401 filter(bool, element.attrib[
"style"].split(
";"))
406 opacity =
float(style.get(
"opacity", 1))
407 transform.opacity.value = opacity * 100
410 if style.get(
"display",
"inline") ==
"none" or style.get(
"visibility",
"visible") ==
"hidden":
413 def add_shapes(self, element, shapes, shape_parent, parent_style):
414 style = self.
parse_styleparse_style(element, parent_style)
419 group.name = self.
_get_name_get_name(element, self.
qualifiedqualified(
"inkscape",
"label"))
421 shape_parent.shapes.insert(0, group)
423 group.add_shape(shape)
431 def _add_style_shapes(self, style, group):
432 stroke_color = style.get(
"stroke",
"none")
433 if stroke_color
not in nocolor:
434 if stroke_color.startswith(
"url"):
440 stroke.color.value = color
442 group.add_shape(stroke)
444 stroke.opacity.value = opacity *
float(style.get(
"stroke-opacity", 1)) * 100
446 stroke.width.value = self.
_parse_unit_parse_unit(style.get(
"stroke-width", 1))
448 linecap = style.get(
"stroke-linecap")
449 if linecap ==
"round":
450 stroke.line_cap = objects.shapes.LineCap.Round
451 elif linecap ==
"butt":
452 stroke.line_cap = objects.shapes.LineCap.Butt
453 elif linecap ==
"square":
454 stroke.line_cap = objects.shapes.LineCap.Square
456 linejoin = style.get(
"stroke-linejoin")
457 if linejoin ==
"round":
458 stroke.line_join = objects.shapes.LineJoin.Round
459 elif linejoin ==
"bevel":
460 stroke.line_join = objects.shapes.LineJoin.Bevel
461 elif linejoin
in {
"miter",
"arcs",
"miter-clip"}:
462 stroke.line_join = objects.shapes.LineJoin.Miter
464 stroke.miter_limit = self.
_parse_unit_parse_unit(style.get(
"stroke-miterlimit", 0))
466 dash_array = style.get(
"stroke-dasharray")
467 if dash_array
and dash_array !=
"none":
468 values = list(map(self.
_parse_unit_parse_unit, dash_array.replace(
",",
" ").split()))
473 for i
in range(0, len(values), 2):
477 fill_color = style.get(
"fill",
"inherit")
478 if fill_color
not in nocolor:
479 if fill_color.startswith(
"url"):
486 opacity *=
float(style.get(
"fill-opacity", 1))
487 fill.opacity.value = opacity * 100
489 if style.get(
"fill-rule",
"") ==
"evenodd":
490 fill.fill_rule = objects.FillRule.EvenOdd
492 group.add_shape(fill)
494 def _parseshape_use(self, element, shape_parent, parent_style):
495 link = element.attrib[self.
qualifiedqualified(
"xlink",
"href")]
496 if link.startswith(
"#"):
498 base_element = self.
documentdocument.find(
".//*[@id='%s']" % id)
499 use_style = self.
parse_styleparse_style(element, parent_style)
501 shape_parent.add_shape(used)
503 used.transform.position.value.x =
float(element.attrib.get(
"x", 0))
504 used.transform.position.value.y =
float(element.attrib.get(
"y", 0))
506 self.
parse_shapeparse_shape(base_element, used, use_style)
509 def _parseshape_g(self, element, shape_parent, parent_style):
511 shape_parent.shapes.insert(0, group)
512 style = self.
parse_styleparse_style(element, parent_style)
515 group.name = self.
_get_name_get_name(element, self.
qualifiedqualified(
"inkscape",
"label"))
519 group.transform.opacity.value = 0
522 def _parseshape_ellipse(self, element, shape_parent, parent_style):
524 ellipse.position.value =
NVector(
529 self.
_parse_unit_parse_unit(element.attrib[
"rx"]) * 2,
530 self.
_parse_unit_parse_unit(element.attrib[
"ry"]) * 2
532 self.
add_shapesadd_shapes(element, [ellipse], shape_parent, parent_style)
535 def _parseshape_anim_ellipse(self, ellipse, element, animations):
536 self.
_merge_animations_merge_animations(element, animations,
"cx",
"cy",
"position")
538 self.
_apply_animations_apply_animations(ellipse.position,
"position", animations)
541 def _parseshape_circle(self, element, shape_parent, parent_style):
543 ellipse.position.value =
NVector(
547 r = self.
_parse_unit_parse_unit(element.attrib[
"r"]) * 2
548 ellipse.size.value =
NVector(r, r)
549 self.
add_shapesadd_shapes(element, [ellipse], shape_parent, parent_style)
552 def _parseshape_anim_circle(self, ellipse, element, animations):
553 self.
_merge_animations_merge_animations(element, animations,
"cx",
"cy",
"position")
554 self.
_apply_animations_apply_animations(ellipse.position,
"position", animations)
557 def _parseshape_rect(self, element, shape_parent, parent_style):
559 w = self.
_parse_unit_parse_unit(element.attrib.get(
"width", 0))
560 h = self.
_parse_unit_parse_unit(element.attrib.get(
"height", 0))
562 self.
_parse_unit_parse_unit(element.attrib.get(
"x", 0)) + w / 2,
563 self.
_parse_unit_parse_unit(element.attrib.get(
"y", 0)) + h / 2
565 rect.size.value =
NVector(w, h)
566 rx = self.
_parse_unit_parse_unit(element.attrib.get(
"rx", 0))
567 ry = self.
_parse_unit_parse_unit(element.attrib.get(
"ry", 0))
568 rect.rounded.value = (rx + ry) / 2
569 self.
add_shapesadd_shapes(element, [rect], shape_parent, parent_style)
572 def _parseshape_anim_rect(self, rect, element, animations):
575 self.
_merge_animations_merge_animations(element, animations,
"x",
"y",
"position")
576 self.
_merge_animations_merge_animations(element, animations,
"position",
"size",
"position",
lambda p, s: p + s / 2)
578 self.
_merge_animations_merge_animations(element, animations,
"rx",
"ry",
"rounded",
lambda x, y: (x + y) / 2)
581 def _parseshape_line(self, element, shape_parent, parent_style):
583 line.shape.value.add_point(
NVector(
587 line.shape.value.add_point(
NVector(
591 return self.
add_shapesadd_shapes(element, [line], shape_parent, parent_style)
593 def _parseshape_anim_line(self, group, element, animations):
594 line = group.shapes[0]
605 self.
_merge_animations_merge_animations(element, animations,
"p1",
"p2",
"points", combine)
608 def _handle_poly(self, element):
610 coords = list(map(float, element.attrib[
"points"].replace(
",",
" ").split()))
611 for i
in range(0, len(coords), 2):
612 line.shape.value.add_point(
NVector(*coords[i:i+2]))
615 def _parseshape_polyline(self, element, shape_parent, parent_style):
617 return self.
add_shapesadd_shapes(element, [line], shape_parent, parent_style)
619 def _parseshape_polygon(self, element, shape_parent, parent_style):
621 line.shape.value.close()
622 return self.
add_shapesadd_shapes(element, [line], shape_parent, parent_style)
624 def _parseshape_path(self, element, shape_parent, parent_style):
625 d_parser =
PathDParser(element.attrib.get(
"d",
""))
628 for path
in d_parser.paths:
634 return self.
add_shapesadd_shapes(element, paths, shape_parent, parent_style)
637 for child
in element:
639 if not self.
parse_shapeparse_shape(child, shape_parent, parent_style):
640 handler = getattr(self,
"_parse_" + tag,
None)
645 handler = getattr(self,
"_parseshape_" + self.
unqualifiedunqualified(element.tag),
None)
647 out = handler(element, shape_parent, parent_style)
649 if element.attrib.get(
"id"):
650 self.
defsdefs.items[element.attrib[
"id"]] = out
654 def _parse_defs(self, element):
657 def _apply_transform_element_to_matrix(self, matrix, t):
659 params = list(map(float, t[2].strip().replace(
",",
" ").split()))
660 if name ==
"translate":
663 (params[1]
if len(params) > 1
else 0),
665 elif name ==
"scale":
667 yfac = params[1]
if len(params) > 1
else xfac
668 matrix.scale(xfac, yfac)
669 elif name ==
"rotate":
675 matrix.translate(-x, -y)
676 matrix.rotate(math.radians(ang))
677 matrix.translate(x, y)
679 matrix.rotate(math.radians(ang))
680 elif name ==
"skewX":
681 matrix.skew(math.radians(params[0]), 0)
682 elif name ==
"skewY":
683 matrix.skew(0, math.radians(params[0]))
684 elif name ==
"matrix":
686 m.a, m.b, m.c, m.d, m.tx, m.ty = params
689 def _transform_to_matrix(self, transform):
692 for t
in re.finditer(
r"([a-zA-Z]+)\s*\(([^\)]*)\)", transform):
697 def _gradient(self, element, grad):
698 grad.matrix = self.
_transform_to_matrix_transform_to_matrix(element.attrib.get(
"gradientTransform",
""))
700 id = element.attrib[
"id"]
702 grad.colors = self.
gradientsgradients[id].colors
703 grad.parse_attrs(element.attrib)
704 href = element.attrib.get(self.
qualifiedqualified(
"xlink",
"href"))
706 srcid = href.strip(
"#")
710 src = grad.__class__()
712 grad.colors = src.colors
714 for stop
in element.findall(
"./%s" % self.
qualifiedqualified(
"svg",
"stop")):
715 off =
float(stop.attrib[
"offset"].strip(
"%"))
716 if stop.attrib[
"offset"].endswith(
"%"):
719 color = self.
parse_colorparse_color(style[
"stop-color"])
720 if "stop-opacity" in style:
721 color[3] =
float(style[
"stop-opacity"])
722 grad.add_color(off, color)
725 def _parse_linearGradient(self, element):
728 def _parse_radialGradient(self, element):
732 match = re.match(
r"""url\(['"]?#([^)'"]+)['"]?\)""", color)
739 outgrad = gradientclass()
740 grad.to_lottie(outgrad, shape)
741 if self.
name_modename_mode != NameMode.NoName:
746 def _parse_text_style(self, style, font_style=None):
747 if "font-family" in style:
748 font_style.query.family(style[
"font-family"].strip(
"'\""))
750 if "font-style" in style:
751 if style[
"font-style"] ==
"oblique":
752 font_style.query.custom(
"slant", 110)
753 elif style[
"font-style"] ==
"italic":
754 font_style.query.custom(
"slant", 100)
756 if "font-weight" in style:
757 if style[
"font-weight"]
in {
"bold",
"bolder"}:
758 font_style.query.weight(200)
759 elif style[
"font-weight"] ==
"lighter":
760 font_style.query.weight(50)
761 elif style[
"font-weight"].isdigit():
762 font_style.query.css_weight(
int(style[
"font-weight"]))
764 if "font-size" in style:
765 fz = style[
"font-size"]
776 font_style.size = fz_names[fz]
777 elif fz ==
"smaller":
781 elif fz.endswith(
"px"):
782 font_style.size =
float(fz[:-2])
784 font_style.size =
float(fz)
786 if "text-align" in style:
787 ta = style[
"text-align"]
788 if ta
in (
"left",
"start"):
789 font_style.justify = font.TextJustify.Left
791 font_style.justify = font.TextJustify.Center
792 elif ta
in (
"right",
"end"):
793 font_style.justify = font.TextJustify.Right
795 def _parse_text_elem(self, element, style, group, parent_style, font_style):
798 if "x" in element.attrib
or "y" in element.attrib:
800 float(element.attrib[
"x"]),
801 float(element.attrib[
"y"]),
804 childpos =
NVector(0, font_style.position.y)
807 fs = font.FontShape(element.text, font_style)
810 childpos.x = fs.wrapped.next_x
812 for child
in element:
813 if child.tag == self.
qualifiedqualified(
"svg",
"tspan"):
814 child_style = font_style.clone()
815 child_style.position = childpos.clone()
816 fs = self.
_parseshape_text_parseshape_text(child, group, parent_style, child_style)
817 childpos.x = fs.next_x
819 child_style = font_style.clone()
820 child_style.position = childpos.clone()
821 fs = font.FontShape(child.tail, child_style)
824 childpos.x = fs.wrapped.next_x
826 group.next_x = childpos.x
828 def _parseshape_text(self, element, shape_parent, parent_style, font_style=None):
831 style = self.
parse_styleparse_style(element, parent_style)
834 group.name = self.
_get_id_get_id(element)
837 if font_style
is None:
838 font_style = font.FontStyle(
"", 64)
839 self.
_parse_text_elem_parse_text_elem(element, style, group, style, font_style)
841 style.setdefault(
"fill",
"none")
862 shape_parent.shapes.insert(0, group)
868 for child
in element:
869 if self.
unqualifiedunqualified(child.tag) ==
"animate":
870 att = child.attrib[
"attributeName"]
872 from_val = child.attrib[
"from"]
877 from_val =
float(from_val)
878 if "to" in child.attrib:
879 to_val =
float(child.attrib[
"to"])
880 elif "by" in child.attrib:
881 to_val =
float(child.attrib[
"by"]) + from_val
884 if "dur" in child.attrib:
886 elif "end" in child.attrib:
891 if att
not in animations:
893 animations[att][begin] = from_val
894 animations[att][end] = to_val
899 handler = getattr(self,
"_parseshape_anim_" + tag,
None)
901 handler(lottie, element, animations)
905 @see https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#Clock-value
913 for elem
in reversed(value.split(
":")):
914 seconds +=
float(elem) * mult
916 elif value.endswith(
"s"):
917 seconds =
float(value[:-1])
918 elif value.endswith(
"ms"):
919 seconds =
float(value[:-2]) / 1000
920 elif value.endswith(
"min"):
921 seconds =
float(value[:-3]) * 60
922 elif value.endswith(
"h"):
923 seconds =
float(value[:-1]) * 60 * 60
925 seconds =
float(value)
926 return seconds * self.
animationanimation.frame_rate
931 def _merge_animations(self, element, animations, val1, val2, dest, merge=NVector):
932 if val1
not in animations
and val2
not in animations:
935 dict1 = list(sorted(animations.pop(val1, {}).items()))
936 dict2 = list(sorted(animations.pop(val2, {}).items()))
938 x =
float(element.attrib[val1])
939 y =
float(element.attrib[val2])
941 while dict1
or dict2:
942 if not dict1
or (dict2
and dict1[0][0] > dict2[0][0]):
944 elif not dict2
or dict1[0][0] < dict2[0][0]:
950 values[t] = merge(x, y)
952 animations[dest] = values
954 def _apply_animations(self, animatable, name, animations, transform=lambda v: v):
955 if name
in animations:
956 for t, v
in animations[name].items():
957 animatable.add_keyframe(t, transform(v))
961 _re = re.compile(
"|".join((
963 r"[-+]?[0-9]*\.?[0-9]*[eE][-+]?[0-9]+",
964 r"[-+]?[0-9]*\.?[0-9]+",
984 self.
lala = self.
tokenstokens.pop(0)
985 if isinstance(self.
lala, str):
1005 while self.
lala
is not None:
1007 parser =
"_parse_" + self.
lala
1009 getattr(self, parser)()
1011 parser =
"_parse_" + self.
implicitimplicit
1012 getattr(self, parser)()
1014 def _push_path(self):
1016 self.
add_padd_p =
True
1024 if not self.
add_padd_p:
1034 if not self.
add_padd_p:
1038 def _rpoint(self, point, rel=None):
1039 return (point - (rel
or self.
pp))
if point
is not None else NVector(0, 0)
1041 def _do_add_p(self, outp=None):
1045 self.
add_padd_p =
False
1047 rp = self.
pathpath.vertices[-1]
1048 self.
pathpath.out_tangents[-1] = self.
_rpoint_rpoint(outp, rp)
1075 self.
pp[0] = self.
lala
1085 self.
pp[0] += self.
lala
1095 self.
pp[1] = self.
lala
1105 self.
pp[1] += self.
lala
1118 self.
pathpath.add_point(
1130 pout = self.
pp + self.
cur_veccur_vec()
1134 self.
pathpath.add_point(
1148 handle = self.
pathpath.in_tangents[-1]
1149 self.
pathpath.out_tangents[-1] = (-handle)
1151 self.
pathpath.add_point(
1163 pin = self.
cur_veccur_vec() + self.
pp
1165 handle = self.
pathpath.in_tangents[-1]
1166 self.
pathpath.out_tangents[-1] = (-handle)
1168 self.
pathpath.add_point(
1183 self.
pathpath.add_point(
1196 pin = self.
pp + self.
cur_veccur_vec()
1198 self.
pathpath.add_point(
1211 handle = self.
pp - self.
pathpath.in_tangents[-1]
1213 self.
pathpath.add_point(
1226 handle = -self.
pathpath.in_tangents[-1] + self.
pp
1228 self.
pathpath.add_point(
1245 self.
_do_arc_do_arc(r[0], r[1], xrot, large, sweep, dest)
1249 def _do_arc(self, rx, ry, xrot, large, sweep, dest):
1254 if rx == 0
or ry == 0:
1257 self.
pathpath.add_point(
1264 ellipse, theta1, deltatheta = Ellipse.from_svg_arc(self.
pp, rx, ry, xrot, large, sweep, dest)
1265 points = ellipse.to_bezier(theta1, deltatheta)
1268 self.
pathpath.out_tangents[-1] = points[0].out_tangent
1269 for point
in points[1:-1]:
1270 self.
pathpath.add_point(
1275 self.
pathpath.add_point(
1277 points[-1].in_tangent,
1290 dest = self.
pp + self.
next_vecnext_vec()
1291 self.
_do_arc_do_arc(r[0], r[1], xrot, large, sweep, dest)
1296 if self.
pathpath.vertices:
1297 self.
pp = self.
pathpath.vertices[0].clone()
1298 self.
pathpath.close()
1307 return parser.parse_etree(etree, layer_frames, *args, **kwargs)
1311 return parse_svg_etree(ElementTree.parse(file), layer_frames, *args, **kwargs)
Top level object, describing the animation.
Layer containing ShapeElement objects.
ShapeElement that can contain other shapes.
A simple rectangle shape.
def unqualified(self, name)
def qualified(self, ns, name)
def _do_arc(self, rx, ry, xrot, large, sweep, dest)
def d_subsplit(self, tok)
def __init__(self, d_string)
def _rpoint(self, point, rel=None)
def _do_add_p(self, outp=None)
def insert(self, dummy, shape)
def __setitem__(self, key, value)
def __contains__(self, key)
def __getitem__(self, key)
def to_value(self, bbox, default=None)
def __init__(self, name, comp, value, percent)
def parse(self, attr, default_percent)
def to_lottie(self, gradient_shape, shape, time=0)
def parse_attrs(self, attrib)
def add_coord(self, value)
def add_color(self, offset, color)
def to_lottie(self, gradient_shape, shape, time=0)
def parse_children(self, element, shape_parent, parent_style)
def _svg_to_layer(self, animation, svg)
def _get_id(self, element)
def parse_style(self, element, parent_style)
def apply_common_style(self, style, transform)
def add_shapes(self, element, shapes, shape_parent, parent_style)
def get_color_url(self, color, gradientclass, shape)
def parse_animations(self, lottie, element)
def _parse_viewbox(self, attrib)
def _merge_animations(self, element, animations, val1, val2, dest, merge=NVector)
def _fix_viewbox(self, svg, layers)
def _handle_poly(self, element)
def _apply_animations(self, animatable, name, animations, transform=lambda v:v)
def _parseshape_text(self, element, shape_parent, parent_style, font_style=None)
def _get_name(self, element, inkscapequal)
def __init__(self, name_mode=NameMode.Inkscape)
def _parse_unit(self, value)
def apply_visibility(self, style, object)
def _gradient(self, element, grad)
def parse_shape(self, element, shape_parent, parent_style)
def _add_style_shapes(self, style, group)
def _apply_transform_element_to_matrix(self, matrix, t)
def parse_transform(self, element, group, dest_trans)
def parse_etree(self, etree, layer_frames=0, *args, **kwargs)
def _transform_to_matrix(self, transform)
def parse_color(self, color)
def _parse_text_elem(self, element, style, group, parent_style, font_style)
def etree_to_layer(self, animation, etree)
def parse_animation_time(self, value)
def _parseshape_g(self, element, shape_parent, parent_style)
def _parse_text_style(self, style, font_style=None)
def to_lottie(self, gradient_shape, shape, time=0)
def parse_svg_file(file, layer_frames=0, *args, **kwargs)
def parse_svg_etree(etree, layer_frames=0, *args, **kwargs)
def parse_color(color, current_color=Color(0, 0, 0, 1))
Parses CSS colors.