Adding locale

Change-Id: I4f118f37a1226f4259d0e5be3b6b557b38b3b316
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/222785
Commit-Queue: Julia Lavrova <jlavrova@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
This commit is contained in:
Julia Lavrova 2019-06-21 12:22:32 -04:00 committed by Skia Commit-Bot
parent 65eb084e78
commit 35f88226bb
12 changed files with 218 additions and 213 deletions

View File

@ -29,7 +29,7 @@ public:
sk_sp<SkTypeface> matchTypeface(const char familyName[], SkFontStyle fontStyle);
sk_sp<SkTypeface> matchDefaultTypeface(SkFontStyle fontStyle);
sk_sp<SkTypeface> defaultFallback(SkUnichar unicode, SkFontStyle fontStyle);
sk_sp<SkTypeface> defaultFallback(SkUnichar unicode, SkFontStyle fontStyle, SkString locale);
void disableFontFallback();
bool fontFallbackEnabled() { return fEnableFontFallback; }

View File

@ -18,12 +18,9 @@ skparagraph_public = [
skparagraph_sources = [
"$_src/FontCollection.cpp",
"$_src/FontIterator.h",
"$_src/FontIterator.cpp",
"$_src/FontResolver.h",
"$_src/FontResolver.cpp",
"$_src/TextLine.h",
"$_src/TextLine.cpp",
"$_src/Iterator.h",
"$_src/ParagraphBuilderImpl.h",
"$_src/ParagraphBuilderImpl.cpp",
"$_src/ParagraphImpl.h",
@ -31,6 +28,8 @@ skparagraph_sources = [
"$_src/ParagraphStyle.cpp",
"$_src/Run.h",
"$_src/Run.cpp",
"$_src/TextLine.h",
"$_src/TextLine.cpp",
"$_src/TextShadow.cpp",
"$_src/TextStyle.cpp",
"$_src/TextWrapper.h",

View File

@ -117,10 +117,13 @@ sk_sp<SkTypeface> FontCollection::matchDefaultTypeface(SkFontStyle fontStyle) {
return nullptr;
}
sk_sp<SkTypeface> FontCollection::defaultFallback(SkUnichar unicode, SkFontStyle fontStyle) {
sk_sp<SkTypeface> FontCollection::defaultFallback(SkUnichar unicode, SkFontStyle fontStyle, SkString locale) {
for (const auto& manager : this->getFontManagerOrder()) {
std::vector<const char*> bcp47;
if (!locale.isEmpty()) {
bcp47.push_back(locale.c_str());
}
sk_sp<SkTypeface> typeface(manager->matchFamilyStyleCharacter(
0, fontStyle, bcp47.data(), bcp47.size(), unicode));
if (typeface != nullptr) {

View File

@ -1,68 +0,0 @@
// Copyright 2019 Google LLC.
#include "modules/skparagraph/src/FontIterator.h"
#include <unicode/brkiter.h>
#include <unicode/ubidi.h>
#include "include/core/SkBlurTypes.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkPictureRecorder.h"
#include "modules/skparagraph/src/ParagraphImpl.h"
#include "src/core/SkSpan.h"
#include "src/utils/SkUTF.h"
// TODO: FontCollection and FontIterator have common functionality
namespace skia {
namespace textlayout {
FontIterator::FontIterator(SkSpan<const char> utf8,
SkSpan<TextBlock> styles,
sk_sp<FontCollection> fonts)
: fText(utf8)
, fStyles(styles)
, fCurrentChar(utf8.begin())
, fFontResolver(std::move(fonts)) {
findAllFontsForAllStyledBlocks();
}
void FontIterator::consume() {
SkASSERT(fCurrentChar < fText.end());
auto found = fFontResolver.findFirst(fCurrentChar, &fFont, &fLineHeight);
SkASSERT(found);
// Move until we find the first character that cannot be resolved with the current font
while (++fCurrentChar != fText.end()) {
SkFont font;
SkScalar height;
found = fFontResolver.findNext(fCurrentChar, &font, &height);
if (found) {
if (fFont == font && fLineHeight == height) {
continue;
}
break;
}
}
}
void FontIterator::findAllFontsForAllStyledBlocks() {
TextBlock combined;
for (auto& block : fStyles) {
SkASSERT(combined.text().begin() == nullptr ||
combined.text().end() == block.text().begin());
if (combined.text().begin() != nullptr &&
block.style().matchOneAttribute(StyleType::kFont, combined.style())) {
combined.add(block.text());
continue;
}
if (!combined.text().empty()) {
fFontResolver.findAllFontsForStyledBlock(combined.style(), combined.text());
}
combined = block;
}
fFontResolver.findAllFontsForStyledBlock(combined.style(), combined.text());
}
} // namespace textlayout
} // namespace skia

View File

@ -1,52 +0,0 @@
// Copyright 2019 Google LLC.
#ifndef FontIterator_DEFINED
#define FontIterator_DEFINED
#include <unicode/brkiter.h>
#include <unicode/ubidi.h>
#include "include/core/SkBlurTypes.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkPictureRecorder.h"
#include "modules/skparagraph/src/FontResolver.h"
#include "modules/skparagraph/src/ParagraphImpl.h"
#include "src/core/SkSpan.h"
#include "src/utils/SkUTF.h"
namespace skia {
namespace textlayout {
class FontIterator final : public SkShaper::FontRunIterator {
public:
FontIterator(SkSpan<const char> utf8,
SkSpan<TextBlock> styles,
sk_sp<FontCollection> fonts);
void consume() override;
size_t endOfCurrentRun() const override { return fCurrentChar - fText.begin(); }
bool atEnd() const override { return fCurrentChar == fText.end(); }
const SkFont& currentFont() const override { return fFont; }
SkScalar lineHeight() const { return fLineHeight; }
private:
struct Hash {
uint32_t operator()(const std::pair<SkFont, SkScalar>& key) const {
return SkTypeface::UniqueID(key.first.getTypeface()) +
SkScalarCeilToInt(key.first.getSize()) + SkScalarCeilToInt(key.second);
}
};
void findAllFontsForAllStyledBlocks();
SkSpan<const char> fText;
SkSpan<TextBlock> fStyles;
const char* fCurrentChar;
SkFont fFont;
SkScalar fLineHeight;
FontResolver fFontResolver;
};
} // namespace textlayout
} // namespace skia
#endif // FontIterator_DEFINED

View File

@ -22,9 +22,6 @@ SkUnichar utf8_next(const char** ptr, const char* end) {
namespace skia {
namespace textlayout {
FontResolver::FontResolver(sk_sp<FontCollection> fontCollection)
: fFontCollection(fontCollection) {}
bool FontResolver::findFirst(const char* codepoint, SkFont* font, SkScalar* height) {
auto found = fFontMapping.find(codepoint);
if (found == nullptr) {
@ -34,8 +31,8 @@ bool FontResolver::findFirst(const char* codepoint, SkFont* font, SkScalar* heig
if (found == nullptr) {
return false;
}
*font = found->first;
*height = found->second;
*font = found->fFont;
*height = found->fHeight;
return true;
}
@ -44,8 +41,8 @@ bool FontResolver::findNext(const char* codepoint, SkFont* font, SkScalar* heigh
if (found == nullptr) {
return false;
}
*font = found->first;
*height = found->second;
*font = found->fFont;
*height = found->fHeight;
return true;
}
@ -93,7 +90,7 @@ void FontResolver::findAllFontsForStyledBlock(const TextStyle& style, SkSpan<con
if (fUnresolved > 0 && fFontCollection->fontFallbackEnabled()) {
while (fUnresolved > 0) {
auto unicode = firstUnresolved();
auto typeface = fFontCollection->defaultFallback(unicode, style.getFontStyle());
auto typeface = fFontCollection->defaultFallback(unicode, style.getFontStyle(), style.getLocale());
if (typeface == nullptr) {
break;
}
@ -110,11 +107,12 @@ void FontResolver::findAllFontsForStyledBlock(const TextStyle& style, SkSpan<con
// In case something still unresolved
if (fResolvedFonts.count() == 0) {
makeFont(fFontCollection->defaultFallback(firstUnresolved(), style.getFontStyle()),
style.getFontSize(), style.getHeight());
if (fFirstResolvedFont.first.getTypeface() != nullptr) {
makeFont(fFontCollection->defaultFallback(firstUnresolved(), style.getFontStyle(), style.getLocale()),
style.getFontSize(),
style.getHeight());
if (fFirstResolvedFont.fFont.getTypeface() != nullptr) {
SkString name;
fFirstResolvedFont.first.getTypeface()->getFamilyName(&name);
fFirstResolvedFont.fFont.getTypeface()->getFamilyName(&name);
SkDebugf("Urgent font resolution: %s\n", name.c_str());
} else {
SkDebugf("No font!!!\n");
@ -122,11 +120,11 @@ void FontResolver::findAllFontsForStyledBlock(const TextStyle& style, SkSpan<con
}
}
size_t FontResolver::resolveAllCharactersByFont(std::pair<SkFont, SkScalar> font) {
size_t FontResolver::resolveAllCharactersByFont(const FontDescr& font) {
// Consolidate all unresolved unicodes in one array to make a batch call
SkTArray<SkGlyphID> glyphs(fUnresolved);
glyphs.push_back_n(fUnresolved, SkGlyphID(0));
font.first.getTypeface()->unicharsToGlyphs(
font.fFont.getTypeface()->unicharsToGlyphs(
fUnresolved == fCodepoints.size() ? fCodepoints.data() : fUnresolvedCodepoints.data(),
fUnresolved, glyphs.data());
@ -209,24 +207,23 @@ void FontResolver::addResolvedWhitespacesToMapping() {
fUnresolved -= resolvedWhitespaces;
}
std::pair<SkFont, SkScalar> FontResolver::makeFont(sk_sp<SkTypeface> typeface,
SkScalar size,
SkScalar height) {
FontResolver::FontDescr FontResolver::makeFont(sk_sp<SkTypeface> typeface,
SkScalar size,
SkScalar height) {
SkFont font(typeface, size);
font.setEdging(SkFont::Edging::kAntiAlias);
font.setHinting(SkFontHinting::kSlight);
font.setSubpixel(true);
auto pair = std::make_pair(font, height);
FontDescr descr(font, height);
auto foundFont = fResolvedFonts.find(pair);
const FontDescr* foundFont = fResolvedFonts.find(descr);
if (foundFont == nullptr) {
if (fResolvedFonts.count() == 0) {
fFirstResolvedFont = pair;
fFirstResolvedFont = descr;
}
fResolvedFonts.add(pair);
fResolvedFonts.add(descr);
}
return pair;
return descr;
}
SkUnichar FontResolver::firstUnresolved() {
@ -236,5 +233,31 @@ SkUnichar FontResolver::firstUnresolved() {
auto index = firstTry ? 0 : fUnresolvedIndexes[0];
return fCodepoints[index];
}
void FontResolver::findAllFontsForAllStyledBlocks(SkSpan<const char> utf8,
SkSpan<TextBlock> styles,
sk_sp<FontCollection> fontCollection) {
fFontCollection = fontCollection;
fStyles = styles;
fText = utf8;
TextBlock combined;
for (auto& block : fStyles) {
SkASSERT(combined.text().begin() == nullptr ||
combined.text().end() == block.text().begin());
if (combined.text().begin() != nullptr &&
block.style().matchOneAttribute(StyleType::kFont, combined.style())) {
combined.add(block.text());
continue;
}
if (!combined.text().empty()) {
this->findAllFontsForStyledBlock(combined.style(), combined.text());
}
combined = block;
}
this->findAllFontsForStyledBlock(combined.style(), combined.text());
}
} // namespace textlayout
} // namespace skia

View File

@ -2,54 +2,70 @@
#ifndef FontResolver_DEFINED
#define FontResolver_DEFINED
#include "src/core/SkSpan.h"
#include <memory>
#include <set>
#include "modules/skparagraph/src/TextLine.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkRefCnt.h"
#include "include/private/SkTHash.h"
#include "modules/skparagraph/include/FontCollection.h"
#include "modules/skparagraph/include/TextStyle.h"
#include "src/core/SkSpan.h"
namespace skia {
namespace textlayout {
class FontResolver {
public:
FontResolver(sk_sp<FontCollection> fontCollection);
struct FontDescr {
FontDescr() {}
FontDescr(SkFont font, SkScalar height)
: fFont(font), fHeight(height) {}
bool operator==(const FontDescr& a) const {
return this->fFont == a.fFont && this->fHeight == a.fHeight;
}
SkFont fFont;
SkScalar fHeight;
};
FontResolver() = default;
~FontResolver() = default;
void findAllFontsForStyledBlock(const TextStyle& style, SkSpan<const char> text);
void findAllFontsForAllStyledBlocks(SkSpan<const char> utf8,
SkSpan<TextBlock> styles,
sk_sp<FontCollection> fontCollection);
bool findFirst(const char* codepoint, SkFont* font, SkScalar* height);
bool findNext(const char* codepoint, SkFont* font, SkScalar* height);
private:
std::pair<SkFont, SkScalar> makeFont(sk_sp<SkTypeface> typeface, SkScalar size,
SkScalar height);
size_t resolveAllCharactersByFont(std::pair<SkFont, SkScalar> font);
void findAllFontsForStyledBlock(const TextStyle& style, SkSpan<const char> text);
FontDescr makeFont(sk_sp<SkTypeface> typeface, SkScalar size, SkScalar height);
size_t resolveAllCharactersByFont(const FontDescr& fontDescr);
void addResolvedWhitespacesToMapping();
struct Hash {
uint32_t operator()(const std::pair<SkFont, SkScalar>& key) const {
return SkTypeface::UniqueID(key.first.getTypeface()) +
SkScalarCeilToInt(key.first.getSize()) + SkScalarCeilToInt(key.second);
uint32_t operator()(const FontDescr& key) const {
return SkTypeface::UniqueID(key.fFont.getTypeface()) +
SkScalarCeilToInt(key.fFont.getSize()) +
SkScalarCeilToInt(key.fHeight);
}
};
SkUnichar firstUnresolved();
sk_sp<FontCollection> fFontCollection;
SkSpan<const char> fText;
SkSpan<TextBlock> fStyles;
SkTHashMap<const char*, std::pair<SkFont, SkScalar>> fFontMapping;
SkTHashSet<std::pair<SkFont, SkScalar>, Hash> fResolvedFonts;
std::pair<SkFont, SkScalar> fFirstResolvedFont;
SkTHashMap<const char*, FontDescr> fFontMapping;
SkTHashSet<FontDescr, Hash> fResolvedFonts;
FontDescr fFirstResolvedFont;
SkTArray<SkUnichar> fCodepoints;
SkTArray<const char*> fCharacters;
SkTArray<size_t> fUnresolvedIndexes;
SkTArray<SkUnichar> fUnresolvedCodepoints;
SkTHashMap<size_t, std::pair<SkFont, SkScalar>> fWhitespaces;
SkTHashMap<size_t, FontDescr> fWhitespaces;
size_t fUnresolved;
};
} // namespace textlayout

View File

@ -0,0 +1,100 @@
// Copyright 2019 Google LLC.
#ifndef FontIterator_DEFINED
#define FontIterator_DEFINED
#include <unicode/brkiter.h>
#include <unicode/ubidi.h>
#include "include/core/SkBlurTypes.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkPictureRecorder.h"
#include "modules/skparagraph/src/FontResolver.h"
#include "modules/skparagraph/src/ParagraphImpl.h"
#include "src/core/SkSpan.h"
#include "src/utils/SkUTF.h"
namespace skia {
namespace textlayout {
class FontIterator final : public SkShaper::FontRunIterator {
public:
FontIterator(SkSpan<const char> utf8, FontResolver* fontResolver)
: fText(utf8), fCurrentChar(utf8.begin()), fFontResolver(fontResolver) { }
void consume() override {
SkASSERT(fCurrentChar < fText.end());
SkString locale;
auto found = fFontResolver->findFirst(fCurrentChar, &fFont, &fLineHeight);
SkASSERT(found);
// Move until we find the first character that cannot be resolved with the current font
while (++fCurrentChar != fText.end()) {
SkFont font;
SkScalar height;
SkString locale;
found = fFontResolver->findNext(fCurrentChar, &font, &height);
if (found) {
if (fFont == font && fLineHeight == height) {
continue;
}
break;
}
}
}
size_t endOfCurrentRun() const override { return fCurrentChar - fText.begin(); }
bool atEnd() const override { return fCurrentChar == fText.end(); }
const SkFont& currentFont() const override { return fFont; }
SkScalar currentLineHeight() const { return fLineHeight; }
private:
SkSpan<const char> fText;
const char* fCurrentChar;
SkFont fFont;
SkScalar fLineHeight;
FontResolver* fFontResolver;
};
class LangIterator final : public SkShaper::LanguageRunIterator {
public:
LangIterator(SkSpan<const char> utf8, SkSpan<TextBlock> styles, TextStyle defaultStyle)
: fText(utf8)
, fTextStyles(styles)
, fCurrentChar(utf8.begin())
, fCurrentStyle(fTextStyles.begin())
, fCurrentLocale(defaultStyle.getLocale()) {}
void consume() override {
SkASSERT(fCurrentChar < fText.end());
if (fCurrentStyle == fTextStyles.end()) {
fCurrentChar = fText.end();
return;
}
fCurrentChar = fCurrentStyle->text().end();
fCurrentLocale = fCurrentStyle->style().getLocale();
while (++fCurrentStyle != fTextStyles.end()) {
if (fCurrentStyle->style().getLocale() != fCurrentLocale) {
break;
}
fCurrentChar = fCurrentStyle->text().end();
}
}
size_t endOfCurrentRun() const override { return fCurrentChar - fText.begin(); }
bool atEnd() const override { return fCurrentChar == fText.end(); }
const char* currentLanguage() const override { return fCurrentLocale.c_str(); }
private:
SkSpan<const char> fText;
SkSpan<TextBlock> fTextStyles;
const char* fCurrentChar;
TextBlock* fCurrentStyle;
SkString fCurrentLocale;
};
} // namespace textlayout
} // namespace skia
#endif // FontIterator_DEFINED

View File

@ -7,7 +7,7 @@
#include "include/core/SkCanvas.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkPictureRecorder.h"
#include "modules/skparagraph/src/FontIterator.h"
#include "modules/skparagraph/src/Iterators.h"
#include "modules/skparagraph/src/Run.h"
#include "modules/skparagraph/src/TextWrapper.h"
#include "src/core/SkSpan.h"
@ -85,6 +85,24 @@ private:
namespace skia {
namespace textlayout {
ParagraphImpl::ParagraphImpl(const SkString& text,
ParagraphStyle style,
std::vector<Block> blocks,
sk_sp<FontCollection> fonts)
: Paragraph(std::move(style), std::move(fonts))
, fText(text)
, fTextSpan(fText.c_str(), fText.size())
, fDirtyLayout(true)
, fOldWidth(0)
, fPicture(nullptr) {
fTextStyles.reserve(blocks.size());
for (auto& block : blocks) {
fTextStyles.emplace_back(
SkSpan<const char>(fTextSpan.begin() + block.fStart, block.fEnd - block.fStart),
block.fStyle);
}
}
ParagraphImpl::ParagraphImpl(const std::u16string& utf16text,
ParagraphStyle style,
std::vector<Block> blocks,
@ -124,11 +142,10 @@ void ParagraphImpl::layout(SkScalar width) {
if (fRuns.empty()) {
fClusters.reset();
if (!this->shapeTextIntoEndlessLine()) {
// Apply the last style to the empty text
FontIterator font(SkMakeSpan(" "),
SkSpan<TextBlock>(&fTextStyles.back(), 1),
fFontCollection);
FontIterator font(SkMakeSpan(" "), &fFontResolver);
// Get the font metrics
font.consume();
LineMetrics lineMetrics(font.currentFont());
@ -311,7 +328,7 @@ bool ParagraphImpl::shapeTextIntoEndlessLine() {
TRACE_EVENT0("skia", TRACE_FUNC);
auto& run = fParagraph->fRuns.emplace_back(fParagraph->text(),
info,
fFontIterator->lineHeight(),
fFontIterator->currentLineHeight(),
fParagraph->fRuns.count(),
fAdvance.fX);
return run.newRunBuffer();
@ -340,8 +357,11 @@ bool ParagraphImpl::shapeTextIntoEndlessLine() {
return false;
}
SkSpan<TextBlock> styles(fTextStyles.begin(), fTextStyles.size());
FontIterator font(fTextSpan, styles, fFontCollection);
// This is a pretty big step - resolving all characters against all given fonts
fFontResolver.findAllFontsForAllStyledBlocks(fTextSpan, styles(), fFontCollection);
LangIterator lang(fTextSpan, styles(), paragraphStyle().getTextStyle());
FontIterator font(fTextSpan, &fFontResolver);
ShapeHandler handler(*this, &font);
std::unique_ptr<SkShaper> shaper = SkShaper::MakeShapeDontWrapOrReorder();
SkASSERT_RELEASE(shaper != nullptr);
@ -352,9 +372,8 @@ bool ParagraphImpl::shapeTextIntoEndlessLine() {
return false;
}
auto script = SkShaper::MakeHbIcuScriptRunIterator(fTextSpan.begin(), fTextSpan.size());
auto lang = SkShaper::MakeStdLanguageRunIterator(fTextSpan.begin(), fTextSpan.size());
shaper->shape(fTextSpan.begin(), fTextSpan.size(), font, *bidi, *script, *lang,
shaper->shape(fTextSpan.begin(), fTextSpan.size(), font, *bidi, *script, lang,
std::numeric_limits<SkScalar>::max(), &handler);
return true;
}

View File

@ -2,6 +2,7 @@
#ifndef ParagraphImpl_DEFINED
#define ParagraphImpl_DEFINED
#include "FontResolver.h"
#include "include/core/SkPicture.h"
#include "include/private/SkTHash.h"
#include "modules/skparagraph/include/Paragraph.h"
@ -28,20 +29,7 @@ public:
ParagraphImpl(const SkString& text,
ParagraphStyle style,
std::vector<Block> blocks,
sk_sp<FontCollection> fonts)
: Paragraph(std::move(style), std::move(fonts))
, fText(text)
, fTextSpan(fText.c_str(), fText.size())
, fDirtyLayout(true)
, fOldWidth(0)
, fPicture(nullptr) {
fTextStyles.reserve(blocks.size());
for (auto& block : blocks) {
fTextStyles.emplace_back(
SkSpan<const char>(fTextSpan.begin() + block.fStart, block.fEnd - block.fStart),
block.fStyle);
}
}
sk_sp<FontCollection> fonts);
ParagraphImpl(const std::u16string& utf16text,
ParagraphStyle style,
@ -107,6 +95,7 @@ private:
SkTArray<Cluster, true> fClusters;
SkTArray<TextLine> fLines;
LineMetrics fStrutMetrics;
FontResolver fFontResolver;
bool fDirtyLayout;
SkScalar fOldWidth;

View File

@ -23,6 +23,7 @@ TextStyle::TextStyle() : fFontStyle() {
fHasBackground = false;
fHasForeground = false;
fTextBaseline = TextBaseline::kAlphabetic;
fLocale = "";
}
bool TextStyle::equals(const TextStyle& other) const {

View File

@ -1220,7 +1220,6 @@ protected:
void onDrawContent(SkCanvas* canvas) override {
canvas->drawColor(SK_ColorWHITE);
/*
const char* text =
"// Create a raised button.\n"
"RaisedButton(\n"
@ -1254,7 +1253,7 @@ protected:
TextStyle text_style;
text_style.setFontFamilies({});
text_style.setColor(SK_ColorBLACK);
text_style.setFontSize(20);
text_style.setFontSize(50);
builder.pushStyle(text_style);
builder.addText(text);
builder.pop();
@ -1268,31 +1267,7 @@ protected:
paint.setColor(SK_ColorLTGRAY);
canvas->drawRect(result[0].rect, paint);
paragraph->paint(canvas, 0, 0);
*/
std::vector<uint16_t> text;
for (uint16_t i = 0; i < 64; ++i) {
text.push_back(i % 5 == 0 ? ' ' : i);
}
std::u16string u16_text(text.data(), text.data() + text.size());
TextStyle default_style;
default_style.setFontFamilies({SkString("Roboto")});
ParagraphStyle paragraph_style;
paragraph_style.setTextStyle(default_style);
TextStyle text_style;
text_style.setFontFamilies({SkString("Roboto")});
text_style.setColor(SK_ColorBLACK);
auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str());
SkASSERT(fontCollection->fontsFound() != 0);
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
builder.pushStyle(text_style);
builder.addText(u16_text);
builder.pop();
auto paragraph = builder.Build();
size_t count = 10;
while (--count > 0) {
paragraph->layout(300);
paragraph->paint(canvas, 0, 0);
}
}
private: