Reland "[svg] Absolute positioning support for text"

This reverts commit e2f62453523e8eeb98968570b48b229f1cc1e712.

Reason for revert: relanding with fixes

Original change's description:
> Revert "[svg] Absolute positioning support for text"
>
> This reverts commit febb1b87a5e73bdbe2cb598783082d3644d0e969.
>
> Reason for revert: breaking the android roll
>
> Original change's description:
> > [svg] Absolute positioning support for text
> >
> > Implement per-character position attribute lookup based on [1]:
> >
> >   - convert "x" and "y" attributes to arrays
> >   - introduce ScopedPosResolver to handle positioning attribute lookup
> >     and fallback
> >   - push a new resolver every time we enter a text positioning element
> >     scope (<text>, <tspan>, etc).
> >   - flush/reposition a new text chunk every time we encounter explicit
> >     absolute positions
> >
> > The position attribute fallback logic is complex enough to warrant a
> > unit test.
> >
> > [1] https://www.w3.org/TR/SVG11/text.html#TSpanElementXAttribute
> >
> > Bug: skia:10840
> > Change-Id: I66c478fea4a179fdb8b1a6a9ff00c4dd9509f8d2
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/345161
> > Commit-Queue: Florin Malita <fmalita@chromium.org>
> > Commit-Queue: Florin Malita <fmalita@google.com>
> > Reviewed-by: Tyler Denniston <tdenniston@google.com>
>
> TBR=fmalita@chromium.org,fmalita@google.com,tdenniston@google.com
>
> Change-Id: I80e3396d555369fe835ee73102135061f63e8bf0
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: skia:10840
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/345417
> Reviewed-by: Derek Sollenberger <djsollen@google.com>
> Commit-Queue: Derek Sollenberger <djsollen@google.com>

TBR=djsollen@google.com,fmalita@chromium.org,fmalita@google.com,tdenniston@google.com

# Not skipping CQ checks because this is a reland.

Bug: skia:10840
Change-Id: I4c6f6a9f19c0f7598bdcf34e915f43c139b995a9
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/345420
Reviewed-by: Florin Malita <fmalita@google.com>
Commit-Queue: Florin Malita <fmalita@google.com>
diff --git a/modules/svg/tests/Text.cpp b/modules/svg/tests/Text.cpp
new file mode 100644
index 0000000..fc09649
--- /dev/null
+++ b/modules/svg/tests/Text.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <vector>
+
+#include "modules/svg/src/SkSVGTextPriv.h"
+#include "tests/Test.h"
+
+DEF_TEST(Svg_Text_PosProvider, r) {
+    const auto L = [](float x) { return SkSVGLength(x); };
+    const float N = SkSVGTextContext::PosAttrs()[SkSVGTextContext::PosAttrs::kX];
+
+    static const struct PosTestDesc {
+        size_t                   offseta;
+        std::vector<SkSVGLength> xa, ya;
+
+        size_t                   offsetb;
+        std::vector<SkSVGLength> xb, yb;
+
+        std::vector<SkPoint>     expected;
+    } gTests[] = {
+        {
+            0, {}, {},
+            0, {}, {},
+
+            { {N,N} }
+        },
+
+        {
+            0, { L(1) }, {},
+            0, {      }, {},
+
+            { {1,N}, {N,N} }
+        },
+        {
+            0, {       }, {},
+            0, { L(10) }, {},
+
+            { {10,N}, {N,N} }
+        },
+        {
+            0, { L( 1) }, {},
+            0, { L(10) }, {},
+
+            { {10,N}, {N,N} }
+        },
+        {
+            0, { L( 1), L(2) }, {},
+            0, { L(10)       }, {},
+
+            { {10,N}, {2,N}, {N,N} }
+        },
+        {
+            0, { L(1), L( 2) }, {},
+            1, {       L(20) }, {},
+
+            { {1,N}, {20,N}, {N,N} }
+        },
+        {
+            0, { L(1), L( 2), L(3) }, {},
+            1, {       L(20)       }, {},
+
+            { {1,N}, {20,N}, {3,N}, {N,N} }
+        },
+        {
+            0, { L(1), L(2), L( 3) }, {},
+            2, {             L(30) }, {},
+
+            { {1,N}, {2,N}, {30,N}, {N,N} }
+        },
+        {
+            0, { L(1)              }, {},
+            2, {             L(30) }, {},
+
+            { {1,N}, {N,N}, {30,N}, {N,N} }
+        },
+
+
+        {
+            0, {}, { L(4) },
+            0, {}, {      },
+
+            { {N,4}, {N,N} }
+        },
+        {
+            0, {}, {       },
+            0, {}, { L(40) },
+
+            { {N,40}, {N,N} }
+        },
+        {
+            0, {}, { L( 4) },
+            0, {}, { L(40) },
+
+            { {N,40}, {N,N} }
+        },
+        {
+            0, {}, { L( 4), L(5) },
+            0, {}, { L(40)       },
+
+            { {N,40}, {N,5}, {N,N} }
+        },
+        {
+            0, {}, { L(4), L( 5) },
+            1, {}, {       L(50) },
+
+            { {N,4}, {N,50}, {N,N} }
+        },
+        {
+            0, {}, { L(4), L( 5), L(6) },
+            1, {}, {       L(50)       },
+
+            { {N,4}, {N,50}, {N,6}, {N,N} }
+        },
+        {
+            0, {}, { L(4), L(5), L( 6) },
+            2, {}, {             L(60) },
+
+            { {N,4}, {N,5}, {N,60}, {N,N} }
+        },
+        {
+            0, {}, { L(4)              },
+            2, {}, {             L(60) },
+
+            { {N,4}, {N,N}, {N,60}, {N,N} }
+        },
+
+        {
+            0, { L( 1), L(2)}, { L( 4)        },
+            0, { L(10)      }, { L(40), L(50) },
+
+            { {10,40}, {2,50}, {N,N} }
+        },
+        {
+            0, { L(1), L( 2), L(3) }, { L(4), L( 5)        },
+            1, {       L(20)       }, {       L(50), L(60) },
+
+            { {1,4}, {20,50}, {3,60}, {N,N} }
+        },
+    };
+
+    auto test = [&](const PosTestDesc& tst) {
+        auto a = SkSVGText::Make();
+        auto b = SkSVGTSpan::Make();
+        a->appendChild(b);
+
+        a->setX(tst.xa);
+        a->setY(tst.ya);
+        b->setX(tst.xb);
+        b->setY(tst.yb);
+
+        SkSVGTextContext tctx(SkSVGPresentationContext(), nullptr);
+        SkSVGLengthContext lctx({0,0});
+        SkSVGTextContext::ScopedPosResolver pa(*a, lctx, &tctx, tst.offseta);
+        SkSVGTextContext::ScopedPosResolver pb(*b, lctx, &tctx, tst.offsetb);
+
+        for (size_t i = 0; i < tst.expected.size(); ++i) {
+            const auto& exp = tst.expected[i];
+            auto pos = i >= tst.offsetb ? pb.resolve(i) : pa.resolve(i);
+
+            REPORTER_ASSERT(r, pos[SkSVGTextContext::PosAttrs::kX] == exp.fX);
+            REPORTER_ASSERT(r, pos[SkSVGTextContext::PosAttrs::kY] == exp.fY);
+        }
+    };
+
+    for (const auto& tst : gTests) {
+        test(tst);
+    }
+}