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)
137 @see https://www.w3.org/wiki/CSS/Properties/color
140 if re.match(
r"^#[0-9a-fA-F]{6}$", color):
141 return Color(
int(color[1:3], 16) / 0xff,
int(color[3:5], 16) / 0xff,
int(color[5:7], 16) / 0xff, 1)
143 if re.match(
r"^#[0-9a-fA-F]{3}$", color):
144 return Color(
int(color[1], 16) / 0xf,
int(color[2], 16) / 0xf,
int(color[3], 16) / 0xf, 1)
146 if re.match(
r"^#[0-9a-fA-F]{8}$", color):
148 int(color[1:3], 16) / 0xff,
149 int(color[3:5], 16) / 0xff,
150 int(color[5:7], 16) / 0xff,
151 int(color[7:9], 16) / 0xff
154 if re.match(
r"^#[0-9a-fA-F]{4}$", color):
155 return Color(
int(color[1], 16) / 0xf,
int(color[2], 16) / 0xf,
int(color[3], 16) / 0xf,
int(color[4], 16) / 0xf)
157 match = re.match(
r"^rgba\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.eE]+)\s*\)$", color)
161 match = re.match(
r"^rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)$", color)
163 return Color(
int(match[1])/255,
int(match[2])/255,
int(match[3])/255, 1)
165 match = re.match(
r"^rgb\s*\(\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*\)$", color)
169 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)
173 if color ==
"transparent":
174 return Color(0, 0, 0, 0)
176 match = re.match(
r"^hsl\s*\(\s*([0-9.eE]+)\s*,\s*([0-9.eE]+)%\s*,\s*([0-9.eE]+)%\s*\)$", color)
178 return Color(*(colorsys.hls_to_rgb(
float(match[1])/360,
float(match[3])/100,
float(match[2])/100) + (1,)))
180 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)
184 if color
in {
"currentColor",
"inherit"}:
185 return current_color.clone()
187 return Color(*color_table[color])
195 self.
itemsitems[shape.name] = shape
198 return self.
itemsitems[key]
201 self.
itemsitems[key] = value
204 return key
in self.
itemsitems
221 def _get_name(self, element, inkscapequal):
222 if self.
name_modename_mode == NameMode.Inkscape:
223 return element.attrib.get(inkscapequal, element.attrib.get(
"id"))
224 return self.
_get_id_get_id(element)
226 def _get_id(self, element):
227 if self.
name_modename_mode != NameMode.NoName:
228 return element.attrib.get(
"id")
231 def _parse_viewbox(self, attrib):
232 return map(float, attrib.replace(
",",
" ").split())
240 svg = etree.getroot()
244 if "width" in svg.attrib
and "height" in svg.attrib:
245 animation.width =
int(round(self.
_parse_unit_parse_unit(svg.attrib[
"width"])))
246 animation.height =
int(round(self.
_parse_unit_parse_unit(svg.attrib[
"height"])))
248 _, _, animation.width, animation.height = self.
_parse_viewbox_parse_viewbox(svg.attrib[
"viewBox"])
249 animation.name = self.
_get_name_get_name(svg, self.
qualifiedqualified(
"sodipodi",
"docname"))
255 layer.in_point = self.
max_timemax_time
256 animation.add_layer(layer)
258 self.
max_timemax_time += layer_frames
259 layer.out_point = self.
max_timemax_time
264 animation.out_point = self.
max_timemax_time
266 self.
_fix_viewbox_fix_viewbox(svg, (layer
for layer
in animation.layers
if not layer.parent_index))
271 svg = etree.getroot()
277 def _get_dpi(self, svg):
278 self.
dpidpi =
float(svg.attrib.get(self.
qualifiedqualified(
"inkscape",
"export-xdpi"), self.
dpidpi))
280 def _svg_to_layer(self, animation, svg):
283 animation.add_layer(layer)
287 sublayer.out_point = self.
max_timemax_time
290 def _fix_viewbox(self, svg, layers):
291 if "viewBox" in svg.attrib:
292 vbx, vby, vbw, vbh = self.
_parse_viewbox_parse_viewbox(svg.attrib[
"viewBox"])
293 if vbx != 0
or vby != 0
or vbw != self.
animationanimation.width
or vbh != self.
animationanimation.height:
295 layer.transform.position.value = -
NVector(vbx, vby)
298 def _parse_unit(self, value):
299 if not isinstance(value, str):
304 if value.endswith(
"px"):
306 elif value.endswith(
"vw"):
308 mult = self.
animationanimation.width * 0.01
309 elif value.endswith(
"vh"):
311 mult = self.
animationanimation.height * 0.01
312 elif value.endswith(
"vmin"):
315 elif value.endswith(
"vmax"):
318 elif value.endswith(
"in"):
321 elif value.endswith(
"pc"):
323 mult = self.
dpidpi / 6
324 elif value.endswith(
"pt"):
326 mult = self.
dpidpi / 72
327 elif value.endswith(
"cm"):
329 mult = self.
dpidpi / cmin
330 elif value.endswith(
"mm"):
332 mult = self.
dpidpi / cmin / 10
333 elif value.endswith(
"Q"):
335 mult = self.
dpidpi / cmin / 40
337 return float(value) * mult
343 bb = group.bounding_box()
345 itcx = self.
qualifiedqualified(
"inkscape",
"transform-center-x")
346 if itcx
in element.attrib:
347 cx =
float(element.attrib[itcx])
348 cy =
float(element.attrib[self.
qualifiedqualified(
"inkscape",
"transform-center-y")])
349 bbx, bby = bb.center()
352 dest_trans.anchor_point.value =
NVector(cx, cy)
353 dest_trans.position.value =
NVector(cx, cy)
359 if "transform" not in element.attrib:
365 for t
in re.finditer(
r"([a-zA-Z]+)\s*\(([^\)]*)\)", element.attrib[
"transform"]):
367 params = list(map(float, t[2].strip().replace(
",",
" ").split()))
368 if name ==
"translate":
369 dest_trans.position.value +=
NVector(
371 (params[1]
if len(params) > 1
else 0),
373 elif name ==
"scale":
375 dest_trans.scale.value[0] = (dest_trans.scale.value[0] / 100 * xfac) * 100
376 yfac = params[1]
if len(params) > 1
else xfac
377 dest_trans.scale.value[1] = (dest_trans.scale.value[1] / 100 * yfac) * 100
378 elif name ==
"rotate":
385 dap = ap - dest_trans.position.value
386 dest_trans.position.value += dap
387 dest_trans.anchor_point.value += dap
388 dest_trans.rotation.value = ang
394 dest_trans.position.value -= dest_trans.anchor_point.value
395 dest_trans.anchor_point.value =
NVector(0, 0)
396 trans = matrix.extract_transform()
397 dest_trans.skew_axis.value = math.degrees(trans[
"skew_axis"])
398 dest_trans.skew.value = -math.degrees(trans[
"skew_angle"])
399 dest_trans.position.value += trans[
"translation"]
400 dest_trans.rotation.value -= math.degrees(trans[
"angle"])
401 dest_trans.scale.value *= trans[
"scale"]
404 style = parent_style.copy()
405 for att
in css_atrrs & set(element.attrib.keys()):
406 if att
in element.attrib:
407 style[att] = element.attrib[att]
408 if "style" in element.attrib:
409 style.update(**dict(map(
410 lambda x: map(
lambda y: y.strip(), x.split(
":")),
411 filter(bool, element.attrib[
"style"].split(
";"))
416 opacity =
float(style.get(
"opacity", 1))
417 transform.opacity.value = opacity * 100
420 if style.get(
"display",
"inline") ==
"none" or style.get(
"visibility",
"visible") ==
"hidden":
423 def add_shapes(self, element, shapes, shape_parent, parent_style):
424 style = self.
parse_styleparse_style(element, parent_style)
429 group.name = self.
_get_name_get_name(element, self.
qualifiedqualified(
"inkscape",
"label"))
431 shape_parent.shapes.insert(0, group)
433 group.add_shape(shape)
441 def _add_style_shapes(self, style, group):
442 stroke_color = style.get(
"stroke",
"none")
443 if stroke_color
not in nocolor:
444 if stroke_color.startswith(
"url"):
450 stroke.color.value = color
452 group.add_shape(stroke)
454 stroke.opacity.value = opacity *
float(style.get(
"stroke-opacity", 1)) * 100
456 stroke.width.value = self.
_parse_unit_parse_unit(style.get(
"stroke-width", 1))
458 linecap = style.get(
"stroke-linecap")
459 if linecap ==
"round":
460 stroke.line_cap = objects.shapes.LineCap.Round
461 elif linecap ==
"butt":
462 stroke.line_cap = objects.shapes.LineCap.Butt
463 elif linecap ==
"square":
464 stroke.line_cap = objects.shapes.LineCap.Square
466 linejoin = style.get(
"stroke-linejoin")
467 if linejoin ==
"round":
468 stroke.line_join = objects.shapes.LineJoin.Round
469 elif linejoin ==
"bevel":
470 stroke.line_join = objects.shapes.LineJoin.Bevel
471 elif linejoin
in {
"miter",
"arcs",
"miter-clip"}:
472 stroke.line_join = objects.shapes.LineJoin.Miter
474 stroke.miter_limit = self.
_parse_unit_parse_unit(style.get(
"stroke-miterlimit", 0))
476 dash_array = style.get(
"stroke-dasharray")
477 if dash_array
and dash_array !=
"none":
478 values = list(map(self.
_parse_unit_parse_unit, dash_array.replace(
",",
" ").split()))
483 for i
in range(0, len(values), 2):
487 fill_color = style.get(
"fill",
"inherit")
488 if fill_color
not in nocolor:
489 if fill_color.startswith(
"url"):
496 opacity *=
float(style.get(
"fill-opacity", 1))
497 fill.opacity.value = opacity * 100
499 if style.get(
"fill-rule",
"") ==
"evenodd":
500 fill.fill_rule = objects.FillRule.EvenOdd
502 group.add_shape(fill)
504 def _parseshape_use(self, element, shape_parent, parent_style):
505 link = element.attrib.get(self.
qualifiedqualified(
"xlink",
"href"))
506 if link
and link.startswith(
"#"):
508 base_element = self.
documentdocument.find(
".//*[@id='%s']" % id)
509 use_style = self.
parse_styleparse_style(element, parent_style)
511 shape_parent.add_shape(used)
513 used.transform.position.value.x =
float(element.attrib.get(
"x", 0))
514 used.transform.position.value.y =
float(element.attrib.get(
"y", 0))
516 self.
parse_shapeparse_shape(base_element, used, use_style)
519 def _parseshape_g(self, element, shape_parent, parent_style):
521 shape_parent.shapes.insert(0, group)
522 style = self.
parse_styleparse_style(element, parent_style)
525 group.name = self.
_get_name_get_name(element, self.
qualifiedqualified(
"inkscape",
"label"))
529 group.transform.opacity.value = 0
532 def _parseshape_ellipse(self, element, shape_parent, parent_style):
534 ellipse.position.value =
NVector(
539 self.
_parse_unit_parse_unit(element.attrib[
"rx"]) * 2,
540 self.
_parse_unit_parse_unit(element.attrib[
"ry"]) * 2
542 self.
add_shapesadd_shapes(element, [ellipse], shape_parent, parent_style)
545 def _parseshape_anim_ellipse(self, ellipse, element, animations):
546 self.
_merge_animations_merge_animations(element, animations,
"cx",
"cy",
"position")
548 self.
_apply_animations_apply_animations(ellipse.position,
"position", animations)
551 def _parseshape_circle(self, element, shape_parent, parent_style):
553 ellipse.position.value =
NVector(
557 r = self.
_parse_unit_parse_unit(element.attrib[
"r"]) * 2
558 ellipse.size.value =
NVector(r, r)
559 self.
add_shapesadd_shapes(element, [ellipse], shape_parent, parent_style)
562 def _parseshape_anim_circle(self, ellipse, element, animations):
563 self.
_merge_animations_merge_animations(element, animations,
"cx",
"cy",
"position")
564 self.
_apply_animations_apply_animations(ellipse.position,
"position", animations)
567 def _parseshape_rect(self, element, shape_parent, parent_style):
569 w = self.
_parse_unit_parse_unit(element.attrib.get(
"width", 0))
570 h = self.
_parse_unit_parse_unit(element.attrib.get(
"height", 0))
572 self.
_parse_unit_parse_unit(element.attrib.get(
"x", 0)) + w / 2,
573 self.
_parse_unit_parse_unit(element.attrib.get(
"y", 0)) + h / 2
575 rect.size.value =
NVector(w, h)
576 rx = self.
_parse_unit_parse_unit(element.attrib.get(
"rx", 0))
577 ry = self.
_parse_unit_parse_unit(element.attrib.get(
"ry", 0))
578 rect.rounded.value = (rx + ry) / 2
579 self.
add_shapesadd_shapes(element, [rect], shape_parent, parent_style)
582 def _parseshape_anim_rect(self, rect, element, animations):
585 self.
_merge_animations_merge_animations(element, animations,
"x",
"y",
"position")
586 self.
_merge_animations_merge_animations(element, animations,
"position",
"size",
"position",
lambda p, s: p + s / 2)
588 self.
_merge_animations_merge_animations(element, animations,
"rx",
"ry",
"rounded",
lambda x, y: (x + y) / 2)
591 def _parseshape_line(self, element, shape_parent, parent_style):
593 line.shape.value.add_point(
NVector(
597 line.shape.value.add_point(
NVector(
601 return self.
add_shapesadd_shapes(element, [line], shape_parent, parent_style)
603 def _parseshape_anim_line(self, group, element, animations):
604 line = group.shapes[0]
615 self.
_merge_animations_merge_animations(element, animations,
"p1",
"p2",
"points", combine)
618 def _handle_poly(self, element):
620 coords = list(map(float, element.attrib[
"points"].replace(
",",
" ").split()))
621 for i
in range(0, len(coords), 2):
622 line.shape.value.add_point(
NVector(*coords[i:i+2]))
625 def _parseshape_polyline(self, element, shape_parent, parent_style):
627 return self.
add_shapesadd_shapes(element, [line], shape_parent, parent_style)
629 def _parseshape_polygon(self, element, shape_parent, parent_style):
631 line.shape.value.close()
632 return self.
add_shapesadd_shapes(element, [line], shape_parent, parent_style)
634 def _parseshape_path(self, element, shape_parent, parent_style):
635 d_parser =
PathDParser(element.attrib.get(
"d",
""))
638 for path
in d_parser.paths:
644 return self.
add_shapesadd_shapes(element, paths, shape_parent, parent_style)
647 for child
in element:
649 if not self.
parse_shapeparse_shape(child, shape_parent, parent_style):
650 handler = getattr(self,
"_parse_" + tag,
None)
655 handler = getattr(self,
"_parseshape_" + self.
unqualifiedunqualified(element.tag),
None)
657 out = handler(element, shape_parent, parent_style)
659 if element.attrib.get(
"id"):
660 self.
defsdefs.items[element.attrib[
"id"]] = out
664 def _parse_defs(self, element):
667 def _apply_transform_element_to_matrix(self, matrix, t):
669 params = list(map(float, t[2].strip().replace(
",",
" ").split()))
670 if name ==
"translate":
673 (params[1]
if len(params) > 1
else 0),
675 elif name ==
"scale":
677 yfac = params[1]
if len(params) > 1
else xfac
678 matrix.scale(xfac, yfac)
679 elif name ==
"rotate":
685 matrix.translate(-x, -y)
686 matrix.rotate(math.radians(ang))
687 matrix.translate(x, y)
689 matrix.rotate(math.radians(ang))
690 elif name ==
"skewX":
691 matrix.skew(math.radians(params[0]), 0)
692 elif name ==
"skewY":
693 matrix.skew(0, math.radians(params[0]))
694 elif name ==
"matrix":
696 m.a, m.b, m.c, m.d, m.tx, m.ty = params
699 def _transform_to_matrix(self, transform):
702 for t
in re.finditer(
r"([a-zA-Z]+)\s*\(([^\)]*)\)", transform):
707 def _gradient(self, element, grad):
708 grad.matrix = self.
_transform_to_matrix_transform_to_matrix(element.attrib.get(
"gradientTransform",
""))
710 id = element.attrib[
"id"]
712 grad.colors = self.
gradientsgradients[id].colors
713 grad.parse_attrs(element.attrib)
714 href = element.attrib.get(self.
qualifiedqualified(
"xlink",
"href"))
716 srcid = href.strip(
"#")
720 src = grad.__class__()
722 grad.colors = src.colors
724 for stop
in element.findall(
"./%s" % self.
qualifiedqualified(
"svg",
"stop")):
725 offset = stop.attrib.get(
"offset",
"0")
726 off =
float(offset.strip(
"%"))
727 if offset.endswith(
"%"):
730 color = self.
parse_colorparse_color(style.get(
"stop-color",
"black"))
731 if "stop-opacity" in style:
732 color[3] =
float(style[
"stop-opacity"])
733 grad.add_color(off, color)
736 def _parse_linearGradient(self, element):
739 def _parse_radialGradient(self, element):
743 match = re.match(
r"""url\(['"]?#([^)'"]+)['"]?\)""", color)
750 outgrad = gradientclass()
751 grad.to_lottie(outgrad, shape)
752 if self.
name_modename_mode != NameMode.NoName:
757 def _parse_text_style(self, style, font_style=None):
758 if "font-family" in style:
759 font_style.query.family(style[
"font-family"].strip(
"'\""))
761 if "font-style" in style:
762 if style[
"font-style"] ==
"oblique":
763 font_style.query.custom(
"slant", 110)
764 elif style[
"font-style"] ==
"italic":
765 font_style.query.custom(
"slant", 100)
767 if "font-weight" in style:
768 if style[
"font-weight"]
in {
"bold",
"bolder"}:
769 font_style.query.weight(200)
770 elif style[
"font-weight"] ==
"lighter":
771 font_style.query.weight(50)
772 elif style[
"font-weight"].isdigit():
773 font_style.query.css_weight(
int(style[
"font-weight"]))
775 if "font-size" in style:
776 fz = style[
"font-size"]
787 font_style.size = fz_names[fz]
788 elif fz ==
"smaller":
792 elif fz.endswith(
"px"):
793 font_style.size =
float(fz[:-2])
795 font_style.size =
float(fz)
797 if "text-align" in style:
798 ta = style[
"text-align"]
799 if ta
in (
"left",
"start"):
800 font_style.justify = font.TextJustify.Left
802 font_style.justify = font.TextJustify.Center
803 elif ta
in (
"right",
"end"):
804 font_style.justify = font.TextJustify.Right
806 def _parse_text_elem(self, element, style, group, parent_style, font_style):
809 if "x" in element.attrib
or "y" in element.attrib:
811 float(element.attrib[
"x"]),
812 float(element.attrib[
"y"]),
815 childpos =
NVector(0, font_style.position.y)
818 fs = font.FontShape(element.text, font_style)
821 childpos.x = fs.wrapped.next_x
823 for child
in element:
824 if child.tag == self.
qualifiedqualified(
"svg",
"tspan"):
825 child_style = font_style.clone()
826 child_style.position = childpos.clone()
827 fs = self.
_parseshape_text_parseshape_text(child, group, parent_style, child_style)
828 childpos.x = fs.next_x
830 child_style = font_style.clone()
831 child_style.position = childpos.clone()
832 fs = font.FontShape(child.tail, child_style)
835 childpos.x = fs.wrapped.next_x
837 group.next_x = childpos.x
839 def _parseshape_text(self, element, shape_parent, parent_style, font_style=None):
842 style = self.
parse_styleparse_style(element, parent_style)
845 group.name = self.
_get_id_get_id(element)
848 if font_style
is None:
849 font_style = font.FontStyle(
"", 64)
850 self.
_parse_text_elem_parse_text_elem(element, style, group, style, font_style)
852 style.setdefault(
"fill",
"none")
873 shape_parent.shapes.insert(0, group)
879 for child
in element:
880 if self.
unqualifiedunqualified(child.tag) ==
"animate":
881 att = child.attrib[
"attributeName"]
883 from_val = child.attrib[
"from"]
888 from_val =
float(from_val)
889 if "to" in child.attrib:
890 to_val =
float(child.attrib[
"to"])
891 elif "by" in child.attrib:
892 to_val =
float(child.attrib[
"by"]) + from_val
895 if "dur" in child.attrib:
897 elif "end" in child.attrib:
902 if att
not in animations:
904 animations[att][begin] = from_val
905 animations[att][end] = to_val
910 handler = getattr(self,
"_parseshape_anim_" + tag,
None)
912 handler(lottie, element, animations)
916 @see https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#Clock-value
924 for elem
in reversed(value.split(
":")):
925 seconds +=
float(elem) * mult
927 elif value.endswith(
"s"):
928 seconds =
float(value[:-1])
929 elif value.endswith(
"ms"):
930 seconds =
float(value[:-2]) / 1000
931 elif value.endswith(
"min"):
932 seconds =
float(value[:-3]) * 60
933 elif value.endswith(
"h"):
934 seconds =
float(value[:-1]) * 60 * 60
936 seconds =
float(value)
937 return seconds * self.
animationanimation.frame_rate
942 def _merge_animations(self, element, animations, val1, val2, dest, merge=NVector):
943 if val1
not in animations
and val2
not in animations:
946 dict1 = list(sorted(animations.pop(val1, {}).items()))
947 dict2 = list(sorted(animations.pop(val2, {}).items()))
949 x =
float(element.attrib[val1])
950 y =
float(element.attrib[val2])
952 while dict1
or dict2:
953 if not dict1
or (dict2
and dict1[0][0] > dict2[0][0]):
955 elif not dict2
or dict1[0][0] < dict2[0][0]:
961 values[t] = merge(x, y)
963 animations[dest] = values
965 def _apply_animations(self, animatable, name, animations, transform=lambda v: v):
966 if name
in animations:
967 for t, v
in animations[name].items():
968 animatable.add_keyframe(t, transform(v))
972 _re = re.compile(
"|".join((
974 r"[-+]?[0-9]*\.?[0-9]*[eE][-+]?[0-9]+",
975 r"[-+]?[0-9]*\.?[0-9]+",
995 self.
lala = self.
tokenstokens.pop(0)
996 if isinstance(self.
lala, str):
1016 while self.
lala
is not None:
1018 parser =
"_parse_" + self.
lala
1020 getattr(self, parser)()
1022 parser =
"_parse_" + self.
implicitimplicit
1023 getattr(self, parser)()
1025 def _push_path(self):
1027 self.
add_padd_p =
True
1035 if not self.
add_padd_p:
1045 if not self.
add_padd_p:
1049 def _rpoint(self, point, rel=None):
1050 return (point - (rel
or self.
pp))
if point
is not None else NVector(0, 0)
1052 def _do_add_p(self, outp=None):
1056 self.
add_padd_p =
False
1058 rp = self.
pathpath.vertices[-1]
1059 self.
pathpath.out_tangents[-1] = self.
_rpoint_rpoint(outp, rp)
1086 self.
pp[0] = self.
lala
1096 self.
pp[0] += self.
lala
1106 self.
pp[1] = self.
lala
1116 self.
pp[1] += self.
lala
1129 self.
pathpath.add_point(
1141 pout = self.
pp + self.
cur_veccur_vec()
1145 self.
pathpath.add_point(
1159 handle = self.
pathpath.in_tangents[-1]
1160 self.
pathpath.out_tangents[-1] = (-handle)
1162 self.
pathpath.add_point(
1174 pin = self.
cur_veccur_vec() + self.
pp
1176 handle = self.
pathpath.in_tangents[-1]
1177 self.
pathpath.out_tangents[-1] = (-handle)
1179 self.
pathpath.add_point(
1194 self.
pathpath.add_point(
1207 pin = self.
pp + self.
cur_veccur_vec()
1209 self.
pathpath.add_point(
1222 handle = self.
pp - self.
pathpath.in_tangents[-1]
1224 self.
pathpath.add_point(
1237 handle = -self.
pathpath.in_tangents[-1] + self.
pp
1239 self.
pathpath.add_point(
1256 self.
_do_arc_do_arc(r[0], r[1], xrot, large, sweep, dest)
1260 def _do_arc(self, rx, ry, xrot, large, sweep, dest):
1265 if rx == 0
or ry == 0:
1268 self.
pathpath.add_point(
1275 ellipse, theta1, deltatheta = Ellipse.from_svg_arc(self.
pp, rx, ry, xrot, large, sweep, dest)
1276 points = ellipse.to_bezier_points(theta1, deltatheta)
1279 self.
pathpath.out_tangents[-1] = points[0].out_tangent
1280 for point
in points[1:-1]:
1281 self.
pathpath.add_point(
1286 self.
pathpath.add_point(
1288 points[-1].in_tangent,
1301 dest = self.
pp + self.
next_vecnext_vec()
1302 self.
_do_arc_do_arc(r[0], r[1], xrot, large, sweep, dest)
1307 if self.
pathpath.vertices:
1308 self.
pp = self.
pathpath.vertices[0].clone()
1309 self.
pathpath.close()
1318 return parser.parse_etree(etree, layer_frames, *args, **kwargs)
1322 return parse_svg_etree(ElementTree.parse(file), layer_frames, *args, **kwargs)
Top level object, describing the animation.
Base class for all layers.
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.