@vvfx/lottie2ir 0.0.1-beta.10 → 0.0.1-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  * Description: Lottie to Animation IR converter
4
4
  * Author: Ant Group CO., Ltd.
5
5
  * Contributors: 澄弈
6
- * Version: v0.0.1-beta.10
6
+ * Version: v0.0.1-beta.12
7
7
  */
8
8
 
9
9
  import { isIRId, IRMaskMode, IRShapePrimitiveType, IRPaintType, IRLineJoin, IRLineCap, IRBlendMode, IREndBehavior, IRMatteMode, ANIMATION_IR_SCHEMA_VERSION, assertIRScene } from '@vvfx/animation-ir';
@@ -230,41 +230,6 @@ function resolveLottieAssetPath(asset) {
230
230
  });
231
231
  }
232
232
 
233
- function resolveDiagnosticAction(code) {
234
- switch(code){
235
- case 'invalid-stretch-ratio':
236
- case 'separated-position-fallback':
237
- case 'animated-anchor-fallback':
238
- case 'animated-gradient-fallback':
239
- case 'missing-font':
240
- return 'approximated';
241
- case 'missing-image-asset':
242
- case 'unsupported-mask':
243
- case 'unsupported-track-matte':
244
- case 'unsupported-shape-feature':
245
- case 'unsupported-shape-type':
246
- case 'missing-text-data':
247
- return 'dropped';
248
- case 'unsupported-layer-type':
249
- return 'unsupported';
250
- }
251
- }
252
- /** Creates a source-scoped warning for a normalized, approximated, or dropped Lottie feature. */ function createLottieDiagnostic(options) {
253
- var _ref;
254
- var _options_featureKinds;
255
- return _extends({
256
- code: "LOTTIE_" + options.code.replaceAll('-', '_').toUpperCase(),
257
- severity: 'warning',
258
- action: resolveDiagnosticAction(options.code),
259
- path: '$.layers'
260
- }, options.layerInd != null && {
261
- sourceId: String(options.layerInd)
262
- }, {
263
- feature: (_ref = (_options_featureKinds = options.featureKinds) == null ? void 0 : _options_featureKinds.join(',')) != null ? _ref : options.code,
264
- message: options.message
265
- });
266
- }
267
-
268
233
  function isFiniteNumber(value) {
269
234
  return typeof value === 'number' && Number.isFinite(value);
270
235
  }
@@ -507,6 +472,257 @@ function mergeSeparatedPosition(pRecord) {
507
472
  };
508
473
  }
509
474
 
475
+ function hasAnimatedAnchor(layer) {
476
+ var property = getPropertyRecord(layer, 'a');
477
+ return property != null && isKeyframedProperty(property);
478
+ }
479
+ function toStaticAnchor(layer) {
480
+ var _ref;
481
+ var _property_keyframes_;
482
+ var property = toAnimatedProperty(getPropertyRecord(layer, 'a'), readVector3);
483
+ if (property == null || property.type === 'static') {
484
+ return property;
485
+ }
486
+ return {
487
+ type: 'static',
488
+ value: (_ref = (_property_keyframes_ = property.keyframes[0]) == null ? void 0 : _property_keyframes_.value) != null ? _ref : [
489
+ 0,
490
+ 0,
491
+ 0
492
+ ]
493
+ };
494
+ }
495
+ function readVector3(value) {
496
+ var _values_, _values_1, _values_2;
497
+ var values = toFiniteNumberArray(value);
498
+ if (values == null) {
499
+ return undefined;
500
+ }
501
+ return [
502
+ (_values_ = values[0]) != null ? _values_ : 0,
503
+ (_values_1 = values[1]) != null ? _values_1 : 0,
504
+ (_values_2 = values[2]) != null ? _values_2 : 0
505
+ ];
506
+ }
507
+ function readSpatialTangent(value) {
508
+ var values = toFiniteNumberArray(value);
509
+ if ((values == null ? void 0 : values.length) === 2) {
510
+ return [
511
+ values[0],
512
+ values[1]
513
+ ];
514
+ }
515
+ if ((values == null ? void 0 : values.length) === 3) {
516
+ return [
517
+ values[0],
518
+ values[1],
519
+ values[2]
520
+ ];
521
+ }
522
+ return undefined;
523
+ }
524
+ function readScaleVector(value) {
525
+ var _values_, _values_1, _values_2;
526
+ var _toFiniteNumberArray;
527
+ var values = (_toFiniteNumberArray = toFiniteNumberArray(value)) == null ? void 0 : _toFiniteNumberArray.map(function(item) {
528
+ return item / 100;
529
+ });
530
+ if (values == null) {
531
+ return undefined;
532
+ }
533
+ return [
534
+ (_values_ = values[0]) != null ? _values_ : 1,
535
+ (_values_1 = values[1]) != null ? _values_1 : 1,
536
+ (_values_2 = values[2]) != null ? _values_2 : 1
537
+ ];
538
+ }
539
+ function readOpacityNumber(value) {
540
+ var valueNumber = toFirstNumber(value);
541
+ return valueNumber != null ? valueNumber / 100 : undefined;
542
+ }
543
+ function getPropertyRecord(layer, key) {
544
+ var ks = asRecord(layer.ks);
545
+ var property = asRecord(ks == null ? void 0 : ks[key]);
546
+ if (property == null || !('k' in property)) {
547
+ return undefined;
548
+ }
549
+ return property;
550
+ }
551
+ function readBezierHandle(value) {
552
+ var record = asRecord(value);
553
+ var x = toFirstNumber(record == null ? void 0 : record['x']);
554
+ var y = toFirstNumber(record == null ? void 0 : record['y']);
555
+ if (x == null || y == null) {
556
+ return undefined;
557
+ }
558
+ return {
559
+ x: x,
560
+ y: y
561
+ };
562
+ }
563
+ function readKeyframes(property, reader) {
564
+ var rawKeyframes = property['k'];
565
+ if (!Array.isArray(rawKeyframes)) {
566
+ return undefined;
567
+ }
568
+ var keyframes = [];
569
+ var previousEndValue;
570
+ // Lottie stores segment tangents on the start keyframe; IR assigns them to their endpoints.
571
+ var pendingInTangent;
572
+ var pendingSpatialIn;
573
+ for(var _iterator = _create_for_of_iterator_helper_loose(rawKeyframes), _step; !(_step = _iterator()).done;){
574
+ var rawKeyframe = _step.value;
575
+ var keyframeRecord = asRecord(rawKeyframe);
576
+ var t = keyframeRecord == null ? void 0 : keyframeRecord['t'];
577
+ if (!isFiniteNumber(t)) {
578
+ continue;
579
+ }
580
+ // Terminal keyframes inherit the preceding `e` value.
581
+ var value = reader(keyframeRecord == null ? void 0 : keyframeRecord['s']);
582
+ if (value == null) {
583
+ if (previousEndValue != null) {
584
+ value = previousEndValue;
585
+ } else {
586
+ continue;
587
+ }
588
+ }
589
+ var endValue = reader(keyframeRecord == null ? void 0 : keyframeRecord['e']);
590
+ previousEndValue = endValue != null ? endValue : value;
591
+ var recordInTangent = readBezierHandle(keyframeRecord == null ? void 0 : keyframeRecord['i']);
592
+ var outTangent = readBezierHandle(keyframeRecord == null ? void 0 : keyframeRecord['o']);
593
+ var spatialOut = readSpatialTangent(keyframeRecord == null ? void 0 : keyframeRecord['to']);
594
+ var recordSpatialIn = readSpatialTangent(keyframeRecord == null ? void 0 : keyframeRecord['ti']);
595
+ var inTangent = pendingInTangent;
596
+ var spatialIn = pendingSpatialIn;
597
+ keyframes.push(_extends({
598
+ frame: t,
599
+ value: value
600
+ }, endValue != null && {
601
+ endValue: endValue
602
+ }, (keyframeRecord == null ? void 0 : keyframeRecord['h']) === 1 && {
603
+ hold: true
604
+ }, inTangent != null && {
605
+ inTangent: inTangent
606
+ }, outTangent != null && {
607
+ outTangent: outTangent
608
+ }, spatialOut != null && {
609
+ spatialOut: spatialOut
610
+ }, spatialIn != null && {
611
+ spatialIn: spatialIn
612
+ }));
613
+ // Incoming tangents belong to the next IR keyframe.
614
+ pendingInTangent = recordInTangent;
615
+ pendingSpatialIn = recordSpatialIn;
616
+ }
617
+ return keyframes.length > 0 ? keyframes : undefined;
618
+ }
619
+ function toAnimatedProperty(property, reader) {
620
+ if (property == null) {
621
+ return undefined;
622
+ }
623
+ if (isKeyframedProperty(property)) {
624
+ var _ref;
625
+ var _keyframes_;
626
+ var keyframes = readKeyframes(property, reader);
627
+ if (keyframes != null && keyframes.length >= 2) {
628
+ return {
629
+ type: 'keyframed',
630
+ keyframes: keyframes
631
+ };
632
+ }
633
+ var staticValue = (_ref = keyframes == null ? void 0 : (_keyframes_ = keyframes[0]) == null ? void 0 : _keyframes_.value) != null ? _ref : reader(property['k']);
634
+ return staticValue != null ? {
635
+ type: 'static',
636
+ value: staticValue
637
+ } : undefined;
638
+ }
639
+ var value = reader(property['k']);
640
+ return value != null ? {
641
+ type: 'static',
642
+ value: value
643
+ } : undefined;
644
+ }
645
+ /**
646
+ * Converts Lottie transform properties and keyframes to IR units and tangent ownership.
647
+ *
648
+ * Scale and opacity percentages become normalized factors. Animated anchors use their first keyframe because IR anchors
649
+ * are static.
650
+ */ function convertLottieTransform(layer) {
651
+ var _getPropertyRecord;
652
+ var transform = {};
653
+ var anchor = toStaticAnchor(layer);
654
+ var positionProperty = getPropertyRecord(layer, 'p');
655
+ var position = toAnimatedProperty(positionProperty, readVector3);
656
+ var scale = toAnimatedProperty(getPropertyRecord(layer, 's'), readScaleVector);
657
+ var rotationZ = toAnimatedProperty((_getPropertyRecord = getPropertyRecord(layer, 'rz')) != null ? _getPropertyRecord : getPropertyRecord(layer, 'r'), toFirstNumber);
658
+ var rotationX = toAnimatedProperty(getPropertyRecord(layer, 'rx'), toFirstNumber);
659
+ var rotationY = toAnimatedProperty(getPropertyRecord(layer, 'ry'), toFirstNumber);
660
+ var opacity = toAnimatedProperty(getPropertyRecord(layer, 'o'), readOpacityNumber);
661
+ if (anchor != null) {
662
+ transform.anchor = anchor;
663
+ }
664
+ if (position != null) {
665
+ transform.position = position;
666
+ }
667
+ if (scale != null) {
668
+ transform.scale = scale;
669
+ }
670
+ if (rotationX != null || rotationY != null || rotationZ != null) {
671
+ var rotation = {};
672
+ if (rotationX != null) {
673
+ rotation.x = rotationX;
674
+ }
675
+ if (rotationY != null) {
676
+ rotation.y = rotationY;
677
+ }
678
+ if (rotationZ != null) {
679
+ rotation.z = rotationZ;
680
+ }
681
+ transform.rotation = rotation;
682
+ }
683
+ if (opacity != null) {
684
+ transform.opacity = opacity;
685
+ }
686
+ return transform;
687
+ }
688
+
689
+ function resolveDiagnosticAction(code) {
690
+ switch(code){
691
+ case 'invalid-stretch-ratio':
692
+ case 'separated-position-fallback':
693
+ case 'animated-anchor-fallback':
694
+ case 'animated-gradient-fallback':
695
+ case 'duplicate-layer-index':
696
+ case 'unsupported-text-glyphs':
697
+ case 'missing-font':
698
+ return 'approximated';
699
+ case 'missing-image-asset':
700
+ case 'unsupported-mask':
701
+ case 'unsupported-track-matte':
702
+ case 'unsupported-shape-feature':
703
+ case 'unsupported-shape-type':
704
+ case 'missing-text-data':
705
+ return 'dropped';
706
+ case 'unsupported-layer-type':
707
+ return 'unsupported';
708
+ }
709
+ }
710
+ /** Creates a source-scoped warning for a normalized, approximated, or dropped Lottie feature. */ function createLottieDiagnostic(options) {
711
+ var _ref;
712
+ var _options_featureKinds;
713
+ return _extends({
714
+ code: "LOTTIE_" + options.code.replaceAll('-', '_').toUpperCase(),
715
+ severity: 'warning',
716
+ action: resolveDiagnosticAction(options.code),
717
+ path: '$.layers'
718
+ }, options.layerInd != null && {
719
+ sourceId: String(options.layerInd)
720
+ }, {
721
+ feature: (_ref = (_options_featureKinds = options.featureKinds) == null ? void 0 : _options_featureKinds.join(',')) != null ? _ref : options.code,
722
+ message: options.message
723
+ });
724
+ }
725
+
510
726
  function hasFullStaticOpacity(mask) {
511
727
  var opacity = mask['o'];
512
728
  if (opacity == null) {
@@ -604,9 +820,10 @@ function collectMaskPathIssues(mask) {
604
820
  'open-mask-path'
605
821
  ];
606
822
  }
607
- /** Converts one supported authored Mask and reports features that prevent conversion. */ function convertLottieMask(maskInfo) {
823
+ /** Converts the ordered authored Mask stack and reports features that prevent conversion. */ function convertLottieMasks(maskInfo) {
608
824
  if (!Array.isArray(maskInfo)) {
609
825
  return {
826
+ masks: [],
610
827
  unsupportedFeatures: [
611
828
  'invalid-mask-data'
612
829
  ]
@@ -620,6 +837,7 @@ function collectMaskPathIssues(mask) {
620
837
  });
621
838
  if (activeMasks.length === 0) {
622
839
  return {
840
+ masks: [],
623
841
  unsupportedFeatures: hasInvalidEntry ? [
624
842
  'invalid-mask-data'
625
843
  ] : []
@@ -629,9 +847,6 @@ function collectMaskPathIssues(mask) {
629
847
  if (hasInvalidEntry) {
630
848
  features.add('invalid-mask-data');
631
849
  }
632
- if (activeMasks.length > 1) {
633
- features.add('multiple-active-masks');
634
- }
635
850
  for(var _iterator = _create_for_of_iterator_helper_loose(activeMasks), _step; !(_step = _iterator()).done;){
636
851
  var mask = _step.value;
637
852
  if (!isSupportedLottieMaskMode(mask['mode'])) {
@@ -646,20 +861,36 @@ function collectMaskPathIssues(mask) {
646
861
  }
647
862
  }
648
863
  var unsupportedFeatures = Array.from(features).sort();
649
- if (unsupportedFeatures.length > 0 || activeMasks.length !== 1) {
864
+ if (unsupportedFeatures.length > 0) {
650
865
  return {
866
+ masks: [],
651
867
  unsupportedFeatures: unsupportedFeatures
652
868
  };
653
869
  }
654
- var mask1 = activeMasks[0];
655
- var pathData = resolveMaskPathData(mask1);
656
- var mode = mapLottieMaskMode(mask1['mode']);
870
+ var masks = [];
871
+ for(var _iterator2 = _create_for_of_iterator_helper_loose(activeMasks), _step2; !(_step2 = _iterator2()).done;){
872
+ var mask1 = _step2.value;
873
+ var converted = convertMaskEntry(mask1);
874
+ if (converted == null) {
875
+ return {
876
+ masks: [],
877
+ unsupportedFeatures: [
878
+ 'invalid-mask-data'
879
+ ]
880
+ };
881
+ }
882
+ masks.push(converted);
883
+ }
884
+ return {
885
+ masks: masks,
886
+ unsupportedFeatures: []
887
+ };
888
+ }
889
+ function convertMaskEntry(mask) {
890
+ var pathData = resolveMaskPathData(mask);
891
+ var mode = mapLottieMaskMode(mask['mode']);
657
892
  if (pathData == null || mode == null) {
658
- return {
659
- unsupportedFeatures: [
660
- 'invalid-mask-data'
661
- ]
662
- };
893
+ return undefined;
663
894
  }
664
895
  var vertices = pathData['v'];
665
896
  var relativeInTangents = pathData['i'];
@@ -688,15 +919,12 @@ function collectMaskPathIssues(mask) {
688
919
  outTangents: outTangents,
689
920
  closed: true
690
921
  };
691
- return {
692
- mask: _extends({
693
- path: path,
694
- mode: mode
695
- }, mask1['inv'] === true && {
696
- inverted: true
697
- }),
698
- unsupportedFeatures: []
699
- };
922
+ return _extends({
923
+ path: path,
924
+ mode: mode
925
+ }, mask['inv'] === true && {
926
+ inverted: true
927
+ });
700
928
  }
701
929
 
702
930
  /** Stable diagnostic labels for unsupported Lottie shape operators. */ var SHAPE_FEATURE_LABELS = {
@@ -1306,18 +1534,195 @@ function buildCustomShapeData(shPaths, fills, strokes, transform) {
1306
1534
  strokes: strokes,
1307
1535
  position: position
1308
1536
  };
1309
- return shapeData;
1310
- }
1311
-
1312
- /** Returns the first Text Document keyframe used for static text conversion. */ function parseLottieTextDocument(layer) {
1313
- var _asRecord;
1314
- if (layer.ty !== 5 || layer.t == null) {
1537
+ return shapeData;
1538
+ }
1539
+
1540
+ /** Returns the first Text Document keyframe used for static text conversion. */ function parseLottieTextDocument(layer) {
1541
+ var _asRecord;
1542
+ if (layer.ty !== 5 || layer.t == null) {
1543
+ return undefined;
1544
+ }
1545
+ var textData = asRecord(layer.t);
1546
+ var documentData = asRecord(textData == null ? void 0 : textData['d']);
1547
+ var keyframes = documentData == null ? void 0 : documentData['k'];
1548
+ return Array.isArray(keyframes) ? asRecord((_asRecord = asRecord(keyframes[0])) == null ? void 0 : _asRecord['s']) : undefined;
1549
+ }
1550
+
1551
+ function isFiniteColor(value) {
1552
+ return Array.isArray(value) && value.every(function(channel) {
1553
+ return typeof channel === 'number' && Number.isFinite(channel);
1554
+ });
1555
+ }
1556
+ function collectOutlinePaths(items) {
1557
+ var paths = [];
1558
+ for(var _iterator = _create_for_of_iterator_helper_loose(items), _step; !(_step = _iterator()).done;){
1559
+ var item = _step.value;
1560
+ if (item.hd === true) {
1561
+ continue;
1562
+ }
1563
+ if (item.ty === 'gr') {
1564
+ var _paths;
1565
+ var _item_it;
1566
+ var nested = collectOutlinePaths((_item_it = item.it) != null ? _item_it : []);
1567
+ if (nested == null) {
1568
+ return undefined;
1569
+ }
1570
+ (_paths = paths).push.apply(_paths, [].concat(nested));
1571
+ } else if (item.ty === 'sh' && item.ks.a !== 1) {
1572
+ paths.push(item);
1573
+ } else {
1574
+ return undefined;
1575
+ }
1576
+ }
1577
+ return paths;
1578
+ }
1579
+ /** Embedded glyph paths use 100-unit font coordinates, independent of the exported font size. */ function convertLottieTextGlyphs(layer, fonts, chars) {
1580
+ var _loop = function() {
1581
+ var _step_value = _step.value, lineIndex = _step_value[0], line = _step_value[1];
1582
+ var glyphs = Array.from(line, function(character) {
1583
+ return chars.find(function(glyph) {
1584
+ return glyph.ch === character && glyph.fFamily === font.fFamily && glyph.style === font.fStyle;
1585
+ });
1586
+ });
1587
+ if (glyphs.some(function(glyph) {
1588
+ return glyph == null;
1589
+ })) {
1590
+ return {
1591
+ v: undefined
1592
+ };
1593
+ }
1594
+ // Trailing spaces advance the pen but do not participate in Lottie's alignment width.
1595
+ var trimmedLength = Array.from(line.replace(/ +$/, '')).length;
1596
+ var width = glyphs.slice(0, trimmedLength).reduce(function(sum, glyph) {
1597
+ var _ref;
1598
+ return sum + ((_ref = glyph == null ? void 0 : glyph.w) != null ? _ref : 0) * scale + tracking;
1599
+ }, 0) - (trimmedLength > 0 ? tracking : 0);
1600
+ var x = document['j'] === 1 ? -width : document['j'] === 2 ? -width / 2 : 0;
1601
+ var y = lineIndex * lineHeight + (lineIndex > 0 ? 1 : 0) - baselineShift;
1602
+ for(var _iterator = _create_for_of_iterator_helper_loose(glyphs), _step1; !(_step1 = _iterator()).done;){
1603
+ var glyph = _step1.value;
1604
+ if (glyph == null) {
1605
+ return {
1606
+ v: undefined
1607
+ };
1608
+ }
1609
+ if ('shapes' in glyph.data) {
1610
+ var _loop = function() {
1611
+ var shape = _step2.value;
1612
+ if (shape.primitiveType !== IRShapePrimitiveType.Custom) {
1613
+ return {
1614
+ v: undefined
1615
+ };
1616
+ }
1617
+ for(var _iterator = _create_for_of_iterator_helper_loose(shape.customPaths), _step; !(_step = _iterator()).done;){
1618
+ var path = _step.value;
1619
+ paths.push({
1620
+ closed: path.closed,
1621
+ points: path.points.map(function(point) {
1622
+ return {
1623
+ x: (point.x + shape.position[0]) * scale + x,
1624
+ y: (point.y + shape.position[1]) * scale + y
1625
+ };
1626
+ }),
1627
+ inTangents: path.inTangents.map(function(point) {
1628
+ return {
1629
+ x: point.x * scale,
1630
+ y: point.y * scale
1631
+ };
1632
+ }),
1633
+ outTangents: path.outTangents.map(function(point) {
1634
+ return {
1635
+ x: point.x * scale,
1636
+ y: point.y * scale
1637
+ };
1638
+ })
1639
+ });
1640
+ }
1641
+ };
1642
+ var outlinePaths = collectOutlinePaths(glyph.data.shapes);
1643
+ if (outlinePaths == null) {
1644
+ return {
1645
+ v: undefined
1646
+ };
1647
+ }
1648
+ var converted = convertLottieShapes([
1649
+ {
1650
+ ty: 'gr',
1651
+ it: outlinePaths
1652
+ },
1653
+ {
1654
+ ty: 'fl',
1655
+ c: {
1656
+ a: 0,
1657
+ k: fillColor
1658
+ },
1659
+ o: {
1660
+ a: 0,
1661
+ k: 100
1662
+ },
1663
+ r: 1
1664
+ }
1665
+ ]);
1666
+ for(var _iterator1 = _create_for_of_iterator_helper_loose(converted.shapes), _step2; !(_step2 = _iterator1()).done;){
1667
+ var _ret = _loop();
1668
+ if (_type_of(_ret) === "object") return {
1669
+ v: _ret.v
1670
+ };
1671
+ }
1672
+ } else if (glyph.ch.trim() !== '') {
1673
+ return {
1674
+ v: undefined
1675
+ };
1676
+ }
1677
+ x += glyph.w * scale + tracking;
1678
+ }
1679
+ };
1680
+ var _color_, _color_1, _color_2;
1681
+ var document = parseLottieTextDocument(layer);
1682
+ var font = fonts.find(function(item) {
1683
+ return item.fName === (document == null ? void 0 : document['f']);
1684
+ });
1685
+ if (document == null || font == null || typeof document['t'] !== 'string' || document['sz'] != null || document['ps'] != null || toFiniteNumber(document['sw'], 0) !== 0) {
1315
1686
  return undefined;
1316
1687
  }
1317
- var textData = asRecord(layer.t);
1318
- var documentData = asRecord(textData == null ? void 0 : textData['d']);
1319
- var keyframes = documentData == null ? void 0 : documentData['k'];
1320
- return Array.isArray(keyframes) ? asRecord((_asRecord = asRecord(keyframes[0])) == null ? void 0 : _asRecord['s']) : undefined;
1688
+ var color = document['fc'];
1689
+ if (!isFiniteColor(color)) {
1690
+ return undefined;
1691
+ }
1692
+ var fillColor = [
1693
+ (_color_ = color[0]) != null ? _color_ : 0,
1694
+ (_color_1 = color[1]) != null ? _color_1 : 0,
1695
+ (_color_2 = color[2]) != null ? _color_2 : 0,
1696
+ 1
1697
+ ];
1698
+ var fontSize = toFiniteNumber(document['s'], 16);
1699
+ var scale = fontSize / 100;
1700
+ var tracking = toFiniteNumber(document['tr'], 0) * fontSize / 1000;
1701
+ var lineHeight = toFiniteNumber(document['lh'], fontSize);
1702
+ var baselineShift = toFiniteNumber(document['ls'], 0);
1703
+ var paths = [];
1704
+ var lines = document['t'].replace(/\r\n?/g, '\n').split('\n');
1705
+ for(var _iterator = _create_for_of_iterator_helper_loose(lines.entries()), _step; !(_step = _iterator()).done;){
1706
+ var _ret = _loop();
1707
+ if (_type_of(_ret) === "object") return _ret.v;
1708
+ }
1709
+ return paths.length === 0 ? [] : [
1710
+ {
1711
+ primitiveType: IRShapePrimitiveType.Custom,
1712
+ customPaths: paths,
1713
+ position: [
1714
+ 0,
1715
+ 0
1716
+ ],
1717
+ fills: [
1718
+ {
1719
+ type: IRPaintType.Solid,
1720
+ color: fillColor
1721
+ }
1722
+ ],
1723
+ strokes: []
1724
+ }
1725
+ ];
1321
1726
  }
1322
1727
 
1323
1728
  var textWeightType = {
@@ -1511,230 +1916,25 @@ function readNamedFontWeight(value) {
1511
1916
  return undefined;
1512
1917
  }
1513
1918
 
1514
- function hasAnimatedAnchor(layer) {
1515
- var property = getPropertyRecord(layer, 'a');
1516
- return property != null && isKeyframedProperty(property);
1517
- }
1518
- function toStaticAnchor(layer) {
1519
- var _ref;
1520
- var _property_keyframes_;
1521
- var property = toAnimatedProperty(getPropertyRecord(layer, 'a'), readVector3);
1522
- if (property == null || property.type === 'static') {
1523
- return property;
1524
- }
1525
- return {
1526
- type: 'static',
1527
- value: (_ref = (_property_keyframes_ = property.keyframes[0]) == null ? void 0 : _property_keyframes_.value) != null ? _ref : [
1528
- 0,
1529
- 0,
1530
- 0
1531
- ]
1532
- };
1533
- }
1534
- function readVector3(value) {
1535
- var _values_, _values_1, _values_2;
1536
- var values = toFiniteNumberArray(value);
1537
- if (values == null) {
1538
- return undefined;
1539
- }
1540
- return [
1541
- (_values_ = values[0]) != null ? _values_ : 0,
1542
- (_values_1 = values[1]) != null ? _values_1 : 0,
1543
- (_values_2 = values[2]) != null ? _values_2 : 0
1544
- ];
1545
- }
1546
- function readSpatialTangent(value) {
1547
- var values = toFiniteNumberArray(value);
1548
- if ((values == null ? void 0 : values.length) === 2) {
1549
- return [
1550
- values[0],
1551
- values[1]
1552
- ];
1553
- }
1554
- if ((values == null ? void 0 : values.length) === 3) {
1555
- return [
1556
- values[0],
1557
- values[1],
1558
- values[2]
1559
- ];
1560
- }
1561
- return undefined;
1562
- }
1563
- function readScaleVector(value) {
1564
- var _values_, _values_1, _values_2;
1565
- var _toFiniteNumberArray;
1566
- var values = (_toFiniteNumberArray = toFiniteNumberArray(value)) == null ? void 0 : _toFiniteNumberArray.map(function(item) {
1567
- return item / 100;
1568
- });
1569
- if (values == null) {
1570
- return undefined;
1571
- }
1572
- return [
1573
- (_values_ = values[0]) != null ? _values_ : 1,
1574
- (_values_1 = values[1]) != null ? _values_1 : 1,
1575
- (_values_2 = values[2]) != null ? _values_2 : 1
1576
- ];
1577
- }
1578
- function readOpacityNumber(value) {
1579
- var valueNumber = toFirstNumber(value);
1580
- return valueNumber != null ? valueNumber / 100 : undefined;
1581
- }
1582
- function getPropertyRecord(layer, key) {
1583
- var ks = asRecord(layer.ks);
1584
- var property = asRecord(ks == null ? void 0 : ks[key]);
1585
- if (property == null || !('k' in property)) {
1586
- return undefined;
1587
- }
1588
- return property;
1589
- }
1590
- function readBezierHandle(value) {
1591
- var record = asRecord(value);
1592
- var x = toFirstNumber(record == null ? void 0 : record['x']);
1593
- var y = toFirstNumber(record == null ? void 0 : record['y']);
1594
- if (x == null || y == null) {
1595
- return undefined;
1596
- }
1597
- return {
1598
- x: x,
1599
- y: y
1600
- };
1601
- }
1602
- function readKeyframes(property, reader) {
1603
- var rawKeyframes = property['k'];
1604
- if (!Array.isArray(rawKeyframes)) {
1605
- return undefined;
1606
- }
1607
- var keyframes = [];
1608
- var previousEndValue;
1609
- // Lottie stores segment tangents on the start keyframe; IR assigns them to their endpoints.
1610
- var pendingInTangent;
1611
- var pendingSpatialIn;
1612
- for(var _iterator = _create_for_of_iterator_helper_loose(rawKeyframes), _step; !(_step = _iterator()).done;){
1613
- var rawKeyframe = _step.value;
1614
- var keyframeRecord = asRecord(rawKeyframe);
1615
- var t = keyframeRecord == null ? void 0 : keyframeRecord['t'];
1616
- if (!isFiniteNumber(t)) {
1617
- continue;
1618
- }
1619
- // Terminal keyframes inherit the preceding `e` value.
1620
- var value = reader(keyframeRecord == null ? void 0 : keyframeRecord['s']);
1621
- if (value == null) {
1622
- if (previousEndValue != null) {
1623
- value = previousEndValue;
1624
- } else {
1625
- continue;
1626
- }
1627
- }
1628
- var endValue = reader(keyframeRecord == null ? void 0 : keyframeRecord['e']);
1629
- previousEndValue = endValue != null ? endValue : value;
1630
- var recordInTangent = readBezierHandle(keyframeRecord == null ? void 0 : keyframeRecord['i']);
1631
- var outTangent = readBezierHandle(keyframeRecord == null ? void 0 : keyframeRecord['o']);
1632
- var spatialOut = readSpatialTangent(keyframeRecord == null ? void 0 : keyframeRecord['to']);
1633
- var recordSpatialIn = readSpatialTangent(keyframeRecord == null ? void 0 : keyframeRecord['ti']);
1634
- var inTangent = pendingInTangent;
1635
- var spatialIn = pendingSpatialIn;
1636
- keyframes.push(_extends({
1637
- frame: t,
1638
- value: value
1639
- }, endValue != null && {
1640
- endValue: endValue
1641
- }, (keyframeRecord == null ? void 0 : keyframeRecord['h']) === 1 && {
1642
- hold: true
1643
- }, inTangent != null && {
1644
- inTangent: inTangent
1645
- }, outTangent != null && {
1646
- outTangent: outTangent
1647
- }, spatialOut != null && {
1648
- spatialOut: spatialOut
1649
- }, spatialIn != null && {
1650
- spatialIn: spatialIn
1651
- }));
1652
- // Incoming tangents belong to the next IR keyframe.
1653
- pendingInTangent = recordInTangent;
1654
- pendingSpatialIn = recordSpatialIn;
1655
- }
1656
- return keyframes.length > 0 ? keyframes : undefined;
1657
- }
1658
- function toAnimatedProperty(property, reader) {
1659
- if (property == null) {
1660
- return undefined;
1661
- }
1662
- if (isKeyframedProperty(property)) {
1663
- var _ref;
1664
- var _keyframes_;
1665
- var keyframes = readKeyframes(property, reader);
1666
- if (keyframes != null && keyframes.length >= 2) {
1667
- return {
1668
- type: 'keyframed',
1669
- keyframes: keyframes
1670
- };
1671
- }
1672
- var staticValue = (_ref = keyframes == null ? void 0 : (_keyframes_ = keyframes[0]) == null ? void 0 : _keyframes_.value) != null ? _ref : reader(property['k']);
1673
- return staticValue != null ? {
1674
- type: 'static',
1675
- value: staticValue
1676
- } : undefined;
1677
- }
1678
- var value = reader(property['k']);
1679
- return value != null ? {
1680
- type: 'static',
1681
- value: value
1682
- } : undefined;
1683
- }
1684
- /**
1685
- * Converts Lottie transform properties and keyframes to IR units and tangent ownership.
1686
- *
1687
- * Scale and opacity percentages become normalized factors. Animated anchors use their first keyframe because IR anchors
1688
- * are static.
1689
- */ function convertLottieTransform(layer) {
1690
- var _getPropertyRecord;
1691
- var transform = {};
1692
- var anchor = toStaticAnchor(layer);
1693
- var positionProperty = getPropertyRecord(layer, 'p');
1694
- var position = toAnimatedProperty(positionProperty, readVector3);
1695
- var scale = toAnimatedProperty(getPropertyRecord(layer, 's'), readScaleVector);
1696
- var rotationZ = toAnimatedProperty((_getPropertyRecord = getPropertyRecord(layer, 'rz')) != null ? _getPropertyRecord : getPropertyRecord(layer, 'r'), toFirstNumber);
1697
- var rotationX = toAnimatedProperty(getPropertyRecord(layer, 'rx'), toFirstNumber);
1698
- var rotationY = toAnimatedProperty(getPropertyRecord(layer, 'ry'), toFirstNumber);
1699
- var opacity = toAnimatedProperty(getPropertyRecord(layer, 'o'), readOpacityNumber);
1700
- if (anchor != null) {
1701
- transform.anchor = anchor;
1702
- }
1703
- if (position != null) {
1704
- transform.position = position;
1705
- }
1706
- if (scale != null) {
1707
- transform.scale = scale;
1708
- }
1709
- if (rotationX != null || rotationY != null || rotationZ != null) {
1710
- var rotation = {};
1711
- if (rotationX != null) {
1712
- rotation.x = rotationX;
1713
- }
1714
- if (rotationY != null) {
1715
- rotation.y = rotationY;
1716
- }
1717
- if (rotationZ != null) {
1718
- rotation.z = rotationZ;
1719
- }
1720
- transform.rotation = rotation;
1721
- }
1722
- if (opacity != null) {
1723
- transform.opacity = opacity;
1724
- }
1725
- return transform;
1726
- }
1727
-
1728
1919
  /**
1729
1920
  * Lowers one supported Lottie Layer after its parent, matte, and precomposition references have been resolved.
1730
1921
  *
1731
1922
  * Fidelity limitations are returned with the converted Layer for Scene-level diagnostic assembly.
1732
1923
  */ function convertLottieLayer(options) {
1733
- var _ref;
1924
+ var _options_chars, _ref;
1734
1925
  var layer = options.layer, asset = options.asset, id = options.id;
1735
1926
  var layerRecord = layer;
1736
- var type = mapLottieLayerTypeToIR(layer);
1927
+ var hasGlyphs = layer.ty === 5 && options.chars != null && options.chars.length > 0;
1928
+ var glyphShapes = hasGlyphs ? convertLottieTextGlyphs(layer, options.fonts, (_options_chars = options.chars) != null ? _options_chars : []) : undefined;
1929
+ var type = glyphShapes != null ? 'shape' : mapLottieLayerTypeToIR(layer);
1737
1930
  var diagnostics = [];
1931
+ if (hasGlyphs && glyphShapes == null) {
1932
+ diagnostics.push(createLottieDiagnostic({
1933
+ code: 'unsupported-text-glyphs',
1934
+ message: "Text layer '" + layer.nm + "' has missing or unsupported glyph outlines; using font-based text instead.",
1935
+ layerInd: layer.ind
1936
+ }));
1937
+ }
1738
1938
  var solidLayerColor = type === 'solid' ? convertSolidColor(layer) : undefined;
1739
1939
  var parentLayerId = options.parentLayerId;
1740
1940
  var shapeLayer = layer.ty === 4 ? layer : undefined;
@@ -1817,7 +2017,7 @@ function toAnimatedProperty(property, reader) {
1817
2017
  content: textConversion.content
1818
2018
  }, type === 'shape' && {
1819
2019
  content: {
1820
- shapes: (_ref = shapeConversion == null ? void 0 : shapeConversion.shapes) != null ? _ref : []
2020
+ shapes: (_ref = glyphShapes != null ? glyphShapes : shapeConversion == null ? void 0 : shapeConversion.shapes) != null ? _ref : []
1821
2021
  }
1822
2022
  }, type === 'precomp' && options.precompCompositionId != null && {
1823
2023
  content: {
@@ -1827,8 +2027,9 @@ function toAnimatedProperty(property, reader) {
1827
2027
  }
1828
2028
  }, {
1829
2029
  transform: transform,
1830
- blendMode: mapLottieBlendMode(layerRecord['bm']),
1831
- mask: maskConversion.mask
2030
+ blendMode: mapLottieBlendMode(layerRecord['bm'])
2031
+ }, maskConversion.masks.length > 0 && {
2032
+ masks: maskConversion.masks
1832
2033
  });
1833
2034
  var localBounds = computeLottieLocalBounds(convertedLayer, layer, asset, textConversion == null ? void 0 : textConversion.localBounds);
1834
2035
  if (localBounds != null) {
@@ -1937,9 +2138,10 @@ function convertSolidColor(layer) {
1937
2138
  /** Analyzes authored masks; precomposition clipping remains in `content.clipToBounds`. */ function convertLayerMask(layer) {
1938
2139
  var layerRecord = layer;
1939
2140
  if (layerRecord['hasMask'] === true && layerRecord['masksProperties'] != null) {
1940
- return convertLottieMask(layerRecord['masksProperties']);
2141
+ return convertLottieMasks(layerRecord['masksProperties']);
1941
2142
  }
1942
2143
  return {
2144
+ masks: [],
1943
2145
  unsupportedFeatures: []
1944
2146
  };
1945
2147
  }
@@ -2151,8 +2353,25 @@ function resolveLayerMattes(layers, contextByLayerIndex, diagnostics) {
2151
2353
  sourceLayer: layerForTransform
2152
2354
  });
2153
2355
  }
2356
+ }, _loop1 = function() {
2357
+ var context = _step2.value;
2358
+ var _collectParents = collectParents(context, new Set()), parents = _collectParents.parents, ambiguous = _collectParents.ambiguous;
2359
+ var childIn = Math.max(inPoint, toFiniteNumber(context.sourceLayer.ip, 0));
2360
+ var childOut = Math.min(outPoint, toFiniteNumber(context.sourceLayer.op, 0));
2361
+ // Lottie evaluates ancestor transforms even when their own content is inactive.
2362
+ var needsIndependentLifetime = childOut > childIn && parents.some(function(parent) {
2363
+ return childIn < toFiniteNumber(parent.sourceLayer.ip, 0) || childOut > toFiniteNumber(parent.sourceLayer.op, 0);
2364
+ });
2365
+ if (ambiguous || needsIndependentLifetime) {
2366
+ context.parentTransformLayers = parents.map(function(parent) {
2367
+ return parent.sourceLayer;
2368
+ });
2369
+ } else {
2370
+ var _parents_;
2371
+ context.parentLayerId = (_parents_ = parents[0]) == null ? void 0 : _parents_.layerId;
2372
+ }
2154
2373
  };
2155
- var layers = param.layers, assetById = param.assetById, fonts = param.fonts;
2374
+ var layers = param.layers, assetById = param.assetById, fonts = param.fonts, inPoint = param.inPoint, outPoint = param.outPoint;
2156
2375
  var contexts = [];
2157
2376
  var contextByLayerIndex = new Map();
2158
2377
  var diagnostics = [];
@@ -2167,16 +2386,53 @@ function resolveLayerMattes(layers, contextByLayerIndex, diagnostics) {
2167
2386
  };
2168
2387
  // Lottie lists topmost layers first; IR uses back-to-front painter order.
2169
2388
  for(var layerIndex = layers.length - 1; layerIndex >= 0; layerIndex--)_loop(layerIndex);
2170
- for(var _iterator = _create_for_of_iterator_helper_loose(contexts), _step; !(_step = _iterator()).done;){
2389
+ var contextsByIndex = new Map();
2390
+ for(var _iterator = _create_for_of_iterator_helper_loose(contexts.toReversed()), _step; !(_step = _iterator()).done;){
2171
2391
  var context = _step.value;
2172
- var parentInd = context.sourceLayer['parent'];
2173
- if (typeof parentInd === 'number') {
2174
- var parentContext = contextByLayerIndex.get(parentInd);
2175
- if (parentContext != null) {
2176
- context.parentLayerId = parentContext.layerId;
2177
- }
2392
+ var index = context.sourceLayer.ind;
2393
+ if (typeof index === 'number') {
2394
+ var _contextsByIndex_get;
2395
+ var matches = (_contextsByIndex_get = contextsByIndex.get(index)) != null ? _contextsByIndex_get : [];
2396
+ matches.push(context);
2397
+ contextsByIndex.set(index, matches);
2398
+ }
2399
+ }
2400
+ for(var _iterator1 = _create_for_of_iterator_helper_loose(contextsByIndex), _step1; !(_step1 = _iterator1()).done;){
2401
+ var _step_value = _step1.value, index1 = _step_value[0], matches1 = _step_value[1];
2402
+ if (matches1.length > 1) {
2403
+ diagnostics.push(createLottieDiagnostic({
2404
+ code: 'duplicate-layer-index',
2405
+ message: "Layer index " + index1 + " is duplicated; parent transforms follow lottie-web's source-order hierarchy.",
2406
+ layerInd: index1
2407
+ }));
2178
2408
  }
2179
2409
  }
2410
+ var collectParents = function collectParents1(context, ancestors) {
2411
+ var _contextsByIndex_get;
2412
+ if (ancestors.has(context)) {
2413
+ throw new TypeError('Cyclic Lottie parent references cannot be converted.');
2414
+ }
2415
+ var parentIndex = context.sourceLayer.parent;
2416
+ var matches = typeof parentIndex === 'number' ? (_contextsByIndex_get = contextsByIndex.get(parentIndex)) != null ? _contextsByIndex_get : [] : [];
2417
+ var parents = [];
2418
+ var ambiguous = matches.length > 1;
2419
+ ancestors.add(context);
2420
+ for(var _iterator = _create_for_of_iterator_helper_loose(matches), _step; !(_step = _iterator()).done;){
2421
+ var parent = _step.value;
2422
+ var _parents;
2423
+ var inherited = collectParents(parent, ancestors);
2424
+ (_parents = parents).push.apply(_parents, [].concat([
2425
+ parent
2426
+ ], inherited.parents));
2427
+ ambiguous || (ambiguous = inherited.ambiguous);
2428
+ }
2429
+ ancestors.delete(context);
2430
+ return {
2431
+ parents: parents,
2432
+ ambiguous: ambiguous
2433
+ };
2434
+ };
2435
+ for(var _iterator2 = _create_for_of_iterator_helper_loose(contexts), _step2; !(_step2 = _iterator2()).done;)_loop1();
2180
2436
  resolveLayerMattes(layers, contextByLayerIndex, diagnostics);
2181
2437
  return {
2182
2438
  contexts: contexts,
@@ -2243,27 +2499,55 @@ function buildIRCompositionFromIRLayers(layers, options) {
2243
2499
  var parsed = parseLottieLayerStack({
2244
2500
  layers: compositionLayers,
2245
2501
  assetById: assetById,
2246
- fonts: fonts
2502
+ fonts: fonts,
2503
+ inPoint: compositionInPoint,
2504
+ outPoint: compositionOutPoint
2247
2505
  });
2248
2506
  var convertedLayers = [];
2249
2507
  (_diagnostics = diagnostics).push.apply(_diagnostics, [].concat(parsed.diagnostics));
2250
2508
  for(var _iterator = _create_for_of_iterator_helper_loose(parsed.contexts), _step; !(_step = _iterator()).done;){
2251
2509
  var context = _step.value;
2252
2510
  var _diagnostics1;
2511
+ var _ref;
2512
+ var _context_parentTransformLayers;
2253
2513
  var refId = 'refId' in context.sourceLayer ? context.sourceLayer.refId : undefined;
2254
2514
  var precompCompositionId = refId != null ? compositionIdByAssetId.get(String(refId)) : undefined;
2515
+ var parentLayerId = context.parentLayerId;
2516
+ // Private null layers retain ancestor transforms independently of visible
2517
+ // content lifetimes and preserve source-order multiplication for duplicate indices.
2518
+ for(var _iterator1 = _create_for_of_iterator_helper_loose((_ref = (_context_parentTransformLayers = context.parentTransformLayers) == null ? void 0 : _context_parentTransformLayers.toReversed()) != null ? _ref : []), _step1; !(_step1 = _iterator1()).done;){
2519
+ var parent = _step1.value;
2520
+ var transform = convertLottieTransform(parent);
2521
+ var id = createId();
2522
+ delete transform.opacity;
2523
+ convertedLayers.push(_extends({
2524
+ id: id,
2525
+ type: 'null',
2526
+ time: {
2527
+ inFrame: compositionInPoint,
2528
+ outFrame: compositionOutPoint
2529
+ },
2530
+ visible: true,
2531
+ transform: transform
2532
+ }, parentLayerId != null && {
2533
+ parentId: parentLayerId
2534
+ }));
2535
+ parentLayerId = id;
2536
+ }
2255
2537
  var converted = convertLottieLayer({
2256
2538
  layer: context.sourceLayer,
2257
2539
  asset: context.asset,
2258
2540
  id: context.layerId,
2259
- parentLayerId: context.parentLayerId,
2541
+ parentLayerId: parentLayerId,
2260
2542
  matte: context.matte,
2261
2543
  compositionRange: {
2262
2544
  inPoint: compositionInPoint,
2263
2545
  outPoint: compositionOutPoint
2264
2546
  },
2265
2547
  precompCompositionId: precompCompositionId,
2266
- fontAssetIdByName: fontRegistry.assetIdByFontName
2548
+ fontAssetIdByName: fontRegistry.assetIdByFontName,
2549
+ fonts: fonts,
2550
+ chars: json.chars
2267
2551
  });
2268
2552
  (_diagnostics1 = diagnostics).push.apply(_diagnostics1, [].concat(converted.diagnostics));
2269
2553
  convertedLayers.push(converted.layer);
@@ -11271,7 +11555,7 @@ Object.defineProperties(JSZip, staticAccessors);
11271
11555
  * @throws `LottieConverterError` when the archive is invalid or contains no JSON file.
11272
11556
  */ function extractLottieFromZip(zipData) {
11273
11557
  return _async_to_generator(function() {
11274
- var zip, error, resourcePaths, jsonPath, file, mimeType, json, dirPath, resources;
11558
+ var zip, error, resourceEntries, jsonEntry, file, mimeType, json, dirPath, resources;
11275
11559
  return _ts_generator(this, function(_state) {
11276
11560
  switch(_state.label){
11277
11561
  case 0:
@@ -11297,34 +11581,34 @@ Object.defineProperties(JSZip, staticAccessors);
11297
11581
  cause: _instanceof(error, Error) ? error.message : String(error)
11298
11582
  });
11299
11583
  case 3:
11300
- resourcePaths = [];
11584
+ resourceEntries = [];
11301
11585
  for(var relativePath in zip.files){
11302
11586
  file = zip.files[relativePath];
11303
11587
  mimeType = resolveZipResourceMimeType(relativePath);
11304
11588
  if (!file.dir && mimeType != null && !relativePath.includes('_MACOSX')) {
11305
- resourcePaths.push({
11589
+ resourceEntries.push({
11306
11590
  file: file,
11307
11591
  mimeType: mimeType
11308
11592
  });
11309
11593
  }
11310
11594
  if (!file.dir && /\.json$/i.test(relativePath) && !relativePath.includes('_MACOSX')) {
11311
- jsonPath = file;
11595
+ jsonEntry = file;
11312
11596
  }
11313
11597
  }
11314
- if (jsonPath == null) {
11598
+ if (jsonEntry == null) {
11315
11599
  throw new LottieConverterError(LOTTIE_CONVERTER_ERROR_CODES.ZIP_JSON_NOT_FOUND, 'No JSON file was found in the ZIP archive.');
11316
11600
  }
11317
11601
  return [
11318
11602
  4,
11319
- jsonPath.async('string')
11603
+ jsonEntry.async('string')
11320
11604
  ];
11321
11605
  case 4:
11322
11606
  json = _state.sent();
11323
- dirPath = jsonPath.name.split('/').slice(0, -1).join('/');
11607
+ dirPath = jsonEntry.name.split('/').slice(0, -1).join('/');
11324
11608
  resources = {};
11325
11609
  return [
11326
11610
  4,
11327
- Promise.all(resourcePaths.map(function(param) {
11611
+ Promise.all(resourceEntries.map(function(param) {
11328
11612
  var file = param.file, mimeType = param.mimeType;
11329
11613
  return _async_to_generator(function() {
11330
11614
  var buffer, uint8Array, binary, i, base64;