Reland "Removing ICU dependencies from skparagraph BUILD.gn file"

This reverts commit 05ce2817f2a060caf8acd5be25b4f29875d63049.

Reason for revert: Fixing the build

Original change's description:
> Revert "Removing ICU dependencies from skparagraph BUILD.gn file"
>
> This reverts commit f1711adb1a04595eba1e9984976bfb92d41d0035.
>
> Reason for revert: Build break
>
> Original change's description:
> > Removing ICU dependencies from skparagraph BUILD.gn file
> >
> > (and from the sources, too)
> >
> > Change-Id: I9d8ff51c91aad4b770b1f183c04734d31252b851
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/313148
> > Commit-Queue: Julia Lavrova <jlavrova@google.com>
> > Reviewed-by: Ben Wagner <bungeman@google.com>
>
> TBR=bungeman@google.com,jlavrova@google.com
>
> Change-Id: I1fce2436855e3e2a4cb7d1d7204b3ae49fd530e8
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/314540
> Reviewed-by: Julia Lavrova <jlavrova@google.com>
> Commit-Queue: Julia Lavrova <jlavrova@google.com>

TBR=bungeman@google.com,jlavrova@google.com

Change-Id: I13d78d75698df47930adc2514d1328abc556a209
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/316444
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
diff --git a/modules/canvaskit/paragraph_bindings.cpp b/modules/canvaskit/paragraph_bindings.cpp
index f5d59f3..a4e31f7 100644
--- a/modules/canvaskit/paragraph_bindings.cpp
+++ b/modules/canvaskit/paragraph_bindings.cpp
@@ -183,21 +183,21 @@
         .function("layout", &para::ParagraphImpl::layout);
 
     class_<para::ParagraphBuilderImpl>("ParagraphBuilder")
-        .class_function("_Make", optional_override([](SimpleParagraphStyle style,
-                                                     sk_sp<SkFontMgr> fontMgr)-> para::ParagraphBuilderImpl {
+        .class_function("_Make", optional_override([](SimpleParagraphStyle style, sk_sp<SkFontMgr> fontMgr)
+                        -> std::unique_ptr<para::ParagraphBuilderImpl> {
             auto fc = sk_make_sp<para::FontCollection>();
             fc->setDefaultFontManager(fontMgr);
             auto ps = toParagraphStyle(style);
-            para::ParagraphBuilderImpl pbi(ps, fc);
-            return pbi;
+            auto pb = para::ParagraphBuilderImpl::make(ps, fc);
+            return std::unique_ptr<para::ParagraphBuilderImpl>(static_cast<para::ParagraphBuilderImpl*>(pb.release()));
         }), allow_raw_pointers())
       .class_function("_MakeFromFontProvider", optional_override([](SimpleParagraphStyle style,
-                                                                    sk_sp<para::TypefaceFontProvider> fontProvider)-> para::ParagraphBuilderImpl {
+                      sk_sp<para::TypefaceFontProvider> fontProvider)-> std::unique_ptr<para::ParagraphBuilderImpl> {
             auto fc = sk_make_sp<para::FontCollection>();
             fc->setDefaultFontManager(fontProvider);
             auto ps = toParagraphStyle(style);
-            para::ParagraphBuilderImpl pbi(ps, fc);
-            return pbi;
+            auto pb = para::ParagraphBuilderImpl::make(ps, fc);
+            return std::unique_ptr<para::ParagraphBuilderImpl>(static_cast<para::ParagraphBuilderImpl*>(pb.release()));
       }), allow_raw_pointers())
         .function("addText", optional_override([](para::ParagraphBuilderImpl& self, std::string text) {
             return self.addText(text.c_str(), text.length());
diff --git a/modules/skparagraph/BUILD.gn b/modules/skparagraph/BUILD.gn
index 48e0929..e8a8b48 100644
--- a/modules/skparagraph/BUILD.gn
+++ b/modules/skparagraph/BUILD.gn
@@ -1,6 +1,7 @@
 # Copyright 2019 Google LLC.
 
 import("../../gn/skia.gni")
+import("../skshaper/skshaper.gni")
 
 declare_args() {
   skia_enable_skparagraph = true
@@ -9,7 +10,8 @@
   paragraph_bench_enabled = false
 }
 
-if (skia_enable_skparagraph) {
+if (skia_enable_skparagraph && skia_enable_skshaper && skia_use_icu &&
+    skia_use_harfbuzz) {
   config("public_config") {
     include_dirs = [
       "include",
@@ -21,16 +23,10 @@
     import("skparagraph.gni")
     public_configs = [ ":public_config" ]
     public = skparagraph_public
-    if (skia_use_icu && skia_use_harfbuzz) {
-      sources = skparagraph_sources
-      configs += [ "../../third_party/icu/config:no_cxx" ]
-    } else {
-      sources = []
-    }
+    sources = skparagraph_sources
     deps = [
       "../..:skia",
       "../skshaper",
-      "//third_party/icu",
     ]
   }
 
@@ -43,21 +39,15 @@
       import("skparagraph.gni")
       public_configs = [ ":utils_config" ]
       configs += [ "../../:skia_private" ]
-      if (skia_use_icu && skia_use_harfbuzz) {
-        sources = skparagraph_utils
-        configs += [ "../../third_party/icu/config:no_cxx" ]
-      } else {
-        sources = []
-      }
+      sources = skparagraph_utils
       deps = [
         "../..:skia",
         "../skshaper",
-        "//third_party/icu",
       ]
     }
 
     source_set("gm") {
-      if (skia_use_icu && skia_use_harfbuzz && paragraph_gms_enabled) {
+      if (paragraph_gms_enabled) {
         testonly = true
         sources = [ "gm/simple_gm.cpp" ]
         deps = [
@@ -65,53 +55,63 @@
           "../..:gpu_tool_utils",
           "../..:skia",
           "../skshaper",
-          "//third_party/icu",
         ]
+      } else {
+        sources = []
       }
     }
 
     source_set("tests") {
-      if (skia_use_icu && skia_use_harfbuzz && paragraph_tests_enabled) {
+      if (paragraph_tests_enabled) {
         testonly = true
         sources = [ "tests/SkParagraphTest.cpp" ]
-        configs += [ "../../third_party/icu/config:no_cxx" ]
         deps = [
           ":skparagraph",
           "../..:gpu_tool_utils",
           "../..:skia",
           "../skshaper",
-          "//third_party/icu",
         ]
+      } else {
+        sources = []
       }
     }
 
     source_set("bench") {
-      if (skia_use_icu && skia_use_harfbuzz && paragraph_bench_enabled) {
+      if (paragraph_bench_enabled) {
         testonly = true
         sources = [ "bench/ParagraphBench.cpp" ]
-        configs += [ "../../third_party/icu/config:no_cxx" ]
         deps = [
           ":skparagraph",
           "../..:skia",
           "../skshaper",
-          "//third_party/icu",
         ]
+      } else {
+        sources = []
       }
     }
 
     source_set("samples") {
-      if (skia_use_icu && skia_use_harfbuzz) {
-        testonly = true
-        sources = [ "samples/SampleParagraph.cpp" ]
-        configs += [ "../../third_party/icu/config:no_cxx" ]
-        deps = [
-          ":skparagraph",
-          ":utils",
-          "../..:skia",
-          "../skshaper",
-          "//third_party/icu",
-        ]
-      }
+      testonly = true
+      sources = [ "samples/SampleParagraph.cpp" ]
+      deps = [
+        ":skparagraph",
+        ":utils",
+        "../..:skia",
+        "../skshaper",
+      ]
     }
   }
+} else {
+  group("skparagraph") {
+  }
+  group("utils") {
+  }
+  group("gm") {
+  }
+  group("tests") {
+  }
+  group("bench") {
+  }
+  group("samples") {
+  }
 }
diff --git a/modules/skparagraph/gm/simple_gm.cpp b/modules/skparagraph/gm/simple_gm.cpp
index bd7d016..be7d15b 100644
--- a/modules/skparagraph/gm/simple_gm.cpp
+++ b/modules/skparagraph/gm/simple_gm.cpp
@@ -21,7 +21,7 @@
 #include "tools/ToolUtils.h"
 
 #include "modules/skparagraph/include/Paragraph.h"
-#include "modules/skparagraph/include/ParagraphBuilder.h"
+#include "modules/skparagraph/src/ParagraphBuilderImpl.h"
 
 static const char* gSpeach = "Five score years ago, a great American, in whose symbolic shadow we stand today, signed the Emancipation Proclamation. This momentous decree came as a great beacon light of hope to millions of Negro slaves who had been seared in the flames of withering injustice. It came as a joyous daybreak to end the long night of their captivity.";
 
@@ -57,7 +57,11 @@
 
         auto collection = sk_make_sp<skia::textlayout::FontCollection>();
         collection->setDefaultFontManager(SkFontMgr::RefDefault());
-        auto builder = skia::textlayout::ParagraphBuilder::make(paraStyle, collection);
+        auto builder = skia::textlayout::ParagraphBuilderImpl::make(paraStyle, collection);
+        if (nullptr == builder) {
+            fPara = nullptr;
+            return;
+        }
 
         builder->addText(gSpeach, strlen(gSpeach));
 
@@ -81,6 +85,9 @@
     SkISize onISize() override { return SkISize::Make(412, 420); }
 
     DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
+        if (nullptr == fPara) {
+            return DrawResult::kSkip;
+        }
         const int loop = (this->getMode() == kGM_Mode) ? 1 : 50;
 
         int parity = 0;
diff --git a/modules/skparagraph/include/ParagraphBuilder.h b/modules/skparagraph/include/ParagraphBuilder.h
index 1f1e0ed..816691b 100644
--- a/modules/skparagraph/include/ParagraphBuilder.h
+++ b/modules/skparagraph/include/ParagraphBuilder.h
@@ -57,6 +57,8 @@
     // Constructs a SkParagraph object that can be used to layout and paint the text to a SkCanvas.
     virtual std::unique_ptr<Paragraph> Build() = 0;
 
+
+    // Just until we fix all the google3 code
     static std::unique_ptr<ParagraphBuilder> make(const ParagraphStyle& style,
                                                   sk_sp<FontCollection> fontCollection);
 };
diff --git a/modules/skparagraph/include/ParagraphStyle.h b/modules/skparagraph/include/ParagraphStyle.h
index 43fb71f..871e674 100644
--- a/modules/skparagraph/include/ParagraphStyle.h
+++ b/modules/skparagraph/include/ParagraphStyle.h
@@ -72,7 +72,9 @@
     ParagraphStyle();
 
     bool operator==(const ParagraphStyle& rhs) const {
-        return this->fHeight == rhs.fHeight && this->fEllipsis == rhs.fEllipsis &&
+        return this->fHeight == rhs.fHeight &&
+               this->fEllipsis == rhs.fEllipsis &&
+               this->fEllipsisUtf16 == rhs.fEllipsisUtf16 &&
                this->fTextDirection == rhs.fTextDirection && this->fTextAlign == rhs.fTextAlign &&
                this->fDefaultTextStyle == rhs.fDefaultTextStyle;
     }
@@ -92,8 +94,9 @@
     size_t getMaxLines() const { return fLinesLimit; }
     void setMaxLines(size_t maxLines) { fLinesLimit = maxLines; }
 
-    const SkString& getEllipsis() const { return fEllipsis; }
-    void setEllipsis(const std::u16string& ellipsis);
+    SkString getEllipsis() const { return fEllipsis; }
+    std::u16string getEllipsisUtf16() const { return fEllipsisUtf16; }
+    void setEllipsis(const std::u16string& ellipsis) {  fEllipsisUtf16 = ellipsis; }
     void setEllipsis(const SkString& ellipsis) { fEllipsis = ellipsis; }
 
     SkScalar getHeight() const { return fHeight; }
@@ -105,7 +108,7 @@
     bool unlimited_lines() const {
         return fLinesLimit == std::numeric_limits<size_t>::max();
     }
-    bool ellipsized() const { return fEllipsis.size() != 0; }
+    bool ellipsized() const { return !fEllipsis.isEmpty() || !fEllipsisUtf16.empty(); }
     TextAlign effective_align() const;
     bool hintingIsOn() const { return fHintingIsOn; }
     void turnHintingOff() { fHintingIsOn = false; }
@@ -118,6 +121,7 @@
     TextAlign fTextAlign;
     TextDirection fTextDirection;
     size_t fLinesLimit;
+    std::u16string fEllipsisUtf16;
     SkString fEllipsis;
     SkScalar fHeight;
     TextHeightBehavior fTextHeightBehavior;
diff --git a/modules/skparagraph/samples/SampleParagraph.cpp b/modules/skparagraph/samples/SampleParagraph.cpp
index c650cba..a2cff7b 100644
--- a/modules/skparagraph/samples/SampleParagraph.cpp
+++ b/modules/skparagraph/samples/SampleParagraph.cpp
@@ -17,9 +17,9 @@
 #include "modules/skparagraph/include/TypefaceFontProvider.h"
 #include "modules/skparagraph/src/ParagraphBuilderImpl.h"
 #include "modules/skparagraph/src/ParagraphImpl.h"
-#include "modules/skparagraph/src/ParagraphUtil.h"
 #include "modules/skparagraph/src/TextLine.h"
 #include "modules/skparagraph/utils/TestFontCollection.h"
+#include "modules/skshaper/src/SkUnicode.h"
 #include "samplecode/Sample.h"
 #include "src/core/SkOSFile.h"
 #include "src/shaders/SkColorShader.h"
@@ -28,7 +28,6 @@
 #include "tools/Resources.h"
 #include "tools/flags/CommandLineFlags.h"
 
-
 static DEFINE_bool(verboseParagraph, false, "paragraph samples very verbose.");
 
 using namespace skia::textlayout;
@@ -746,14 +745,15 @@
             builder.addText(text4);
             builder.pop();
         } else {
-            if (this->isVerbose()) {
-                SkString str = SkStringFromU16String(text);
-                SkDebugf("Text: %s\n", str.c_str());
-            }
             builder.addText(text + expected);
         }
 
         auto paragraph = builder.Build();
+        auto impl = static_cast<ParagraphImpl*>(paragraph.get());
+        if (this->isVerbose()) {
+            SkDebugf("Text: >%s<\n", impl->text().data());
+        }
+
         paragraph->layout(w - margin * 2);
         paragraph->paint(canvas, margin, margin);
     }
@@ -1608,15 +1608,14 @@
                 ParagraphBuilderImpl builder(paragraph_style, fontCollection);
                 builder.pushStyle(text_style);
                 auto utf16text = zalgo.zalgo("SkParagraph");
-                if (this->isVerbose()) {
-                    SkString str = SkStringFromU16String(utf16text);
-                    SkDebugf("Text:>%s<\n", str.c_str());
-                }
                 builder.addText(utf16text);
                 fParagraph = builder.Build();
             }
 
             auto impl = static_cast<ParagraphImpl*>(fParagraph.get());
+            if (this->isVerbose()) {
+                SkDebugf("Text:>%s<\n", impl->text().data());
+            }
             impl->setState(InternalState::kUnknown);
             fParagraph->layout(1000);
             fParagraph->paint(canvas, 300, 200);
diff --git a/modules/skparagraph/skparagraph.gni b/modules/skparagraph/skparagraph.gni
index 0221d78..df0091c 100644
--- a/modules/skparagraph/skparagraph.gni
+++ b/modules/skparagraph/skparagraph.gni
@@ -31,8 +31,6 @@
   "$_src/ParagraphImpl.cpp",
   "$_src/ParagraphImpl.h",
   "$_src/ParagraphStyle.cpp",
-  "$_src/ParagraphUtil.cpp",
-  "$_src/ParagraphUtil.h",
   "$_src/Run.cpp",
   "$_src/Run.h",
   "$_src/TextLine.cpp",
diff --git a/modules/skparagraph/src/OneLineShaper.cpp b/modules/skparagraph/src/OneLineShaper.cpp
index d2a8ec2..45c622c 100644
--- a/modules/skparagraph/src/OneLineShaper.cpp
+++ b/modules/skparagraph/src/OneLineShaper.cpp
@@ -2,10 +2,15 @@
 
 #include "modules/skparagraph/src/Iterators.h"
 #include "modules/skparagraph/src/OneLineShaper.h"
-#include "modules/skparagraph/src/ParagraphUtil.h"
+#include "src/utils/SkUTF.h"
 #include <algorithm>
 #include <unordered_set>
 
+static inline SkUnichar nextUtf8Unit(const char** ptr, const char* end) {
+    SkUnichar val = SkUTF::NextUTF8(ptr, end);
+    return val < 0 ? 0xFFFD : val;
+}
+
 namespace skia {
 namespace textlayout {
 
@@ -297,8 +302,8 @@
 
         const char* cluster = text.begin() + clusterIndex(i);
         SkUnichar codepoint = nextUtf8Unit(&cluster, text.end());
-        bool isControl8 = isControl(codepoint);
-        bool isWhitespace8 = isWhitespace(codepoint);
+        bool isControl8 = fParagraph->getUnicode()->isControl(codepoint);
+        bool isWhitespace8 = fParagraph->getUnicode()->isWhitespace(codepoint);
 
         // Inspect the glyph
         auto glyph = fCurrentRun->fGlyphs[i];
diff --git a/modules/skparagraph/src/ParagraphBuilderImpl.cpp b/modules/skparagraph/src/ParagraphBuilderImpl.cpp
index 5f904e9..1502012 100644
--- a/modules/skparagraph/src/ParagraphBuilderImpl.cpp
+++ b/modules/skparagraph/src/ParagraphBuilderImpl.cpp
@@ -8,24 +8,50 @@
 #include "modules/skparagraph/include/TextStyle.h"
 #include "modules/skparagraph/src/ParagraphBuilderImpl.h"
 #include "modules/skparagraph/src/ParagraphImpl.h"
-#include "modules/skparagraph/src/ParagraphUtil.h"
 
 #include <algorithm>
 #include <utility>
+#include "src/core/SkStringUtils.h"
 
 namespace skia {
 namespace textlayout {
 
 std::unique_ptr<ParagraphBuilder> ParagraphBuilder::make(
         const ParagraphStyle& style, sk_sp<FontCollection> fontCollection) {
+    return ParagraphBuilderImpl::make(style, fontCollection);
+}
+
+std::unique_ptr<ParagraphBuilder> ParagraphBuilderImpl::make(
+        const ParagraphStyle& style, sk_sp<FontCollection> fontCollection) {
+    auto unicode = SkUnicode::Make();
+    if (nullptr == unicode) {
+        return nullptr;
+    }
     return std::make_unique<ParagraphBuilderImpl>(style, fontCollection);
 }
 
+std::unique_ptr<ParagraphBuilder> ParagraphBuilderImpl::make(
+        const ParagraphStyle& style, sk_sp<FontCollection> fontCollection, std::unique_ptr<SkUnicode> unicode) {
+    if (nullptr == unicode) {
+        return nullptr;
+    }
+    return std::make_unique<ParagraphBuilderImpl>(style, fontCollection, std::move(unicode));
+}
+
+ParagraphBuilderImpl::ParagraphBuilderImpl(
+        const ParagraphStyle& style, sk_sp<FontCollection> fontCollection, std::unique_ptr<SkUnicode> unicode)
+        : ParagraphBuilder(style, fontCollection)
+        , fUtf8()
+        , fFontCollection(std::move(fontCollection))
+        , fUnicode(std::move(unicode)) {
+    SkASSERT(fUnicode);
+    this->setParagraphStyle(style);
+}
+
 ParagraphBuilderImpl::ParagraphBuilderImpl(
         const ParagraphStyle& style, sk_sp<FontCollection> fontCollection)
-        : ParagraphBuilder(style, fontCollection), fUtf8(), fFontCollection(std::move(fontCollection)) {
-    this->setParagraphStyle(style);
-}
+        : ParagraphBuilderImpl(style, fontCollection, SkUnicode::Make())
+{ }
 
 ParagraphBuilderImpl::~ParagraphBuilderImpl() = default;
 
@@ -74,7 +100,8 @@
 }
 
 void ParagraphBuilderImpl::addText(const std::u16string& text) {
-    fUtf8.append(SkStringFromU16String(text));
+    auto utf8 = fUnicode->convertUtf16ToUtf8(text);
+    fUtf8.append(utf8);
 }
 
 void ParagraphBuilderImpl::addText(const char* text) {
@@ -128,7 +155,7 @@
     // Add one fake placeholder with the rest of the text
     addPlaceholder(PlaceholderStyle(), true);
     return std::make_unique<ParagraphImpl>(
-            fUtf8, fParagraphStyle, fStyledBlocks, fPlaceholders, fFontCollection);
+            fUtf8, fParagraphStyle, fStyledBlocks, fPlaceholders, fFontCollection, std::move(fUnicode));
 }
 
 }  // namespace textlayout
diff --git a/modules/skparagraph/src/ParagraphBuilderImpl.h b/modules/skparagraph/src/ParagraphBuilderImpl.h
index 644769e..49a24c4 100644
--- a/modules/skparagraph/src/ParagraphBuilderImpl.h
+++ b/modules/skparagraph/src/ParagraphBuilderImpl.h
@@ -11,12 +11,18 @@
 #include "modules/skparagraph/include/ParagraphBuilder.h"
 #include "modules/skparagraph/include/ParagraphStyle.h"
 #include "modules/skparagraph/include/TextStyle.h"
+#include "modules/skshaper/src/SkUnicode.h"
 
 namespace skia {
 namespace textlayout {
 
 class ParagraphBuilderImpl : public ParagraphBuilder {
 public:
+    ParagraphBuilderImpl(const ParagraphStyle& style,
+        sk_sp<FontCollection> fontCollection,
+        std::unique_ptr<SkUnicode> unicode);
+
+    // Just until we fix all the code; calls icu::make inside
     ParagraphBuilderImpl(const ParagraphStyle& style, sk_sp<FontCollection> fontCollection);
 
     ~ParagraphBuilderImpl() override;
@@ -55,6 +61,13 @@
     // Constructs a SkParagraph object that can be used to layout and paint the text to a SkCanvas.
     std::unique_ptr<Paragraph> Build() override;
 
+    static std::unique_ptr<ParagraphBuilder> make(const ParagraphStyle& style,
+                                                  sk_sp<FontCollection> fontCollection,
+                                                  std::unique_ptr<SkUnicode> unicode);
+
+    // Just until we fix all the code; calls icu::make inside
+    static std::unique_ptr<ParagraphBuilder> make(const ParagraphStyle& style,
+                                                  sk_sp<FontCollection> fontCollection);
 private:
     void endRunIfNeeded();
     void addPlaceholder(const PlaceholderStyle& placeholderStyle, bool lastOne);
@@ -65,6 +78,8 @@
     SkTArray<Placeholder, true> fPlaceholders;
     sk_sp<FontCollection> fFontCollection;
     ParagraphStyle fParagraphStyle;
+
+    std::unique_ptr<SkUnicode> fUnicode;
 };
 }  // namespace textlayout
 }  // namespace skia
diff --git a/modules/skparagraph/src/ParagraphImpl.cpp b/modules/skparagraph/src/ParagraphImpl.cpp
index 487daf2..9804a8c 100644
--- a/modules/skparagraph/src/ParagraphImpl.cpp
+++ b/modules/skparagraph/src/ParagraphImpl.cpp
@@ -13,17 +13,11 @@
 #include "modules/skparagraph/include/TextStyle.h"
 #include "modules/skparagraph/src/OneLineShaper.h"
 #include "modules/skparagraph/src/ParagraphImpl.h"
-#include "modules/skparagraph/src/ParagraphUtil.h"
 #include "modules/skparagraph/src/Run.h"
 #include "modules/skparagraph/src/TextLine.h"
 #include "modules/skparagraph/src/TextWrapper.h"
 #include "src/core/SkSpan.h"
 #include "src/utils/SkUTF.h"
-
-#if defined(SK_USING_THIRD_PARTY_ICU)
-#include "third_party/icu/SkLoadICU.h"
-#endif
-
 #include <math.h>
 #include <algorithm>
 #include <utility>
@@ -71,7 +65,8 @@
                              ParagraphStyle style,
                              SkTArray<Block, true> blocks,
                              SkTArray<Placeholder, true> placeholders,
-                             sk_sp<FontCollection> fonts)
+                             sk_sp<FontCollection> fonts,
+                             std::unique_ptr<SkUnicode> unicode)
         : Paragraph(std::move(style), std::move(fonts))
         , fTextStyles(std::move(blocks))
         , fPlaceholders(std::move(placeholders))
@@ -81,21 +76,28 @@
         , fPicture(nullptr)
         , fStrutMetrics(false)
         , fOldWidth(0)
-        , fOldHeight(0) {
-    fICU = SkUnicode::Make();
+        , fOldHeight(0)
+        , fUnicode(std::move(unicode))
+{
+    SkASSERT(fUnicode);
 }
 
 ParagraphImpl::ParagraphImpl(const std::u16string& utf16text,
                              ParagraphStyle style,
                              SkTArray<Block, true> blocks,
                              SkTArray<Placeholder, true> placeholders,
-                             sk_sp<FontCollection> fonts)
-        : ParagraphImpl(SkStringFromU16String(utf16text),
+                             sk_sp<FontCollection> fonts,
+                             std::unique_ptr<SkUnicode> unicode)
+        : ParagraphImpl(SkString(),
                         std::move(style),
                         std::move(blocks),
                         std::move(placeholders),
-                        std::move(fonts))
-{ }
+                        std::move(fonts),
+                        std::move(unicode))
+{
+    SkASSERT(fUnicode);
+    fText =  fUnicode->convertUtf16ToUtf8(utf16text);
+}
 
 ParagraphImpl::~ParagraphImpl() = default;
 
@@ -243,26 +245,23 @@
 }
 
 // shapeTextIntoEndlessLine is the thing that calls this method
-// (that contains all ICU dependencies except for words)
 bool ParagraphImpl::computeCodeUnitProperties() {
 
-    #if defined(SK_USING_THIRD_PARTY_ICU)
-    if (!SkLoadICU()) {
+    if (nullptr == fUnicode) {
         return false;
     }
-    #endif
 
     // Get bidi regions
     auto textDirection = fParagraphStyle.getTextDirection() == TextDirection::kLtr
                               ? SkUnicode::TextDirection::kLTR
                               : SkUnicode::TextDirection::kRTL;
-    if (!fICU->getBidiRegions(fText.c_str(), fText.size(), textDirection, &fBidiRegions)) {
+    if (!fUnicode->getBidiRegions(fText.c_str(), fText.size(), textDirection, &fBidiRegions)) {
         return false;
     }
 
     // Get white spaces
     std::vector<SkUnicode::Position> whitespaces;
-    if (!fICU->getWhitespaces(fText.c_str(), fText.size(), &whitespaces)) {
+    if (!fUnicode->getWhitespaces(fText.c_str(), fText.size(), &whitespaces)) {
         return false;
     }
     for (auto whitespace : whitespaces) {
@@ -271,7 +270,7 @@
 
     // Get line breaks
     std::vector<SkUnicode::LineBreakBefore> lineBreaks;
-    if (!fICU->getLineBreaks(fText.c_str(), fText.size(), &lineBreaks)) {
+    if (!fUnicode->getLineBreaks(fText.c_str(), fText.size(), &lineBreaks)) {
         return false;
     }
     for (auto& lineBreak : lineBreaks) {
@@ -282,7 +281,7 @@
 
     // Get graphemes
     std::vector<SkUnicode::Position> graphemes;
-    if (!fICU->getGraphemes(fText.c_str(), fText.size(), &graphemes)) {
+    if (!fUnicode->getGraphemes(fText.c_str(), fText.size(), &graphemes)) {
         return false;
     }
     for (auto pos : graphemes) {
@@ -429,7 +428,7 @@
                 // TODO: Take in account clipped edges
                 auto& line = this->addLine(offset, advance, text, textWithSpaces, clusters, clustersWithGhosts, widthWithSpaces, metrics);
                 if (addEllipsis) {
-                    line.createEllipsis(maxWidth, fParagraphStyle.getEllipsis(), true);
+                    line.createEllipsis(maxWidth, getEllipsis(), true);
                 }
 
                 fLongestLine = std::max(fLongestLine, nearlyZero(advance.fX) ? widthWithSpaces : advance.fX);
@@ -686,7 +685,7 @@
 SkRange<size_t> ParagraphImpl::getWordBoundary(unsigned offset) {
 
     if (fWords.empty()) {
-        if (!fICU->getWords(fText.c_str(), fText.size(), &fWords)) {
+        if (!fUnicode->getWords(fText.c_str(), fText.size(), &fWords)) {
             return {0, 0 };
         }
     }
@@ -837,6 +836,17 @@
     }
 }
 
+SkString ParagraphImpl::getEllipsis() const {
+
+    auto ellipsis8 = fParagraphStyle.getEllipsis();
+    auto ellipsis16 = fParagraphStyle.getEllipsisUtf16();
+    if (!ellipsis8.isEmpty()) {
+        return ellipsis8;
+    } else {
+        return fUnicode->convertUtf16ToUtf8(fParagraphStyle.getEllipsisUtf16());
+    }
+}
+
 void ParagraphImpl::updateText(size_t from, SkString text) {
   fText.remove(from, from + text.size());
   fText.insert(from, text);
diff --git a/modules/skparagraph/src/ParagraphImpl.h b/modules/skparagraph/src/ParagraphImpl.h
index 4bd3d90..a4af3db 100644
--- a/modules/skparagraph/src/ParagraphImpl.h
+++ b/modules/skparagraph/src/ParagraphImpl.h
@@ -100,13 +100,15 @@
                   ParagraphStyle style,
                   SkTArray<Block, true> blocks,
                   SkTArray<Placeholder, true> placeholders,
-                  sk_sp<FontCollection> fonts);
+                  sk_sp<FontCollection> fonts,
+                  std::unique_ptr<SkUnicode> unicode);
 
     ParagraphImpl(const std::u16string& utf16text,
                   ParagraphStyle style,
                   SkTArray<Block, true> blocks,
                   SkTArray<Placeholder, true> placeholders,
-                  sk_sp<FontCollection> fonts);
+                  sk_sp<FontCollection> fonts,
+                  std::unique_ptr<SkUnicode> unicode);
     ~ParagraphImpl() override;
 
     void layout(SkScalar width) override;
@@ -155,6 +157,8 @@
     }
     InternalLineMetrics strutMetrics() const { return fStrutMetrics; }
 
+    SkString getEllipsis() const;
+
     SkSpan<const char> text(TextRange textRange);
     SkSpan<Cluster> clusters(ClusterRange clusterRange);
     Cluster& cluster(ClusterIndex clusterIndex);
@@ -218,7 +222,7 @@
 
     bool codeUnitHasProperty(size_t index, CodeUnitFlags property) const { return (fCodeUnitProperties[index] & property) == property; }
 
-    SkUnicode* getICU() { return fICU.get(); }
+    SkUnicode* getUnicode() { return fUnicode.get(); }
 
 private:
     friend class ParagraphBuilder;
@@ -268,7 +272,7 @@
     SkScalar fOldHeight;
     SkScalar fMaxWidthWithTrailingSpaces;
 
-    std::unique_ptr<SkUnicode> fICU;
+    std::unique_ptr<SkUnicode> fUnicode;
 };
 }  // namespace textlayout
 }  // namespace skia
diff --git a/modules/skparagraph/src/ParagraphStyle.cpp b/modules/skparagraph/src/ParagraphStyle.cpp
index c40372d..4e3094f 100644
--- a/modules/skparagraph/src/ParagraphStyle.cpp
+++ b/modules/skparagraph/src/ParagraphStyle.cpp
@@ -2,7 +2,9 @@
 
 #include "modules/skparagraph/include/DartTypes.h"
 #include "modules/skparagraph/include/ParagraphStyle.h"
-#include "modules/skparagraph/src/ParagraphUtil.h"
+#include "modules/skshaper/src/SkUnicode.h"
+#include "src/core/SkStringUtils.h"
+#include "src/utils/SkUTF.h"
 
 namespace skia {
 namespace textlayout {
@@ -35,9 +37,5 @@
         return fTextAlign;
     }
 }
-
-void ParagraphStyle::setEllipsis(const std::u16string& ellipsis) {
-    fEllipsis = SkStringFromU16String(ellipsis);
-}
 }  // namespace textlayout
 }  // namespace skia
diff --git a/modules/skparagraph/src/ParagraphUtil.cpp b/modules/skparagraph/src/ParagraphUtil.cpp
deleted file mode 100644
index f7b78a7..0000000
--- a/modules/skparagraph/src/ParagraphUtil.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2019 Google LLC.
-
-#include "include/core/SkString.h"
-#include "include/core/SkTypes.h"
-#include "include/private/SkTo.h"
-#include "modules/skparagraph/src/ParagraphUtil.h"
-#include "src/utils/SkUTF.h"
-
-#include <unicode/umachine.h>
-#include <unicode/uchar.h>
-#include <unicode/ustring.h>
-#include <unicode/utypes.h>
-#include <string>
-
-namespace skia {
-namespace textlayout {
-
-SkString SkStringFromU16String(const std::u16string& utf16text) {
-    SkString dst;
-    UErrorCode status = U_ZERO_ERROR;
-    int32_t dstSize;
-    // Getting the length like this seems to always set U_BUFFER_OVERFLOW_ERROR
-    u_strToUTF8(nullptr, 0, &dstSize, (UChar*)utf16text.data(), SkToS32(utf16text.size()), &status);
-    dst.resize(dstSize);
-    status = U_ZERO_ERROR;
-    u_strToUTF8(dst.writable_str(), dst.size(), nullptr,
-                (UChar*)utf16text.data(), SkToS32(utf16text.size()), &status);
-    if (U_FAILURE(status)) {
-        SkDEBUGF("Invalid UTF-16 input: %s", u_errorName(status));
-        return dst;
-    }
-    return dst;
-}
-
-SkUnichar nextUtf8Unit(const char** ptr, const char* end) {
-    SkUnichar val = SkUTF::NextUTF8(ptr, end);
-    return val < 0 ? 0xFFFD : val;
-}
-
-bool isControl(SkUnichar utf8) {
-    return u_iscntrl(utf8);
-}
-
-bool isWhitespace(SkUnichar utf8) {
-    return u_isWhitespace(utf8);
-}
-
-}  // namespace textlayout
-}  // namespace skia
diff --git a/modules/skparagraph/src/ParagraphUtil.h b/modules/skparagraph/src/ParagraphUtil.h
deleted file mode 100644
index 3b6432f..0000000
--- a/modules/skparagraph/src/ParagraphUtil.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2020 Google LLC.
-#ifndef ParagraphUtil_DEFINED
-#define ParagraphUtil_DEFINED
-
-#include "include/core/SkString.h"
-#include <string>
-
-namespace skia {
-namespace textlayout {
-SkString SkStringFromU16String(const std::u16string& utf16text);
-SkUnichar nextUtf8Unit(const char** ptr, const char* end);
-bool isControl(SkUnichar utf8);
-bool isWhitespace(SkUnichar utf8);
-}  // namespace textlayout
-}  // namespace skia
-
-#endif
diff --git a/modules/skparagraph/src/TextLine.cpp b/modules/skparagraph/src/TextLine.cpp
index 1eddb93..b30651a 100644
--- a/modules/skparagraph/src/TextLine.cpp
+++ b/modules/skparagraph/src/TextLine.cpp
@@ -144,7 +144,7 @@
     SkAutoSTArray<kPreallocCount, int32_t> logicalOrder(numRuns);
 
     // TODO: hide all these logic in SkUnicode?
-    fOwner->getICU()->reorderVisual(runLevels.data(), numRuns, logicalOrder.data());
+  fOwner->getUnicode()->reorderVisual(runLevels.data(), numRuns, logicalOrder.data());
     auto firstRunIndex = start.runIndex();
     for (auto index : logicalOrder) {
         fRunsInVisualOrder.push_back(firstRunIndex + index);
diff --git a/modules/skparagraph/src/TextWrapper.cpp b/modules/skparagraph/src/TextWrapper.cpp
index 747b2ffe..7f7b73a 100644
--- a/modules/skparagraph/src/TextWrapper.cpp
+++ b/modules/skparagraph/src/TextWrapper.cpp
@@ -224,11 +224,10 @@
         return;
     }
     auto maxLines = parent->paragraphStyle().getMaxLines();
-    auto& ellipsisStr = parent->paragraphStyle().getEllipsis();
     auto align = parent->paragraphStyle().effective_align();
     auto unlimitedLines = maxLines == std::numeric_limits<size_t>::max();
     auto endlessLine = !SkScalarIsFinite(maxWidth);
-    auto hasEllipsis = !ellipsisStr.isEmpty();
+    auto hasEllipsis = parent->paragraphStyle().ellipsized();
 
     SkScalar softLineMaxIntrinsicWidth = 0;
     fEndLine = TextStretch(span.begin(), span.begin(), parent->strutForceHeight());
diff --git a/modules/skshaper/BUILD.gn b/modules/skshaper/BUILD.gn
index 5acd981..57160c1 100644
--- a/modules/skshaper/BUILD.gn
+++ b/modules/skshaper/BUILD.gn
@@ -4,10 +4,7 @@
 # found in the LICENSE file.
 
 import("../../gn/skia.gni")
-
-declare_args() {
-  skia_enable_skshaper = true
-}
+import("skshaper.gni")
 
 if (skia_enable_skshaper) {
   config("public_config") {
@@ -27,7 +24,6 @@
 
   component("skshaper") {
     check_includes = false
-    import("skshaper.gni")
     public_configs = [ ":public_config" ]
     public = skia_shaper_public
     deps = [ "../..:skia" ]
diff --git a/modules/skshaper/skshaper.gni b/modules/skshaper/skshaper.gni
index 0618b63..27c9624 100644
--- a/modules/skshaper/skshaper.gni
+++ b/modules/skshaper/skshaper.gni
@@ -19,3 +19,7 @@
 ]
 skia_shaper_harfbuzz_sources = [ "$_src/SkShaper_harfbuzz.cpp" ]
 skia_shaper_coretext_sources = [ "$_src/SkShaper_coretext.cpp" ]
+
+declare_args() {
+  skia_enable_skshaper = true
+}
diff --git a/modules/skshaper/src/SkUnicode.h b/modules/skshaper/src/SkUnicode.h
index 3bbb02e..5414ed2 100644
--- a/modules/skshaper/src/SkUnicode.h
+++ b/modules/skshaper/src/SkUnicode.h
@@ -9,6 +9,7 @@
 
 #include "include/core/SkTypes.h"
 #include "src/core/SkSpan.h"
+#include "src/utils/SkUTF.h"
 #include <vector>
 
 #if !defined(SKUNICODE_IMPLEMENTATION)
@@ -89,6 +90,10 @@
 
         virtual ~SkUnicode() = default;
 
+        virtual bool isControl(SkUnichar utf8) = 0;
+        virtual bool isWhitespace(SkUnichar utf8) = 0;
+        virtual SkString convertUtf16ToUtf8(const std::u16string& utf16) = 0;
+
         // Iterators (used in SkShaper)
         virtual std::unique_ptr<SkBidiIterator> makeBidiIterator
             (const uint16_t text[], int count, SkBidiIterator::Direction) = 0;
diff --git a/modules/skshaper/src/SkUnicode_icu.cpp b/modules/skshaper/src/SkUnicode_icu.cpp
index 1a859fe..13de14d 100644
--- a/modules/skshaper/src/SkUnicode_icu.cpp
+++ b/modules/skshaper/src/SkUnicode_icu.cpp
@@ -4,17 +4,23 @@
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
+#include "include/core/SkString.h"
 #include "include/private/SkTFitsIn.h"
 #include "include/private/SkTemplates.h"
 #include "modules/skshaper/src/SkUnicode.h"
 #include "src/utils/SkUTF.h"
 #include <unicode/ubidi.h>
 #include <unicode/ubrk.h>
+#include <unicode/ustring.h>
 #include <unicode/utext.h>
 #include <unicode/utypes.h>
 #include <vector>
 #include <functional>
 
+#if defined(SK_USING_THIRD_PARTY_ICU)
+#include "SkLoadICU.h"
+#endif
+
 using SkUnicodeBidi = std::unique_ptr<UBiDi, SkFunctionWrapper<decltype(ubidi_close), ubidi_close>>;
 using ICUUText = std::unique_ptr<UText, SkFunctionWrapper<decltype(utext_close), utext_close>>;
 using ICUBreakIterator = std::unique_ptr<UBreakIterator, SkFunctionWrapper<decltype(ubrk_close), ubrk_close>>;
@@ -125,23 +131,11 @@
         }
     }
 
-    static int convertUtf8ToUtf16(const char* utf8, size_t utf8Units, std::unique_ptr<uint16_t[]>* utf16) {
-        int utf16Units = SkUTF::UTF8ToUTF16(nullptr, 0, utf8, utf8Units);
-        if (utf16Units < 0) {
-            SkDEBUGF("Convert error: Invalid utf8 input");
-            return utf16Units;
-        }
-        *utf16 = std::unique_ptr<uint16_t[]>(new uint16_t[utf16Units]);
-        SkDEBUGCODE(int dstLen =) SkUTF::UTF8ToUTF16(utf16->get(), utf16Units, utf8, utf8Units);
-        SkASSERT(dstLen == utf16Units);
-        return utf16Units;
-    }
-
     static bool extractBidi(const char utf8[], int utf8Units, TextDirection dir, std::vector<BidiRegion>* bidiRegions) {
 
         // Convert to UTF16 since for now bidi iterator only operates on utf16
         std::unique_ptr<uint16_t[]> utf16;
-        auto utf16Units = convertUtf8ToUtf16(utf8, utf8Units, &utf16);
+        auto utf16Units = utf8ToUtf16(utf8, utf8Units, &utf16);
         if (utf16Units < 0) {
             return false;
         }
@@ -276,6 +270,29 @@
         return true;
     }
 
+    static int utf8ToUtf16(const char* utf8, size_t utf8Units, std::unique_ptr<uint16_t[]>* utf16) {
+        int utf16Units = SkUTF::UTF8ToUTF16(nullptr, 0, utf8, utf8Units);
+        if (utf16Units < 0) {
+            SkDEBUGF("Convert error: Invalid utf8 input");
+            return utf16Units;
+        }
+        *utf16 = std::unique_ptr<uint16_t[]>(new uint16_t[utf16Units]);
+        SkDEBUGCODE(int dstLen =) SkUTF::UTF8ToUTF16(utf16->get(), utf16Units, utf8, utf8Units);
+        SkASSERT(dstLen == utf16Units);
+        return utf16Units;
+   }
+
+    static int utf16ToUtf8(const uint16_t* utf16, size_t utf16Units, std::unique_ptr<char[]>* utf8) {
+        int utf8Units = SkUTF::UTF16ToUTF8(nullptr, 0, utf16, utf16Units);
+        if (utf8Units < 0) {
+            SkDEBUGF("Convert error: Invalid utf16 input");
+            return utf8Units;
+        }
+        *utf8 = std::unique_ptr<char[]>(new char[utf8Units]);
+        SkDEBUGCODE(int dstLen =) SkUTF::UTF16ToUTF8(utf8->get(), utf8Units, utf16, utf16Units);
+        SkASSERT(dstLen == utf8Units);
+        return utf8Units;
+   }
 public:
     ~SkUnicode_icu() override { }
     std::unique_ptr<SkBidiIterator> makeBidiIterator(const uint16_t text[], int count,
@@ -287,6 +304,25 @@
         return SkBidiIterator_icu::makeBidiIterator(text, count, dir);
     }
 
+    // TODO: Use ICU data file to detect controls and whitespaces
+    bool isControl(SkUnichar utf8) override {
+        return u_iscntrl(utf8);
+    }
+
+    bool isWhitespace(SkUnichar utf8) override {
+        return u_isWhitespace(utf8);
+    }
+
+    SkString convertUtf16ToUtf8(const std::u16string& utf16) override {
+        std::unique_ptr<char[]> utf8;
+        auto utf8Units = SkUnicode_icu::utf16ToUtf8((uint16_t*)utf16.data(), utf16.size(), &utf8);
+        if (utf8Units >= 0) {
+            return SkString(utf8.get(), utf8Units);
+        } else {
+            return SkString();
+        }
+    }
+
     bool getBidiRegions(const char utf8[], int utf8Units, TextDirection dir, std::vector<BidiRegion>* results) override {
         return extractBidi(utf8, utf8Units, dir, results);
     }
@@ -305,7 +341,7 @@
 
         // Convert to UTF16 since we want the results in utf16
         std::unique_ptr<uint16_t[]> utf16;
-        auto utf16Units = convertUtf8ToUtf16(utf8, utf8Units, &utf16);
+        auto utf16Units = utf8ToUtf16(utf8, utf8Units, &utf16);
         if (utf16Units < 0) {
             return false;
         }
@@ -330,4 +366,12 @@
     }
 };
 
-std::unique_ptr<SkUnicode> SkUnicode::Make() { return std::make_unique<SkUnicode_icu>(); }
+std::unique_ptr<SkUnicode> SkUnicode::Make() {
+    #if defined(SK_USING_THIRD_PARTY_ICU)
+    if (!SkLoadICU()) {
+        SkDEBUGF("SkLoadICU() failed!\n");
+        return nullptr;
+    }
+    #endif
+    return std::make_unique<SkUnicode_icu>();
+}
diff --git a/src/utils/SkUTF.cpp b/src/utils/SkUTF.cpp
index ff17462..500b1ec 100644
--- a/src/utils/SkUTF.cpp
+++ b/src/utils/SkUTF.cpp
@@ -283,4 +283,28 @@
     return dstLength;
 }
 
+int SkUTF::UTF16ToUTF8(char dst[], int dstCapacity, const uint16_t src[], size_t srcLength) {
 
+    if (!dst) {
+        dstCapacity = 0;
+    }
+
+    int dstLength = 0;
+    const char* endDst = dst + dstCapacity;
+    for (size_t i = 0; i < srcLength; ++i) {
+        char utf8[SkUTF::kMaxBytesInUTF8Sequence];
+        size_t count = ToUTF8(src[i], utf8);
+        if (count == 0) {
+            return -1;
+        }
+        dstLength += count;
+        if (dst) {
+            const char* elems = utf8;
+            while (dst < endDst && count > 0) {
+                *dst++ = *elems++;
+                count -= 1;
+            }
+        }
+    }
+    return dstLength;
+}
diff --git a/src/utils/SkUTF.h b/src/utils/SkUTF.h
index eb1ee91..23344a4 100644
--- a/src/utils/SkUTF.h
+++ b/src/utils/SkUTF.h
@@ -6,6 +6,7 @@
 #include "include/core/SkTypes.h"
 #include <cstddef>
 #include <cstdint>
+#include <memory>
 
 typedef int32_t SkUnichar;
 
@@ -70,6 +71,12 @@
  */
 SK_SPI int UTF8ToUTF16(uint16_t dst[], int dstCapacity, const char src[], size_t srcByteLength);
 
+/** Returns the number of resulting UTF8 values needed to convert the src utf16 sequence.
+ *  If dst is not null, it is filled with the corresponding values up to its capacity.
+ *  If there is an error, -1 is returned and the dst[] buffer is undefined.
+ */
+SK_SPI int UTF16ToUTF8(char dst[], int dstCapacity, const uint16_t src[], size_t srcLength);
+
 }  // namespace SkUTF
 
 #endif  // SkUTF_DEFINED
diff --git a/third_party/icu/SkLoadICU.cpp b/third_party/icu/SkLoadICU.cpp
index b41af4e..5b3b153 100644
--- a/third_party/icu/SkLoadICU.cpp
+++ b/third_party/icu/SkLoadICU.cpp
@@ -25,7 +25,7 @@
     struct FCloseWrapper { void operator()(FILE* f) { fclose(f); } };
     std::unique_ptr<FILE, FCloseWrapper> stream(fopen(dataFile, "rb"));
     if (!stream) {
-        fprintf(stderr, "SkIcuLoader: datafile missing.\n");
+        fprintf(stderr, "SkIcuLoader: datafile missing: %s.\n", dataFile);
         return nullptr;
     }
     int fileno = _fileno(stream.get());