1cb93a386Sopenharmony_ci// Copyright 2019 Google LLC.
2cb93a386Sopenharmony_ci#include "include/core/SkFontMetrics.h"
3cb93a386Sopenharmony_ci#include "include/core/SkTextBlob.h"
4cb93a386Sopenharmony_ci#include "include/private/SkFloatingPoint.h"
5cb93a386Sopenharmony_ci#include "include/private/SkMalloc.h"
6cb93a386Sopenharmony_ci#include "include/private/SkTo.h"
7cb93a386Sopenharmony_ci#include "modules/skparagraph/include/DartTypes.h"
8cb93a386Sopenharmony_ci#include "modules/skparagraph/include/TextStyle.h"
9cb93a386Sopenharmony_ci#include "modules/skparagraph/src/ParagraphImpl.h"
10cb93a386Sopenharmony_ci#include "modules/skparagraph/src/Run.h"
11cb93a386Sopenharmony_ci#include "modules/skshaper/include/SkShaper.h"
12cb93a386Sopenharmony_ci#include "src/utils/SkUTF.h"
13cb93a386Sopenharmony_ci
14cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT
15cb93a386Sopenharmony_ci#include "include/FontCollection.h"
16cb93a386Sopenharmony_ci#include "log.h"
17cb93a386Sopenharmony_ci#endif
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_cinamespace skia {
20cb93a386Sopenharmony_cinamespace textlayout {
21cb93a386Sopenharmony_ciconstexpr SkScalar PARAM_TWO = 2.0;
22cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT
23cb93a386Sopenharmony_ci// 1px font size "HarmonyOS Sans" metrics
24cb93a386Sopenharmony_ciconstexpr SkScalar DEFAULT_TOP = -1.056;
25cb93a386Sopenharmony_ciconstexpr SkScalar DEFAULT_BOTTOM = 0.271;
26cb93a386Sopenharmony_ciconstexpr SkScalar DEFAULT_ASCENT = -0.928;
27cb93a386Sopenharmony_ciconstexpr SkScalar DEFAULT_DESCENT = 0.244;
28cb93a386Sopenharmony_cistruct ScaleParam {
29cb93a386Sopenharmony_ci    SkScalar fontScale;
30cb93a386Sopenharmony_ci    SkScalar baselineShiftScale;
31cb93a386Sopenharmony_ci};
32cb93a386Sopenharmony_ci// unordered_map<familyName, ScaleParam>: compress <familyName> font height, shift font baseline.
33cb93a386Sopenharmony_ci// target font size = font size * ScaleParam.scale.
34cb93a386Sopenharmony_ci// target baseline = baseline - height * font size * ScaleParam.baselineShiftScale.
35cb93a386Sopenharmony_ciconst std::unordered_map<std::string, ScaleParam> FONT_FAMILY_COMPRESSION_CONFIG = {
36cb93a386Sopenharmony_ci    {"Noto Serif Tibetan", ScaleParam{ .fontScale = 0.79, .baselineShiftScale = 0.1 }},
37cb93a386Sopenharmony_ci    {"Noto Sans Tibetan", ScaleParam{ .fontScale = 0.79, .baselineShiftScale = 0.1 }},
38cb93a386Sopenharmony_ci};
39cb93a386Sopenharmony_ciconst std::unordered_map<std::string, ScaleParam> FONT_FAMILY_COMPRESSION_WITH_HEIGHT_ADAPTER_CONFIG = {
40cb93a386Sopenharmony_ci    {"Noto Serif Tibetan", ScaleParam{ .fontScale = 0.85, .baselineShiftScale = 0.11 }},
41cb93a386Sopenharmony_ci    {"Noto Sans Tibetan", ScaleParam{ .fontScale = 0.85, .baselineShiftScale = 0.11 }},
42cb93a386Sopenharmony_ci};
43cb93a386Sopenharmony_ciconst ScaleParam DEFAULT_SCALE_PARAM = ScaleParam{ .fontScale = 0, .baselineShiftScale = 0 };
44cb93a386Sopenharmony_cienum FontCompressionStatus {
45cb93a386Sopenharmony_ci    UNDEFINED, // undefined font, the typeface is null.
46cb93a386Sopenharmony_ci    SYSTEM,    // system font, need to be compressed.
47cb93a386Sopenharmony_ci    CUSTOM,    // custom font, doesn't need to be compressed.
48cb93a386Sopenharmony_ci};
49cb93a386Sopenharmony_ci// the font padding does not take effect for these font families.
50cb93a386Sopenharmony_ciconst std::unordered_set<std::string> FONT_PADDING_NOT_EFFECT_FAMILY = {
51cb93a386Sopenharmony_ci    "Harmony Clock_01",
52cb93a386Sopenharmony_ci    "Harmony Clock_02",
53cb93a386Sopenharmony_ci    "Harmony Clock_03",
54cb93a386Sopenharmony_ci    "Harmony Clock_04",
55cb93a386Sopenharmony_ci    "Harmony Clock_05",
56cb93a386Sopenharmony_ci    "Harmony Clock_06",
57cb93a386Sopenharmony_ci    "Harmony Clock_07",
58cb93a386Sopenharmony_ci    "Harmony Clock_08",
59cb93a386Sopenharmony_ci// symbol: need to ensure "the symbol height = the font size".
60cb93a386Sopenharmony_ci// so the height compression is not enabled for symbol.
61cb93a386Sopenharmony_ci    "HM Symbol",
62cb93a386Sopenharmony_ci};
63cb93a386Sopenharmony_ci
64cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT
65cb93a386Sopenharmony_ciFontCompressionStatus getFontCompressionStatus(const RSFont& font)
66cb93a386Sopenharmony_ci{
67cb93a386Sopenharmony_ci    auto typeface = font.GetTypeface();
68cb93a386Sopenharmony_ci    if (typeface == nullptr) {
69cb93a386Sopenharmony_ci        return FontCompressionStatus::UNDEFINED;
70cb93a386Sopenharmony_ci    }
71cb93a386Sopenharmony_ci    return typeface->IsCustomTypeface() ? FontCompressionStatus::CUSTOM : FontCompressionStatus::SYSTEM;
72cb93a386Sopenharmony_ci}
73cb93a386Sopenharmony_cistd::string getFamilyNameFromFont(const RSFont& font)
74cb93a386Sopenharmony_ci{
75cb93a386Sopenharmony_ci    auto typeface = font.GetTypeface();
76cb93a386Sopenharmony_ci    return typeface == nullptr ? "" : typeface->GetFamilyName();
77cb93a386Sopenharmony_ci}
78cb93a386Sopenharmony_ci#else
79cb93a386Sopenharmony_ciFontCompressionStatus getFontCompressionStatus(const SkFont& font)
80cb93a386Sopenharmony_ci{
81cb93a386Sopenharmony_ci    auto typeface = font.refTypeface();
82cb93a386Sopenharmony_ci    if (typeface == nullptr) {
83cb93a386Sopenharmony_ci        return FontCompressionStatus::UNDEFINED;
84cb93a386Sopenharmony_ci    }
85cb93a386Sopenharmony_ci    return typeface->isCustomTypeface() ? FontCompressionStatus::CUSTOM : FontCompressionStatus::SYSTEM;
86cb93a386Sopenharmony_ci}
87cb93a386Sopenharmony_cistd::string getFamilyNameFromFont(const SkFont& font)
88cb93a386Sopenharmony_ci{
89cb93a386Sopenharmony_ci    auto typeface = font.refTypeface();
90cb93a386Sopenharmony_ci    if (typeface == nullptr) {
91cb93a386Sopenharmony_ci        return "";
92cb93a386Sopenharmony_ci    }
93cb93a386Sopenharmony_ci    SkString familyName;
94cb93a386Sopenharmony_ci    typeface->getFamilyName(&familyName);
95cb93a386Sopenharmony_ci    return std::string(familyName.c_str(), familyName.size());
96cb93a386Sopenharmony_ci}
97cb93a386Sopenharmony_ci#endif
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT
100cb93a386Sopenharmony_ciconst ScaleParam& findCompressionConfigWithFont(const RSFont& font)
101cb93a386Sopenharmony_ci#else
102cb93a386Sopenharmony_ciconst ScaleParam& findCompressionConfigWithFont(const SkFont& font)
103cb93a386Sopenharmony_ci#endif
104cb93a386Sopenharmony_ci{
105cb93a386Sopenharmony_ci    auto fontCompressionStatus = getFontCompressionStatus(font);
106cb93a386Sopenharmony_ci    if (fontCompressionStatus != FontCompressionStatus::SYSTEM) {
107cb93a386Sopenharmony_ci        return DEFAULT_SCALE_PARAM;
108cb93a386Sopenharmony_ci    }
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_ci    const auto& config = FontCollection::IsAdapterTextHeightEnabled() ?
111cb93a386Sopenharmony_ci        FONT_FAMILY_COMPRESSION_WITH_HEIGHT_ADAPTER_CONFIG : FONT_FAMILY_COMPRESSION_CONFIG;
112cb93a386Sopenharmony_ci    std::string familyName = getFamilyNameFromFont(font);
113cb93a386Sopenharmony_ci    auto iter = config.find(familyName);
114cb93a386Sopenharmony_ci    if (iter == config.end()) {
115cb93a386Sopenharmony_ci        return DEFAULT_SCALE_PARAM;
116cb93a386Sopenharmony_ci    }
117cb93a386Sopenharmony_ci    return iter->second;
118cb93a386Sopenharmony_ci}
119cb93a386Sopenharmony_ci
120cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT
121cb93a386Sopenharmony_civoid metricsIncludeFontPadding(RSFontMetrics* metrics, const RSFont& font)
122cb93a386Sopenharmony_ci#else
123cb93a386Sopenharmony_civoid metricsIncludeFontPadding(SkFontMetrics* metrics, const SkFont& font)
124cb93a386Sopenharmony_ci#endif
125cb93a386Sopenharmony_ci{
126cb93a386Sopenharmony_ci    if (metrics == nullptr) {
127cb93a386Sopenharmony_ci        return;
128cb93a386Sopenharmony_ci    }
129cb93a386Sopenharmony_ci    auto fontCompressionStatus = getFontCompressionStatus(font);
130cb93a386Sopenharmony_ci    if (fontCompressionStatus == FontCompressionStatus::UNDEFINED) {
131cb93a386Sopenharmony_ci        return;
132cb93a386Sopenharmony_ci    }
133cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT
134cb93a386Sopenharmony_ci    SkScalar fontSize = font.GetSize();
135cb93a386Sopenharmony_ci#else
136cb93a386Sopenharmony_ci    SkScalar fontSize = font.getSize();
137cb93a386Sopenharmony_ci#endif
138cb93a386Sopenharmony_ci    if (!FontCollection::IsAdapterTextHeightEnabled()) {
139cb93a386Sopenharmony_ci        if (fontCompressionStatus == FontCompressionStatus::SYSTEM &&
140cb93a386Sopenharmony_ci            !SkScalarNearlyZero(findCompressionConfigWithFont(font).fontScale)) {
141cb93a386Sopenharmony_ci            metrics->fAscent = DEFAULT_ASCENT * fontSize;
142cb93a386Sopenharmony_ci            metrics->fDescent = DEFAULT_DESCENT * fontSize;
143cb93a386Sopenharmony_ci        }
144cb93a386Sopenharmony_ci        return;
145cb93a386Sopenharmony_ci    }
146cb93a386Sopenharmony_ci
147cb93a386Sopenharmony_ci    std::string curFamilyName = getFamilyNameFromFont(font);
148cb93a386Sopenharmony_ci    auto setIter = FONT_PADDING_NOT_EFFECT_FAMILY.find(curFamilyName);
149cb93a386Sopenharmony_ci    if (setIter == FONT_PADDING_NOT_EFFECT_FAMILY.end()) {
150cb93a386Sopenharmony_ci        if (fontCompressionStatus == FontCompressionStatus::SYSTEM) {
151cb93a386Sopenharmony_ci            metrics->fAscent = DEFAULT_TOP * fontSize;
152cb93a386Sopenharmony_ci            metrics->fDescent = DEFAULT_BOTTOM * fontSize;
153cb93a386Sopenharmony_ci            return;
154cb93a386Sopenharmony_ci        }
155cb93a386Sopenharmony_ci        // use top and bottom as ascent and descent.
156cb93a386Sopenharmony_ci        // calculate height with top and bottom.(includeFontPadding)
157cb93a386Sopenharmony_ci        metrics->fAscent = metrics->fTop;
158cb93a386Sopenharmony_ci        metrics->fDescent = metrics->fBottom;
159cb93a386Sopenharmony_ci    }
160cb93a386Sopenharmony_ci}
161cb93a386Sopenharmony_ci
162cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT
163cb93a386Sopenharmony_civoid scaleFontWithCompressionConfig(RSFont& font, ScaleOP op)
164cb93a386Sopenharmony_ci{
165cb93a386Sopenharmony_ci    SkScalar fontSize = font.GetSize();
166cb93a386Sopenharmony_ci#else
167cb93a386Sopenharmony_civoid scaleFontWithCompressionConfig(SkFont& font, ScaleOP op)
168cb93a386Sopenharmony_ci{
169cb93a386Sopenharmony_ci    SkScalar fontSize = font.getSize();
170cb93a386Sopenharmony_ci#endif
171cb93a386Sopenharmony_ci    auto config = findCompressionConfigWithFont(font);
172cb93a386Sopenharmony_ci    if (SkScalarNearlyZero(config.fontScale)) {
173cb93a386Sopenharmony_ci        return;
174cb93a386Sopenharmony_ci    }
175cb93a386Sopenharmony_ci    switch (op) {
176cb93a386Sopenharmony_ci    case ScaleOP::COMPRESS:
177cb93a386Sopenharmony_ci        fontSize *= config.fontScale;
178cb93a386Sopenharmony_ci        break;
179cb93a386Sopenharmony_ci    case ScaleOP::DECOMPRESS:
180cb93a386Sopenharmony_ci        fontSize /= config.fontScale;
181cb93a386Sopenharmony_ci        break;
182cb93a386Sopenharmony_ci    default:
183cb93a386Sopenharmony_ci        return;
184cb93a386Sopenharmony_ci    }
185cb93a386Sopenharmony_ci#ifdef USE_SKIA_TXT
186cb93a386Sopenharmony_ci    font.SetSize(fontSize);
187cb93a386Sopenharmony_ci#else
188cb93a386Sopenharmony_ci    font.setSize(fontSize);
189cb93a386Sopenharmony_ci#endif
190cb93a386Sopenharmony_ci}
191cb93a386Sopenharmony_ci#endif
192cb93a386Sopenharmony_ci
193cb93a386Sopenharmony_ciRun::Run(ParagraphImpl* owner,
194cb93a386Sopenharmony_ci         const SkShaper::RunHandler::RunInfo& info,
195cb93a386Sopenharmony_ci         size_t firstChar,
196cb93a386Sopenharmony_ci         SkScalar heightMultiplier,
197cb93a386Sopenharmony_ci         bool useHalfLeading,
198cb93a386Sopenharmony_ci         SkScalar baselineShift,
199cb93a386Sopenharmony_ci         size_t index,
200cb93a386Sopenharmony_ci         SkScalar offsetX)
201cb93a386Sopenharmony_ci    : fOwner(owner)
202cb93a386Sopenharmony_ci    , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end())
203cb93a386Sopenharmony_ci    , fClusterRange(EMPTY_CLUSTERS)
204cb93a386Sopenharmony_ci    , fFont(info.fFont)
205cb93a386Sopenharmony_ci    , fClusterStart(firstChar)
206cb93a386Sopenharmony_ci    , fGlyphData(std::make_shared<GlyphData>())
207cb93a386Sopenharmony_ci    , fGlyphs(fGlyphData->glyphs)
208cb93a386Sopenharmony_ci    , fPositions(fGlyphData->positions)
209cb93a386Sopenharmony_ci    , fOffsets(fGlyphData->offsets)
210cb93a386Sopenharmony_ci    , fClusterIndexes(fGlyphData->clusterIndexes)
211cb93a386Sopenharmony_ci    , fHeightMultiplier(heightMultiplier)
212cb93a386Sopenharmony_ci    , fUseHalfLeading(useHalfLeading)
213cb93a386Sopenharmony_ci    , fBaselineShift(baselineShift)
214cb93a386Sopenharmony_ci{
215cb93a386Sopenharmony_ci    fBidiLevel = info.fBidiLevel;
216cb93a386Sopenharmony_ci    fAdvance = info.fAdvance;
217cb93a386Sopenharmony_ci    fIndex = index;
218cb93a386Sopenharmony_ci    fUtf8Range = info.utf8Range;
219cb93a386Sopenharmony_ci    fOffset = SkVector::Make(offsetX, 0);
220cb93a386Sopenharmony_ci
221cb93a386Sopenharmony_ci    fGlyphs.push_back_n(info.glyphCount);
222cb93a386Sopenharmony_ci    fPositions.push_back_n(info.glyphCount + 1);
223cb93a386Sopenharmony_ci    fOffsets.push_back_n(info.glyphCount + 1);
224cb93a386Sopenharmony_ci    fClusterIndexes.push_back_n(info.glyphCount + 1);
225cb93a386Sopenharmony_ci    fHalfLetterspacings.push_back_n(info.glyphCount + 1);
226cb93a386Sopenharmony_ci    std::fill(fHalfLetterspacings.begin(), fHalfLetterspacings.end(), 0.0);
227cb93a386Sopenharmony_ci#ifndef USE_SKIA_TXT
228cb93a386Sopenharmony_ci    info.fFont.getMetrics(&fFontMetrics);
229cb93a386Sopenharmony_ci#else
230cb93a386Sopenharmony_ci    info.fFont.GetMetrics(&fFontMetrics);
231cb93a386Sopenharmony_ci#endif
232cb93a386Sopenharmony_ci
233cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT
234cb93a386Sopenharmony_ci    auto decompressFont = info.fFont;
235cb93a386Sopenharmony_ci    scaleFontWithCompressionConfig(decompressFont, ScaleOP::DECOMPRESS);
236cb93a386Sopenharmony_ci    metricsIncludeFontPadding(&fFontMetrics, decompressFont);
237cb93a386Sopenharmony_ci    auto config = findCompressionConfigWithFont(decompressFont);
238cb93a386Sopenharmony_ci    fCompressionBaselineShift = (fFontMetrics.fDescent - fFontMetrics.fAscent) * config.baselineShiftScale;
239cb93a386Sopenharmony_ci#endif
240cb93a386Sopenharmony_ci
241cb93a386Sopenharmony_ci    this->calculateMetrics();
242cb93a386Sopenharmony_ci
243cb93a386Sopenharmony_ci    // To make edge cases easier:
244cb93a386Sopenharmony_ci    fPositions[info.glyphCount] = fOffset + fAdvance;
245cb93a386Sopenharmony_ci    fOffsets[info.glyphCount] = {0, 0};
246cb93a386Sopenharmony_ci    fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
247cb93a386Sopenharmony_ci    fEllipsis = false;
248cb93a386Sopenharmony_ci    fPlaceholderIndex = std::numeric_limits<size_t>::max();
249cb93a386Sopenharmony_ci}
250cb93a386Sopenharmony_ci
251cb93a386Sopenharmony_civoid Run::calculateMetrics() {
252cb93a386Sopenharmony_ci    fCorrectAscent = fFontMetrics.fAscent - fFontMetrics.fLeading * 0.5;
253cb93a386Sopenharmony_ci    fCorrectDescent = fFontMetrics.fDescent + fFontMetrics.fLeading * 0.5;
254cb93a386Sopenharmony_ci    fCorrectLeading = 0;
255cb93a386Sopenharmony_ci    if (SkScalarNearlyZero(fHeightMultiplier)) {
256cb93a386Sopenharmony_ci        return;
257cb93a386Sopenharmony_ci    }
258cb93a386Sopenharmony_ci#ifndef USE_SKIA_TXT
259cb93a386Sopenharmony_ci    const auto runHeight = fHeightMultiplier * fFont.getSize();
260cb93a386Sopenharmony_ci#else
261cb93a386Sopenharmony_ci    const auto runHeight = fHeightMultiplier * fFont.GetSize();
262cb93a386Sopenharmony_ci#endif
263cb93a386Sopenharmony_ci    const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent;
264cb93a386Sopenharmony_ci    if (fUseHalfLeading) {
265cb93a386Sopenharmony_ci        const auto extraLeading = (runHeight - fontIntrinsicHeight) / 2;
266cb93a386Sopenharmony_ci        fCorrectAscent -= extraLeading;
267cb93a386Sopenharmony_ci        fCorrectDescent += extraLeading;
268cb93a386Sopenharmony_ci    } else {
269cb93a386Sopenharmony_ci        const auto multiplier = runHeight / fontIntrinsicHeight;
270cb93a386Sopenharmony_ci        fCorrectAscent *= multiplier;
271cb93a386Sopenharmony_ci        fCorrectDescent *= multiplier;
272cb93a386Sopenharmony_ci    }
273cb93a386Sopenharmony_ci    // If we shift the baseline we need to make sure the shifted text fits the line
274cb93a386Sopenharmony_ci    fCorrectAscent += fBaselineShift;
275cb93a386Sopenharmony_ci    fCorrectDescent += fBaselineShift;
276cb93a386Sopenharmony_ci}
277cb93a386Sopenharmony_ci
278cb93a386Sopenharmony_ciSkShaper::RunHandler::Buffer Run::newRunBuffer() {
279cb93a386Sopenharmony_ci    return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset};
280cb93a386Sopenharmony_ci}
281cb93a386Sopenharmony_ci
282cb93a386Sopenharmony_ci#ifndef USE_SKIA_TXT
283cb93a386Sopenharmony_civoid Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size) const {
284cb93a386Sopenharmony_ci    SkASSERT(pos + size <= this->size());
285cb93a386Sopenharmony_ci    const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
286cb93a386Sopenharmony_ci    sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
287cb93a386Sopenharmony_ci
288cb93a386Sopenharmony_ci    for (size_t i = 0; i < size; ++i) {
289cb93a386Sopenharmony_ci        auto point = fPositions[i + pos];
290cb93a386Sopenharmony_ci        if (!fJustificationShifts.empty()) {
291cb93a386Sopenharmony_ci            point.fX += fJustificationShifts[i + pos].fX;
292cb93a386Sopenharmony_ci        }
293cb93a386Sopenharmony_ci        if (!fAutoSpacings.empty()) {
294cb93a386Sopenharmony_ci            point.fX += fAutoSpacings[i + pos].fX;
295cb93a386Sopenharmony_ci        }
296cb93a386Sopenharmony_ci        point += fOffsets[i + pos];
297cb93a386Sopenharmony_ci        blobBuffer.points()[i] = point;
298cb93a386Sopenharmony_ci    }
299cb93a386Sopenharmony_ci}
300cb93a386Sopenharmony_ci#else
301cb93a386Sopenharmony_civoid Run::copyTo(RSTextBlobBuilder& builder, size_t pos, size_t size) const {
302cb93a386Sopenharmony_ci    SkASSERT(pos + size <= this->size());
303cb93a386Sopenharmony_ci    const auto& blobBuffer = builder.AllocRunPos(fFont, SkToInt(size));
304cb93a386Sopenharmony_ci    sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
305cb93a386Sopenharmony_ci    auto points = reinterpret_cast<SkPoint*>(blobBuffer.pos);
306cb93a386Sopenharmony_ci
307cb93a386Sopenharmony_ci    for (size_t i = 0; i < size; ++i) {
308cb93a386Sopenharmony_ci        auto point = fPositions[i + pos];
309cb93a386Sopenharmony_ci        if (!fJustificationShifts.empty()) {
310cb93a386Sopenharmony_ci            point.fX += fJustificationShifts[i + pos].fX;
311cb93a386Sopenharmony_ci        }
312cb93a386Sopenharmony_ci        if (!fAutoSpacings.empty()) {
313cb93a386Sopenharmony_ci            point.fX += fAutoSpacings[i + pos].fX;
314cb93a386Sopenharmony_ci        }
315cb93a386Sopenharmony_ci        point += fOffsets[i + pos];
316cb93a386Sopenharmony_ci        points[i] = point;
317cb93a386Sopenharmony_ci    }
318cb93a386Sopenharmony_ci}
319cb93a386Sopenharmony_ci
320cb93a386Sopenharmony_civoid Run::copyTo(RSTextBlobBuilder& builder,
321cb93a386Sopenharmony_ci                 const RSPath* path,
322cb93a386Sopenharmony_ci                 float hOffset,
323cb93a386Sopenharmony_ci                 float vOffset,
324cb93a386Sopenharmony_ci                 float fTextShift,
325cb93a386Sopenharmony_ci                 size_t pos,
326cb93a386Sopenharmony_ci                 size_t size) const {
327cb93a386Sopenharmony_ci    SkASSERT(pos + size <= this->size());
328cb93a386Sopenharmony_ci    auto& blobBuffer = builder.AllocRunRSXform(fFont, SkToInt(size));
329cb93a386Sopenharmony_ci    sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
330cb93a386Sopenharmony_ci    std::vector<float> widths(size);
331cb93a386Sopenharmony_ci    fFont.GetWidths(blobBuffer.glyphs, size, widths.data());
332cb93a386Sopenharmony_ci    RSXform* xform = reinterpret_cast<RSXform*>(blobBuffer.pos);
333cb93a386Sopenharmony_ci    for (size_t i = 0; i < size; ++i) {
334cb93a386Sopenharmony_ci        float halfWidth = widths[i + pos] * 0.5f;
335cb93a386Sopenharmony_ci        float x = hOffset + posX(i + pos) + halfWidth + fOffsets[i + pos].x() + fTextShift;
336cb93a386Sopenharmony_ci        if (!fJustificationShifts.empty()) {
337cb93a386Sopenharmony_ci            x += fJustificationShifts[i + pos].fX;
338cb93a386Sopenharmony_ci        }
339cb93a386Sopenharmony_ci        RSPoint rsPos;
340cb93a386Sopenharmony_ci        RSPoint rsTan;
341cb93a386Sopenharmony_ci        if (!path->GetPositionAndTangent(x, rsPos, rsTan, false)) {
342cb93a386Sopenharmony_ci            rsPos.Set(x, vOffset);
343cb93a386Sopenharmony_ci            rsTan.Set(1, 0);
344cb93a386Sopenharmony_ci        }
345cb93a386Sopenharmony_ci        xform[i].cos_ = rsTan.GetX();
346cb93a386Sopenharmony_ci        xform[i].sin_ = rsTan.GetY();
347cb93a386Sopenharmony_ci        xform[i].tx_ = rsPos.GetX() - rsTan.GetY() * vOffset - halfWidth * rsTan.GetX();
348cb93a386Sopenharmony_ci        xform[i].ty_ = rsPos.GetY() + rsTan.GetX() * vOffset - halfWidth * rsTan.GetY();
349cb93a386Sopenharmony_ci    }
350cb93a386Sopenharmony_ci}
351cb93a386Sopenharmony_ci#endif
352cb93a386Sopenharmony_ci
353cb93a386Sopenharmony_ci// Find a cluster range from text range (within one run)
354cb93a386Sopenharmony_ci// Cluster range is normalized ([start:end) start < end regardless of TextDirection
355cb93a386Sopenharmony_ci// Boolean value in triple indicates whether the cluster range was found or not
356cb93a386Sopenharmony_cistd::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text) const {
357cb93a386Sopenharmony_ci    if (text.width() == 0) {
358cb93a386Sopenharmony_ci        // Special Flutter case for "\n" and "...\n"
359cb93a386Sopenharmony_ci        if (text.end > this->fTextRange.start) {
360cb93a386Sopenharmony_ci            ClusterIndex index = fOwner->clusterIndex(text.end - 1);
361cb93a386Sopenharmony_ci            return std::make_tuple(true, index, index);
362cb93a386Sopenharmony_ci        } else {
363cb93a386Sopenharmony_ci            return std::make_tuple(false, 0, 0);
364cb93a386Sopenharmony_ci        }
365cb93a386Sopenharmony_ci    }
366cb93a386Sopenharmony_ci
367cb93a386Sopenharmony_ci    ClusterRange clusterRange;
368cb93a386Sopenharmony_ci    bool found = true;
369cb93a386Sopenharmony_ci    // Deal with the case when either start or end are not align with glyph cluster edge
370cb93a386Sopenharmony_ci    // In such case we shift the text range to the right
371cb93a386Sopenharmony_ci    // (cutting from the left and adding to the right)
372cb93a386Sopenharmony_ci    if (leftToRight()) {
373cb93a386Sopenharmony_ci        // LTR: [start:end)
374cb93a386Sopenharmony_ci        found = clusterRange.start != fClusterRange.end;
375cb93a386Sopenharmony_ci        clusterRange.start = fOwner->clusterIndex(text.start);
376cb93a386Sopenharmony_ci        clusterRange.end = fOwner->clusterIndex(text.end - 1);
377cb93a386Sopenharmony_ci    } else {
378cb93a386Sopenharmony_ci        // RTL: (start:end]
379cb93a386Sopenharmony_ci        clusterRange.start = fOwner->clusterIndex(text.end);
380cb93a386Sopenharmony_ci        clusterRange.end = fOwner->clusterIndex(text.start + 1);
381cb93a386Sopenharmony_ci        found = clusterRange.end != fClusterRange.start;
382cb93a386Sopenharmony_ci    }
383cb93a386Sopenharmony_ci
384cb93a386Sopenharmony_ci    return std::make_tuple(
385cb93a386Sopenharmony_ci            found,
386cb93a386Sopenharmony_ci            clusterRange.start,
387cb93a386Sopenharmony_ci            clusterRange.end);
388cb93a386Sopenharmony_ci}
389cb93a386Sopenharmony_ci
390cb93a386Sopenharmony_cistd::tuple<bool, TextIndex, TextIndex> Run::findLimitingGlyphClusters(TextRange text) const {
391cb93a386Sopenharmony_ci    TextIndex start = fOwner->findPreviousGlyphClusterBoundary(text.start);
392cb93a386Sopenharmony_ci    TextIndex end = fOwner->findNextGlyphClusterBoundary(text.end);
393cb93a386Sopenharmony_ci    return std::make_tuple(true, start, end);
394cb93a386Sopenharmony_ci}
395cb93a386Sopenharmony_ci
396cb93a386Sopenharmony_ci// Adjust the text to grapheme edges so the first grapheme start is in the text and the last grapheme start is in the text
397cb93a386Sopenharmony_ci// It actually means that the first grapheme is entirely in the text and the last grapheme does not have to be
398cb93a386Sopenharmony_ci// 12345 234 2:2 -> 2,5 4:4
399cb93a386Sopenharmony_cistd::tuple<bool, TextIndex, TextIndex> Run::findLimitingGraphemes(TextRange text) const {
400cb93a386Sopenharmony_ci    TextIndex start = fOwner->findPreviousGraphemeBoundary(text.start);
401cb93a386Sopenharmony_ci    TextIndex end = fOwner->findNextGraphemeBoundary(text.end);
402cb93a386Sopenharmony_ci    return std::make_tuple(true, start, end);
403cb93a386Sopenharmony_ci}
404cb93a386Sopenharmony_ci
405cb93a386Sopenharmony_civoid Run::iterateThroughClusters(const ClusterVisitor& visitor) {
406cb93a386Sopenharmony_ci
407cb93a386Sopenharmony_ci    for (size_t index = 0; index < fClusterRange.width(); ++index) {
408cb93a386Sopenharmony_ci        auto correctIndex = leftToRight() ? fClusterRange.start + index : fClusterRange.end - index - 1;
409cb93a386Sopenharmony_ci        auto cluster = &fOwner->cluster(correctIndex);
410cb93a386Sopenharmony_ci        visitor(cluster);
411cb93a386Sopenharmony_ci    }
412cb93a386Sopenharmony_ci}
413cb93a386Sopenharmony_ci
414cb93a386Sopenharmony_civoid Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
415cb93a386Sopenharmony_ci    // Increment the run width
416cb93a386Sopenharmony_ci    fAdvance.fX += space;
417cb93a386Sopenharmony_ci    // Increment the cluster width
418cb93a386Sopenharmony_ci    cluster->space(space);
419cb93a386Sopenharmony_ci}
420cb93a386Sopenharmony_ci
421cb93a386Sopenharmony_ciSkScalar Run::addSpacesEvenly(SkScalar space) {
422cb93a386Sopenharmony_ci    SkScalar shift = 0;
423cb93a386Sopenharmony_ci    if (this->size()) {
424cb93a386Sopenharmony_ci        shift += space / PARAM_TWO;
425cb93a386Sopenharmony_ci    }
426cb93a386Sopenharmony_ci    for (size_t i = 0; i < this->size(); ++i) {
427cb93a386Sopenharmony_ci        fPositions[i].fX += shift;
428cb93a386Sopenharmony_ci        fHalfLetterspacings[i] = space / PARAM_TWO;
429cb93a386Sopenharmony_ci        shift += space;
430cb93a386Sopenharmony_ci    }
431cb93a386Sopenharmony_ci    if (this->size()) {
432cb93a386Sopenharmony_ci        shift -= space / PARAM_TWO;
433cb93a386Sopenharmony_ci    }
434cb93a386Sopenharmony_ci    fPositions[this->size()].fX += shift;
435cb93a386Sopenharmony_ci    fAdvance.fX += shift;
436cb93a386Sopenharmony_ci    return shift;
437cb93a386Sopenharmony_ci}
438cb93a386Sopenharmony_ci
439cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT
440cb93a386Sopenharmony_ciSkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
441cb93a386Sopenharmony_ci    // Offset all the glyphs in the cluster
442cb93a386Sopenharmony_ci    SkScalar shift = 0;
443cb93a386Sopenharmony_ci    for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
444cb93a386Sopenharmony_ci        fPositions[i].fX += shift;
445cb93a386Sopenharmony_ci        fHalfLetterspacings[i] = space / PARAM_TWO;
446cb93a386Sopenharmony_ci        shift += space;
447cb93a386Sopenharmony_ci    }
448cb93a386Sopenharmony_ci    if (this->size() == cluster->endPos()) {
449cb93a386Sopenharmony_ci        // To make calculations easier
450cb93a386Sopenharmony_ci        fPositions[cluster->endPos()].fX += shift;
451cb93a386Sopenharmony_ci        fHalfLetterspacings[cluster->endPos()] = space / PARAM_TWO;
452cb93a386Sopenharmony_ci    }
453cb93a386Sopenharmony_ci    // Increment the run width
454cb93a386Sopenharmony_ci    fAdvance.fX += shift;
455cb93a386Sopenharmony_ci    // Increment the cluster width
456cb93a386Sopenharmony_ci    cluster->space(shift);
457cb93a386Sopenharmony_ci    cluster->setHalfLetterSpacing(space / PARAM_TWO);
458cb93a386Sopenharmony_ci
459cb93a386Sopenharmony_ci    return shift;
460cb93a386Sopenharmony_ci}
461cb93a386Sopenharmony_ci#else
462cb93a386Sopenharmony_ciSkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
463cb93a386Sopenharmony_ci    // Offset all the glyphs in the cluster
464cb93a386Sopenharmony_ci    SkScalar shift = 0;
465cb93a386Sopenharmony_ci    for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
466cb93a386Sopenharmony_ci        fPositions[i].fX += shift;
467cb93a386Sopenharmony_ci        shift += space;
468cb93a386Sopenharmony_ci    }
469cb93a386Sopenharmony_ci    if (this->size() == cluster->endPos()) {
470cb93a386Sopenharmony_ci        // To make calculations easier
471cb93a386Sopenharmony_ci        fPositions[cluster->endPos()].fX += shift;
472cb93a386Sopenharmony_ci    }
473cb93a386Sopenharmony_ci    // Increment the run width
474cb93a386Sopenharmony_ci    fAdvance.fX += shift;
475cb93a386Sopenharmony_ci    // Increment the cluster width
476cb93a386Sopenharmony_ci    cluster->space(shift);
477cb93a386Sopenharmony_ci    cluster->setHalfLetterSpacing(space / 2);
478cb93a386Sopenharmony_ci
479cb93a386Sopenharmony_ci    return shift;
480cb93a386Sopenharmony_ci}
481cb93a386Sopenharmony_ci#endif
482cb93a386Sopenharmony_ci
483cb93a386Sopenharmony_civoid Run::shift(const Cluster* cluster, SkScalar offset) {
484cb93a386Sopenharmony_ci    if (offset == 0) {
485cb93a386Sopenharmony_ci        return;
486cb93a386Sopenharmony_ci    }
487cb93a386Sopenharmony_ci
488cb93a386Sopenharmony_ci    for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
489cb93a386Sopenharmony_ci        fPositions[i].fX += offset;
490cb93a386Sopenharmony_ci    }
491cb93a386Sopenharmony_ci    if (this->size() == cluster->endPos()) {
492cb93a386Sopenharmony_ci        // To make calculations easier
493cb93a386Sopenharmony_ci        fPositions[cluster->endPos()].fX += offset;
494cb93a386Sopenharmony_ci    }
495cb93a386Sopenharmony_ci}
496cb93a386Sopenharmony_ci
497cb93a386Sopenharmony_ci#ifdef OHOS_SUPPORT
498cb93a386Sopenharmony_civoid Run::extendClusterWidth(Cluster* cluster, SkScalar space) {
499cb93a386Sopenharmony_ci    addSpacesAtTheEnd(space, cluster);
500cb93a386Sopenharmony_ci    for (size_t pos = cluster->endPos(); pos < fPositions.size(); pos++) {
501cb93a386Sopenharmony_ci        fPositions[pos].fX += space;
502cb93a386Sopenharmony_ci    }
503cb93a386Sopenharmony_ci}
504cb93a386Sopenharmony_ci#endif
505cb93a386Sopenharmony_ci
506cb93a386Sopenharmony_civoid Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
507cb93a386Sopenharmony_ci
508cb93a386Sopenharmony_ci    SkASSERT(isPlaceholder());
509cb93a386Sopenharmony_ci    auto placeholderStyle = this->placeholderStyle();
510cb93a386Sopenharmony_ci    // Difference between the placeholder baseline and the line bottom
511cb93a386Sopenharmony_ci    SkScalar baselineAdjustment = 0;
512cb93a386Sopenharmony_ci    switch (placeholderStyle->fBaseline) {
513cb93a386Sopenharmony_ci        case TextBaseline::kAlphabetic:
514cb93a386Sopenharmony_ci            break;
515cb93a386Sopenharmony_ci
516cb93a386Sopenharmony_ci        case TextBaseline::kIdeographic:
517cb93a386Sopenharmony_ci            baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
518cb93a386Sopenharmony_ci            break;
519cb93a386Sopenharmony_ci    }
520cb93a386Sopenharmony_ci
521cb93a386Sopenharmony_ci    auto height = placeholderStyle->fHeight;
522cb93a386Sopenharmony_ci    auto offset = placeholderStyle->fBaselineOffset;
523cb93a386Sopenharmony_ci
524cb93a386Sopenharmony_ci    fFontMetrics.fLeading = 0;
525cb93a386Sopenharmony_ci    switch (placeholderStyle->fAlignment) {
526cb93a386Sopenharmony_ci        case PlaceholderAlignment::kBaseline:
527cb93a386Sopenharmony_ci            fFontMetrics.fAscent = baselineAdjustment - height - offset;
528cb93a386Sopenharmony_ci            fFontMetrics.fDescent = baselineAdjustment - offset;
529cb93a386Sopenharmony_ci            break;
530cb93a386Sopenharmony_ci
531cb93a386Sopenharmony_ci        case PlaceholderAlignment::kAboveBaseline:
532cb93a386Sopenharmony_ci            fFontMetrics.fAscent = baselineAdjustment - height;
533cb93a386Sopenharmony_ci            fFontMetrics.fDescent = baselineAdjustment;
534cb93a386Sopenharmony_ci            break;
535cb93a386Sopenharmony_ci
536cb93a386Sopenharmony_ci        case PlaceholderAlignment::kBelowBaseline:
537cb93a386Sopenharmony_ci            fFontMetrics.fAscent = baselineAdjustment;
538cb93a386Sopenharmony_ci            fFontMetrics.fDescent = baselineAdjustment + height;
539cb93a386Sopenharmony_ci            break;
540cb93a386Sopenharmony_ci
541cb93a386Sopenharmony_ci        case PlaceholderAlignment::kTop:
542cb93a386Sopenharmony_ci            fFontMetrics.fAscent = endlineMetrics->ascent();
543cb93a386Sopenharmony_ci            fFontMetrics.fDescent = height + fFontMetrics.fAscent;
544cb93a386Sopenharmony_ci            break;
545cb93a386Sopenharmony_ci
546cb93a386Sopenharmony_ci        case PlaceholderAlignment::kBottom:
547cb93a386Sopenharmony_ci            fFontMetrics.fDescent = endlineMetrics->descent();
548cb93a386Sopenharmony_ci            fFontMetrics.fAscent = fFontMetrics.fDescent - height;
549cb93a386Sopenharmony_ci            break;
550cb93a386Sopenharmony_ci
551cb93a386Sopenharmony_ci        case PlaceholderAlignment::kMiddle:
552cb93a386Sopenharmony_ci            auto mid = (endlineMetrics->ascent() + endlineMetrics->descent()) / PARAM_TWO;
553cb93a386Sopenharmony_ci            fFontMetrics.fDescent = mid + height / PARAM_TWO;
554cb93a386Sopenharmony_ci            fFontMetrics.fAscent = mid - height / PARAM_TWO;
555cb93a386Sopenharmony_ci            break;
556cb93a386Sopenharmony_ci    }
557cb93a386Sopenharmony_ci
558cb93a386Sopenharmony_ci    this->calculateMetrics();
559cb93a386Sopenharmony_ci
560cb93a386Sopenharmony_ci    // Make sure the placeholder can fit the line
561cb93a386Sopenharmony_ci    endlineMetrics->add(this);
562cb93a386Sopenharmony_ci}
563cb93a386Sopenharmony_ci
564cb93a386Sopenharmony_ciSkScalar Cluster::sizeToChar(TextIndex ch) const {
565cb93a386Sopenharmony_ci    if (ch < fTextRange.start || ch >= fTextRange.end) {
566cb93a386Sopenharmony_ci        return 0;
567cb93a386Sopenharmony_ci    }
568cb93a386Sopenharmony_ci    auto shift = ch - fTextRange.start;
569cb93a386Sopenharmony_ci    auto ratio = shift * 1.0 / fTextRange.width();
570cb93a386Sopenharmony_ci
571cb93a386Sopenharmony_ci    return SkDoubleToScalar(fWidth * ratio);
572cb93a386Sopenharmony_ci}
573cb93a386Sopenharmony_ci
574cb93a386Sopenharmony_ciSkScalar Cluster::sizeFromChar(TextIndex ch) const {
575cb93a386Sopenharmony_ci    if (ch < fTextRange.start || ch >= fTextRange.end) {
576cb93a386Sopenharmony_ci        return 0;
577cb93a386Sopenharmony_ci    }
578cb93a386Sopenharmony_ci    auto shift = fTextRange.end - ch - 1;
579cb93a386Sopenharmony_ci    auto ratio = shift * 1.0 / fTextRange.width();
580cb93a386Sopenharmony_ci
581cb93a386Sopenharmony_ci    return SkDoubleToScalar(fWidth * ratio);
582cb93a386Sopenharmony_ci}
583cb93a386Sopenharmony_ci
584cb93a386Sopenharmony_cisize_t Cluster::roundPos(SkScalar s) const {
585cb93a386Sopenharmony_ci    auto ratio = (s * 1.0) / fWidth;
586cb93a386Sopenharmony_ci    return sk_double_floor2int(ratio * size());
587cb93a386Sopenharmony_ci}
588cb93a386Sopenharmony_ci
589cb93a386Sopenharmony_ciSkScalar Cluster::trimmedWidth(size_t pos) const {
590cb93a386Sopenharmony_ci    // Find the width until the pos and return the min between trimmedWidth and the width(pos)
591cb93a386Sopenharmony_ci    // We don't have to take in account cluster shift since it's the same for 0 and for pos
592cb93a386Sopenharmony_ci    auto& run = fOwner->run(fRunIndex);
593cb93a386Sopenharmony_ci    SkScalar delta = getHalfLetterSpacing() - run.halfLetterspacing(pos);
594cb93a386Sopenharmony_ci    return std::min(run.positionX(pos) - run.positionX(fStart) + delta, fWidth);
595cb93a386Sopenharmony_ci}
596cb93a386Sopenharmony_ci
597cb93a386Sopenharmony_ciSkScalar Run::positionX(size_t pos) const {
598cb93a386Sopenharmony_ci    return posX(pos) + (fJustificationShifts.empty() ? 0 : fJustificationShifts[pos].fY) +
599cb93a386Sopenharmony_ci        (fAutoSpacings.empty() ? 0 : fAutoSpacings[pos].fY);
600cb93a386Sopenharmony_ci}
601cb93a386Sopenharmony_ci
602cb93a386Sopenharmony_ciSkScalar Run::posX(size_t index) const {
603cb93a386Sopenharmony_ci    if (index < fPositions.size()) {
604cb93a386Sopenharmony_ci        return fPositions[index].fX;
605cb93a386Sopenharmony_ci    }
606cb93a386Sopenharmony_ci    LOGE("index:%{public}zu,size:%{public}zu", index, fPositions.size());
607cb93a386Sopenharmony_ci    if (fPositions.empty()) {
608cb93a386Sopenharmony_ci        return 0.0f;
609cb93a386Sopenharmony_ci    }
610cb93a386Sopenharmony_ci    return fPositions[fPositions.size() - 1].fX;
611cb93a386Sopenharmony_ci}
612cb93a386Sopenharmony_ci
613cb93a386Sopenharmony_ciPlaceholderStyle* Run::placeholderStyle() const {
614cb93a386Sopenharmony_ci    if (isPlaceholder()) {
615cb93a386Sopenharmony_ci        return &fOwner->placeholders()[fPlaceholderIndex].fStyle;
616cb93a386Sopenharmony_ci    } else {
617cb93a386Sopenharmony_ci        return nullptr;
618cb93a386Sopenharmony_ci    }
619cb93a386Sopenharmony_ci}
620cb93a386Sopenharmony_ci
621cb93a386Sopenharmony_cibool Run::isResolved() const {
622cb93a386Sopenharmony_ci    for (auto& glyph :fGlyphs) {
623cb93a386Sopenharmony_ci        if (glyph == 0) {
624cb93a386Sopenharmony_ci            return false;
625cb93a386Sopenharmony_ci        }
626cb93a386Sopenharmony_ci    }
627cb93a386Sopenharmony_ci    return true;
628cb93a386Sopenharmony_ci}
629cb93a386Sopenharmony_ci
630cb93a386Sopenharmony_ciRun* Cluster::runOrNull() const {
631cb93a386Sopenharmony_ci    if (fRunIndex >= fOwner->runs().size()) {
632cb93a386Sopenharmony_ci        return nullptr;
633cb93a386Sopenharmony_ci    }
634cb93a386Sopenharmony_ci    return &fOwner->run(fRunIndex);
635cb93a386Sopenharmony_ci}
636cb93a386Sopenharmony_ci
637cb93a386Sopenharmony_ciRun& Cluster::run() const {
638cb93a386Sopenharmony_ci    SkASSERT(fRunIndex < fOwner->runs().size());
639cb93a386Sopenharmony_ci    return fOwner->run(fRunIndex);
640cb93a386Sopenharmony_ci}
641cb93a386Sopenharmony_ci
642cb93a386Sopenharmony_ci#ifndef USE_SKIA_TXT
643cb93a386Sopenharmony_ciSkFont Cluster::font() const {
644cb93a386Sopenharmony_ci#else
645cb93a386Sopenharmony_ciRSFont Cluster::font() const {
646cb93a386Sopenharmony_ci#endif
647cb93a386Sopenharmony_ci    SkASSERT(fRunIndex < fOwner->runs().size());
648cb93a386Sopenharmony_ci    return fOwner->run(fRunIndex).font();
649cb93a386Sopenharmony_ci}
650cb93a386Sopenharmony_ci
651cb93a386Sopenharmony_cibool Cluster::isSoftBreak() const {
652cb93a386Sopenharmony_ci    return fOwner->codeUnitHasProperty(fTextRange.end,
653cb93a386Sopenharmony_ci                                       SkUnicode::CodeUnitFlags::kSoftLineBreakBefore);
654cb93a386Sopenharmony_ci}
655cb93a386Sopenharmony_ci
656cb93a386Sopenharmony_cibool Cluster::isGraphemeBreak() const {
657cb93a386Sopenharmony_ci    return fOwner->codeUnitHasProperty(fTextRange.end, SkUnicode::CodeUnitFlags::kGraphemeStart);
658cb93a386Sopenharmony_ci}
659cb93a386Sopenharmony_ci}  // namespace textlayout
660cb93a386Sopenharmony_ci}  // namespace skia
661