[skottie] Path support for paragraph text

In addition to single line (point) text, AE also supports path layout
for paragraph text.

At a high level, the paragraph box top is mapped to the path (following
alignment rules), and each glyph is displaced along its path positioning
vector, post orientation.

The main difference compared to point text, is that the distance on path
is based on the fragment position relative to the paragraph left edge.

The paragraph box also plays a role in alignment: left/center/right
aligns with path start/mid/end.

This includes a tangential optimization: instead of validating cached
contour data in each PathInfo::getMatrix() call, we only check once at
a higher level (onSync) -- to avoid performing a shape vector comparison
for each fragment.

Change-Id: I2c31ce3b0a525a3cd2d4525abcf88d5fc943bb6e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/457656
Commit-Queue: Florin Malita <fmalita@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
Reviewed-by: Jorge Betancourt <jmbetancourt@google.com>
diff --git a/modules/skottie/src/text/TextAdapter.cpp b/modules/skottie/src/text/TextAdapter.cpp
index 691c011..d74bfb5 100644
--- a/modules/skottie/src/text/TextAdapter.cpp
+++ b/modules/skottie/src/text/TextAdapter.cpp
@@ -67,7 +67,7 @@
                 fPathPerpendicular = 0,
                 fPathReverse       = 0;
 
-    SkM44 getMatrix(float distance, SkTextUtils::Align alignment) {
+    void updateContourData() {
         const auto reverse = fPathReverse != 0;
 
         if (fPath != fCurrentPath || reverse != fCurrentReversed) {
@@ -88,6 +88,18 @@
             // AE paths are always single-contour (no moves allowed).
             SkASSERT(!iter.next());
         }
+    }
+
+    float pathLength() const {
+        SkASSERT(fPath == fCurrentPath);
+        SkASSERT((fPathReverse != 0) == fCurrentReversed);
+
+        return fCurrentMeasure ? fCurrentMeasure->length() : 0;
+    }
+
+    SkM44 getMatrix(float distance, SkTextUtils::Align alignment) const {
+        SkASSERT(fPath == fCurrentPath);
+        SkASSERT((fPathReverse != 0) == fCurrentReversed);
 
         if (!fCurrentMeasure) {
             return SkM44();
@@ -95,9 +107,6 @@
 
         const auto path_len = fCurrentMeasure->length();
 
-        // Alignment adjustment, relative to path len.
-        distance += path_len * align_factor(alignment);
-
         // First/last margin adjustment also depends on alignment.
         switch (alignment) {
             case SkTextUtils::Align::kLeft_Align:   distance += fPathFMargin; break;
@@ -526,6 +535,11 @@
         return;
     }
 
+    // Update the path contour measure, if needed.
+    if (fPathInfo) {
+        fPathInfo->updateContourData();
+    }
+
     // Seed props from the current text value.
     TextAnimator::ResolvedProps seed_props;
     seed_props.fill_color   = fText->fFillColor;
@@ -662,10 +676,24 @@
         return SkM44::Translate(pos.x, pos.y, pos.z);
     }
 
-    // When using a text path, the horizontal component determines the position on path.
-    const auto path_distance = pos.x;
+    // "Align" the paragraph box left/center/right to path start/mid/end, respectively.
+    const auto align_offset =
+            align_factor(fText->fHAlign)*(fPathInfo->pathLength() - fText->fBox.width());
+
+    // Path positioning is based on the fragment position relative to the paragraph box
+    // upper-left corner:
+    //
+    //   - the horizontal component determines the distance on path
+    //
+    //   - the vertical component is post-applied after orienting on path
+    //
+    // Note: in point-text mode, the box adjustments have no effect as fBox is {0,0,0,0}.
+    //
+    const auto rel_pos = SkV2{pos.x, pos.y} - SkV2{fText->fBox.fLeft, fText->fBox.fTop};
+    const auto path_distance = rel_pos.x + align_offset;
+
     return fPathInfo->getMatrix(path_distance, fText->fHAlign)
-         * SkM44::Translate(0, pos.y, pos.z);
+         * SkM44::Translate(0, rel_pos.y, pos.z);
 }
 
 void TextAdapter::pushPropsToFragment(const TextAnimator::ResolvedProps& props,
diff --git a/resources/skottie/skottie-textpath-paragraph-01.json b/resources/skottie/skottie-textpath-paragraph-01.json
new file mode 100644
index 0000000..27ad59a
--- /dev/null
+++ b/resources/skottie/skottie-textpath-paragraph-01.json
@@ -0,0 +1 @@
+{"v":"5.7.11","fr":60,"ip":0,"op":300,"w":500,"h":500,"nm":"tpath paragraph 1","ddd":0,"assets":[],"fonts":{"list":[{"origin":0,"fPath":"","fClass":"","fFamily":"Arial","fWeight":"","fStyle":"Bold","fName":"Arial-BoldMT","ascent":71.5988159179688}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"The quick brown fox  jumps over the  lazy dog. 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,100,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"n","pt":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-150.703,-8.95],[-50.187,-69.088],[51.187,-8.95],[149.985,-69.947]],"c":false},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"t":{"d":{"k":[{"s":{"sz":[227.663223266602,173.539520263672],"ps":[-113.831611633301,-86.7697601318359],"s":22,"f":"Arial-BoldMT","t":"The quick brown fox \rjumps over the \rlazy dog.","ca":0,"j":2,"tr":16,"lh":26.4000015258789,"ls":0,"fc":[0,0.553,0]},"t":0}]},"p":{"m":0,"f":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.995],"y":[0.746]},"o":{"x":[0.333],"y":[0]},"t":195,"s":[0]},{"t":299,"s":[-300]}],"ix":5},"l":{"a":0,"k":0,"ix":6},"a":0,"p":1,"r":0},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[{"nm":"Animator 1","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":2,"e":{"a":0,"k":10,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.96],"y":[0.738]},"o":{"x":[0.564],"y":[0.052]},"t":0,"s":[-10]},{"t":225,"s":[100]}],"ix":3},"r":1},"a":{"p":{"a":0,"k":[470,0,0],"ix":2}}}]},"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,100,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-151.203,-9.45],[-50.687,-69.588],[50.687,-9.45],[149.485,-70.447]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":5,"nm":"The quick brown fox  jumps over the  lazy dog. 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"n","pt":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-150.703,-8.95],[-50.187,-69.088],[51.187,-8.95],[149.985,-69.947]],"c":false},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"t":{"d":{"k":[{"s":{"sz":[227.663223266602,173.539520263672],"ps":[-113.831611633301,-86.7697601318359],"s":22,"f":"Arial-BoldMT","t":"The quick brown fox \rjumps over the \rlazy dog.","ca":0,"j":2,"tr":16,"lh":26.4000015258789,"ls":0,"fc":[0,0.553,0]},"t":0}]},"p":{"m":0,"f":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.995],"y":[0.746]},"o":{"x":[0.333],"y":[0]},"t":195,"s":[0]},{"t":299,"s":[-300]}],"ix":5},"l":{"a":0,"k":0,"ix":6},"a":0,"p":1,"r":0},"m":{"g":2,"a":{"a":0,"k":[0,0],"ix":2}},"a":[{"nm":"Animator 1","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":2,"e":{"a":0,"k":10,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.96],"y":[0.738]},"o":{"x":[0.564],"y":[0.052]},"t":0,"s":[-10]},{"t":225,"s":[100]}],"ix":3},"r":1},"a":{"p":{"a":0,"k":[470,0,0],"ix":2}}}]},"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-151.203,-9.45],[-50.687,-69.588],[50.687,-9.45],[149.485,-70.447]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":5,"nm":"The quick brown fox  jumps over the  lazy dog. 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,400,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"n","pt":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-150.703,-8.95],[-50.187,-69.088],[51.187,-8.95],[149.985,-69.947]],"c":false},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"t":{"d":{"k":[{"s":{"sz":[227.663223266602,173.539520263672],"ps":[-113.831611633301,-86.7697601318359],"s":22,"f":"Arial-BoldMT","t":"The quick brown fox \rjumps over the \rlazy dog.","ca":0,"j":2,"tr":16,"lh":26.4000015258789,"ls":0,"fc":[0,0.553,0]},"t":0}]},"p":{"m":0,"f":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.995],"y":[0.746]},"o":{"x":[0.333],"y":[0]},"t":195,"s":[0]},{"t":299,"s":[-300]}],"ix":5},"l":{"a":0,"k":0,"ix":6},"a":0,"p":1,"r":0},"m":{"g":3,"a":{"a":0,"k":[0,0],"ix":2}},"a":[{"nm":"Animator 1","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":2,"e":{"a":0,"k":10,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.96],"y":[0.738]},"o":{"x":[0.564],"y":[0.052]},"t":0,"s":[-10]},{"t":225,"s":[100]}],"ix":3},"r":1},"a":{"p":{"a":0,"k":[470,0,0],"ix":2}}}]},"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,400,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-151.203,-9.45],[-50.687,-69.588],[50.687,-9.45],[149.485,-70.447]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":1,"nm":"Medium Yellow Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[600,600,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":1200,"sh":1200,"sc":"#ffffbd","ip":0,"op":300,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/resources/skottie/skottie-textpath-paragraph-02.json b/resources/skottie/skottie-textpath-paragraph-02.json
new file mode 100644
index 0000000..4c76563
--- /dev/null
+++ b/resources/skottie/skottie-textpath-paragraph-02.json
@@ -0,0 +1 @@
+{"v":"5.7.11","fr":60,"ip":0,"op":300,"w":500,"h":500,"nm":"tpath paragraph 2","ddd":0,"assets":[],"fonts":{"list":[{"origin":0,"fPath":"","fClass":"","fFamily":"Arial","fWeight":"","fStyle":"Bold","fName":"Arial-BoldMT","ascent":71.5988159179688}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"The quick brown fox  jumps over the  lazy dog. 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,100,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"n","pt":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-150.703,-8.95],[-50.187,-69.088],[51.187,-8.95],[149.985,-69.947]],"c":false},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"t":{"d":{"k":[{"s":{"sz":[227.663223266602,173.539520263672],"ps":[-113.831611633301,-86.7697601318359],"s":22,"f":"Arial-BoldMT","t":"The quick brown fox \rjumps over the \rlazy dog.","ca":0,"j":2,"tr":16,"lh":26.4000015258789,"ls":0,"fc":[0,0.553,0]},"t":0}]},"p":{"m":0,"f":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.995],"y":[0.746]},"o":{"x":[0.333],"y":[0]},"t":195,"s":[0]},{"t":299,"s":[-300]}],"ix":5},"l":{"a":0,"k":0,"ix":6},"a":0,"p":0,"r":0},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[{"nm":"Animator 1","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":2,"e":{"a":0,"k":10,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.96],"y":[0.738]},"o":{"x":[0.564],"y":[0.052]},"t":0,"s":[-10]},{"t":225,"s":[100]}],"ix":3},"r":1},"a":{"p":{"a":0,"k":[470,0,0],"ix":2}}}]},"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,100,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-151.203,-9.45],[-50.687,-69.588],[50.687,-9.45],[149.485,-70.447]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":5,"nm":"The quick brown fox  jumps over the  lazy dog. 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"n","pt":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-150.703,-8.95],[-50.187,-69.088],[51.187,-8.95],[149.985,-69.947]],"c":false},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"t":{"d":{"k":[{"s":{"sz":[227.663223266602,173.539520263672],"ps":[-113.831611633301,-86.7697601318359],"s":22,"f":"Arial-BoldMT","t":"The quick brown fox \rjumps over the \rlazy dog.","ca":0,"j":2,"tr":16,"lh":26.4000015258789,"ls":0,"fc":[0,0.553,0]},"t":0}]},"p":{"m":0,"f":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.995],"y":[0.746]},"o":{"x":[0.333],"y":[0]},"t":195,"s":[0]},{"t":299,"s":[-300]}],"ix":5},"l":{"a":0,"k":0,"ix":6},"a":0,"p":0,"r":0},"m":{"g":2,"a":{"a":0,"k":[0,0],"ix":2}},"a":[{"nm":"Animator 1","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":2,"e":{"a":0,"k":10,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.96],"y":[0.738]},"o":{"x":[0.564],"y":[0.052]},"t":0,"s":[-10]},{"t":225,"s":[100]}],"ix":3},"r":1},"a":{"p":{"a":0,"k":[470,0,0],"ix":2}}}]},"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-151.203,-9.45],[-50.687,-69.588],[50.687,-9.45],[149.485,-70.447]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":5,"nm":"The quick brown fox  jumps over the  lazy dog. 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,400,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"n","pt":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-150.703,-8.95],[-50.187,-69.088],[51.187,-8.95],[149.985,-69.947]],"c":false},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"t":{"d":{"k":[{"s":{"sz":[227.663223266602,173.539520263672],"ps":[-113.831611633301,-86.7697601318359],"s":22,"f":"Arial-BoldMT","t":"The quick brown fox \rjumps over the \rlazy dog.","ca":0,"j":2,"tr":16,"lh":26.4000015258789,"ls":0,"fc":[0,0.553,0]},"t":0}]},"p":{"m":0,"f":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.995],"y":[0.746]},"o":{"x":[0.333],"y":[0]},"t":195,"s":[0]},{"t":299,"s":[-300]}],"ix":5},"l":{"a":0,"k":0,"ix":6},"a":0,"p":0,"r":0},"m":{"g":3,"a":{"a":0,"k":[0,0],"ix":2}},"a":[{"nm":"Animator 1","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":2,"e":{"a":0,"k":10,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.96],"y":[0.738]},"o":{"x":[0.564],"y":[0.052]},"t":0,"s":[-10]},{"t":225,"s":[100]}],"ix":3},"r":1},"a":{"p":{"a":0,"k":[470,0,0],"ix":2}}}]},"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,400,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-151.203,-9.45],[-50.687,-69.588],[50.687,-9.45],[149.485,-70.447]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":1,"nm":"Medium Yellow Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[600,600,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":1200,"sh":1200,"sc":"#ffffbd","ip":0,"op":300,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/resources/skottie/skottie-textpath-paragraph-03.json b/resources/skottie/skottie-textpath-paragraph-03.json
new file mode 100644
index 0000000..7dd4b6d
--- /dev/null
+++ b/resources/skottie/skottie-textpath-paragraph-03.json
@@ -0,0 +1 @@
+{"v":"5.7.11","fr":60,"ip":0,"op":300,"w":500,"h":500,"nm":"tpath paragraph 3","ddd":0,"assets":[],"fonts":{"list":[{"origin":0,"fPath":"","fClass":"","fFamily":"Arial","fWeight":"","fStyle":"Bold","fName":"Arial-BoldMT","ascent":71.5988159179688}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"The quick brown fox  jumps over the  lazy dog. 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,100,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"n","pt":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-150.703,-8.95],[-50.187,-69.088],[51.187,-8.95],[149.985,-69.947]],"c":false},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"t":{"d":{"k":[{"s":{"sz":[227.663223266602,173.539520263672],"ps":[-113.831611633301,-86.7697601318359],"s":22,"f":"Arial-BoldMT","t":"The quick brown fox \rjumps over the \rlazy dog.","ca":0,"j":0,"tr":16,"lh":26.4000015258789,"ls":0,"fc":[0,0.553,0]},"t":0}]},"p":{"m":0,"f":{"a":0,"k":0,"ix":5},"l":{"a":0,"k":0,"ix":6},"a":0,"p":1,"r":0},"m":{"g":1,"a":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[0,0]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":30,"s":[0,0]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":90,"s":[-150,100]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":210,"s":[150,-100]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[0,0]},{"t":299,"s":[0,0]}],"ix":2}},"a":[]},"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,100,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-151.203,-9.45],[-50.687,-69.588],[50.687,-9.45],[149.485,-70.447]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":5,"nm":"The quick brown fox  jumps over the  lazy dog. 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"n","pt":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-150.703,-8.95],[-50.187,-69.088],[51.187,-8.95],[149.985,-69.947]],"c":false},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"t":{"d":{"k":[{"s":{"sz":[227.663223266602,173.539520263672],"ps":[-113.831611633301,-86.7697601318359],"s":22,"f":"Arial-BoldMT","t":"The quick brown fox \rjumps over the \rlazy dog.","ca":0,"j":2,"tr":16,"lh":26.4000015258789,"ls":0,"fc":[0,0.553,0]},"t":0}]},"p":{"m":0,"f":{"a":0,"k":0,"ix":5},"l":{"a":0,"k":0,"ix":6},"a":0,"p":1,"r":0},"m":{"g":1,"a":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[0,0]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":30,"s":[0,0]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":90,"s":[-150,100]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":210,"s":[150,-100]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[0,0]},{"t":299,"s":[0,0]}],"ix":2}},"a":[]},"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-151.203,-9.45],[-50.687,-69.588],[50.687,-9.45],[149.485,-70.447]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":5,"nm":"The quick brown fox  jumps over the  lazy dog. 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,400,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"n","pt":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-150.703,-8.95],[-50.187,-69.088],[51.187,-8.95],[149.985,-69.947]],"c":false},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"t":{"d":{"k":[{"s":{"sz":[227.663223266602,173.539520263672],"ps":[-113.831611633301,-86.7697601318359],"s":22,"f":"Arial-BoldMT","t":"The quick brown fox \rjumps over the \rlazy dog.","ca":0,"j":1,"tr":16,"lh":26.4000015258789,"ls":0,"fc":[0,0.553,0]},"t":0}]},"p":{"m":0,"f":{"a":0,"k":0,"ix":5},"l":{"a":0,"k":0,"ix":6},"a":0,"p":1,"r":0},"m":{"g":1,"a":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[0,0]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":30,"s":[0,0]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":90,"s":[-150,100]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":210,"s":[150,-100]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[0,0]},{"t":299,"s":[0,0]}],"ix":2}},"a":[]},"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.55,400,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-39.519,0],[-39.528,0],[-42.132,0],[-41.237,0]],"o":[[38.66,0],[39.519,0],[40.378,0],[39.519,0]],"v":[[-151.203,-9.45],[-50.687,-69.588],[50.687,-9.45],[149.485,-70.447]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":1,"nm":"Medium Yellow Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[600,600,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":1200,"sh":1200,"sc":"#ffffbd","ip":0,"op":300,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file