1// Copyright 2019 Google LLC.
2
3#include "modules/skparagraph/src/Iterators.h"
4#include "modules/skparagraph/src/OneLineShaper.h"
5#include "src/Run.h"
6#include "src/utils/SkUTF.h"
7#ifdef OHOS_SUPPORT
8#include "utils/text_trace.h"
9#endif
10
11#include <algorithm>
12#include <cstdint>
13#include <unordered_set>
14
15
16static inline SkUnichar nextUtf8Unit(const char** ptr, const char* end) {
17    SkUnichar val = SkUTF::NextUTF8(ptr, end);
18    return val < 0 ? 0xFFFD : val;
19}
20
21namespace skia {
22namespace textlayout {
23
24void OneLineShaper::commitRunBuffer(const RunInfo&) {
25
26    fCurrentRun->commit();
27
28    auto oldUnresolvedCount = fUnresolvedBlocks.size();
29/*
30    SkDebugf("Run [%zu:%zu)\n", fCurrentRun->fTextRange.start, fCurrentRun->fTextRange.end);
31    for (size_t i = 0; i < fCurrentRun->size(); ++i) {
32        SkDebugf("[%zu] %hu %u %f\n", i, fCurrentRun->fGlyphs[i], fCurrentRun->fClusterIndexes[i], fCurrentRun->fPositions[i].fX);
33    }
34*/
35    // Find all unresolved blocks
36    sortOutGlyphs([&](GlyphRange block){
37        if (block.width() == 0) {
38            return;
39        }
40        addUnresolvedWithRun(block);
41    });
42
43    // Fill all the gaps between unresolved blocks with resolved ones
44    if (oldUnresolvedCount == fUnresolvedBlocks.size()) {
45        // No unresolved blocks added - we resolved the block with one run entirely
46        addFullyResolved();
47        return;
48    } else if (oldUnresolvedCount == fUnresolvedBlocks.size() - 1) {
49        auto& unresolved = fUnresolvedBlocks.back();
50        if (fCurrentRun->textRange() == unresolved.fText) {
51            // Nothing was resolved; preserve the initial run if it makes sense
52            auto& front = fUnresolvedBlocks.front();
53            if (front.fRun != nullptr) {
54               unresolved.fRun = front.fRun;
55               unresolved.fGlyphs = front.fGlyphs;
56            }
57            return;
58        }
59    }
60
61    fillGaps(oldUnresolvedCount);
62}
63
64#ifdef SK_DEBUG
65void OneLineShaper::printState() {
66    SkDebugf("Resolved: %zu\n", fResolvedBlocks.size());
67    for (auto& resolved : fResolvedBlocks) {
68        if (resolved.fRun ==  nullptr) {
69            SkDebugf("[%zu:%zu) unresolved\n",
70                    resolved.fText.start, resolved.fText.end);
71            continue;
72        }
73        SkString name("???");
74        if (resolved.fRun->fFont.getTypeface() != nullptr) {
75            resolved.fRun->fFont.getTypeface()->getFamilyName(&name);
76        }
77        SkDebugf("[%zu:%zu) ", resolved.fGlyphs.start, resolved.fGlyphs.end);
78        SkDebugf("[%zu:%zu) with %s\n",
79                resolved.fText.start, resolved.fText.end,
80                name.c_str());
81    }
82
83    auto size = fUnresolvedBlocks.size();
84    SkDebugf("Unresolved: %zu\n", size);
85    for (const auto& unresolved : fUnresolvedBlocks) {
86        SkDebugf("[%zu:%zu)\n", unresolved.fText.start, unresolved.fText.end);
87    }
88}
89#endif
90
91void OneLineShaper::fillGaps(size_t startingCount) {
92    // Fill out gaps between all unresolved blocks
93    TextRange resolvedTextLimits = fCurrentRun->fTextRange;
94    if (!fCurrentRun->leftToRight()) {
95        std::swap(resolvedTextLimits.start, resolvedTextLimits.end);
96    }
97    TextIndex resolvedTextStart = resolvedTextLimits.start;
98    GlyphIndex resolvedGlyphsStart = 0;
99
100    auto begin = fUnresolvedBlocks.begin();
101    auto end = fUnresolvedBlocks.end();
102    begin += startingCount; // Skip the old ones, do the new ones
103    TextRange prevText = EMPTY_TEXT;
104    for (; begin != end; ++begin) {
105        auto& unresolved = *begin;
106
107        if (unresolved.fText == prevText) {
108            // Clean up repetitive blocks that appear inside the same grapheme block
109            unresolved.fText = EMPTY_TEXT;
110            continue;
111        } else {
112            prevText = unresolved.fText;
113        }
114
115        TextRange resolvedText(resolvedTextStart, fCurrentRun->leftToRight() ? unresolved.fText.start : unresolved.fText.end);
116        if (resolvedText.width() > 0) {
117            if (!fCurrentRun->leftToRight()) {
118                std::swap(resolvedText.start, resolvedText.end);
119            }
120
121            GlyphRange resolvedGlyphs(resolvedGlyphsStart, unresolved.fGlyphs.start);
122            RunBlock resolved(fCurrentRun, resolvedText, resolvedGlyphs, resolvedGlyphs.width());
123
124            if (resolvedGlyphs.width() == 0) {
125                // Extend the unresolved block with an empty resolved
126                if (unresolved.fText.end <= resolved.fText.start) {
127                    unresolved.fText.end = resolved.fText.end;
128                }
129                if (unresolved.fText.start >= resolved.fText.end) {
130                    unresolved.fText.start = resolved.fText.start;
131                }
132            } else {
133                fResolvedBlocks.emplace_back(resolved);
134            }
135        }
136        resolvedGlyphsStart = unresolved.fGlyphs.end;
137        resolvedTextStart =  fCurrentRun->leftToRight()
138                                ? unresolved.fText.end
139                                : unresolved.fText.start;
140    }
141
142    TextRange resolvedText(resolvedTextStart,resolvedTextLimits.end);
143    if (resolvedText.width() > 0) {
144        if (!fCurrentRun->leftToRight()) {
145            std::swap(resolvedText.start, resolvedText.end);
146        }
147
148        GlyphRange resolvedGlyphs(resolvedGlyphsStart, fCurrentRun->size());
149        RunBlock resolved(fCurrentRun, resolvedText, resolvedGlyphs, resolvedGlyphs.width());
150        fResolvedBlocks.emplace_back(resolved);
151    }
152}
153
154void OneLineShaper::finish(const Block& block, SkScalar height, SkScalar& advanceX) {
155#ifdef OHOS_SUPPORT
156    TEXT_TRACE_FUNC();
157#endif
158    auto blockText = block.fRange;
159
160    // Add all unresolved blocks to resolved blocks
161    while (!fUnresolvedBlocks.empty()) {
162        auto unresolved = fUnresolvedBlocks.front();
163        fUnresolvedBlocks.pop_front();
164        if (unresolved.fText.width() == 0) {
165            continue;
166        }
167        fResolvedBlocks.emplace_back(unresolved);
168        fUnresolvedGlyphs += unresolved.fGlyphs.width();
169        fParagraph->addUnresolvedCodepoints(unresolved.fText);
170    }
171
172    // Sort all pieces by text
173    std::sort(fResolvedBlocks.begin(), fResolvedBlocks.end(),
174              [](const RunBlock& a, const RunBlock& b) {
175                return a.fText.start < b.fText.start;
176              });
177
178    // Go through all of them
179    size_t lastTextEnd = blockText.start;
180    for (auto& resolvedBlock : fResolvedBlocks) {
181
182        if (resolvedBlock.fText.end <= blockText.start) {
183            continue;
184        }
185
186        if (resolvedBlock.fRun != nullptr) {
187            fParagraph->fFontSwitches.emplace_back(resolvedBlock.fText.start, resolvedBlock.fRun->fFont);
188        }
189
190        auto run = resolvedBlock.fRun;
191        auto glyphs = resolvedBlock.fGlyphs;
192        auto text = resolvedBlock.fText;
193        if (lastTextEnd != text.start) {
194            SkDEBUGF("Text ranges mismatch: ...:%zu] - [%zu:%zu] (%zu-%zu)\n",
195                     lastTextEnd, text.start, text.end,  glyphs.start, glyphs.end);
196            SkASSERT(false);
197        }
198        lastTextEnd = text.end;
199
200        if (resolvedBlock.isFullyResolved()) {
201            // Just move the entire run
202            resolvedBlock.fRun->fIndex = this->fParagraph->fRuns.size();
203            this->fParagraph->fRuns.emplace_back(*resolvedBlock.fRun);
204            resolvedBlock.fRun.reset();
205            continue;
206        } else if (run == nullptr) {
207            continue;
208        }
209
210        auto runAdvance = SkVector::Make(run->posX(glyphs.end) - run->posX(glyphs.start), run->fAdvance.fY);
211        const SkShaper::RunHandler::RunInfo info = {
212                run->fFont,
213                run->fBidiLevel,
214                runAdvance,
215                glyphs.width(),
216                SkShaper::RunHandler::Range(text.start - run->fClusterStart, text.width())
217        };
218        this->fParagraph->fRuns.emplace_back(
219                    this->fParagraph,
220                    info,
221                    run->fClusterStart,
222                    height,
223                    block.fStyle.getHalfLeading(),
224                    block.fStyle.getBaselineShift(),
225                    this->fParagraph->fRuns.size(),
226                    advanceX
227                );
228        auto piece = &this->fParagraph->fRuns.back();
229
230        // TODO: Optimize copying
231        SkPoint zero = {run->fPositions[glyphs.start].fX, 0};
232        for (size_t i = glyphs.start; i <= glyphs.end; ++i) {
233
234            auto index = i - glyphs.start;
235            if (i < glyphs.end) {
236                piece->fGlyphs[index] = run->fGlyphs[i];
237            }
238            piece->fClusterIndexes[index] = run->fClusterIndexes[i];
239            piece->fPositions[index] = run->fPositions[i] - zero;
240            piece->fOffsets[index] = run->fOffsets[i];
241            piece->addX(index, advanceX);
242        }
243
244        // Carve out the line text out of the entire run text
245        fAdvance.fX += runAdvance.fX;
246        fAdvance.fY = std::max(fAdvance.fY, runAdvance.fY);
247    }
248
249    advanceX = fAdvance.fX;
250    if (lastTextEnd != blockText.end) {
251        SkDEBUGF("Last range mismatch: %zu - %zu\n", lastTextEnd, blockText.end);
252        SkASSERT(false);
253    }
254}
255
256// Make it [left:right) regardless of a text direction
257TextRange OneLineShaper::normalizeTextRange(GlyphRange glyphRange) {
258
259    if (fCurrentRun->leftToRight()) {
260        return TextRange(clusterIndex(glyphRange.start), clusterIndex(glyphRange.end));
261    } else {
262        return TextRange(clusterIndex(glyphRange.end - 1),
263                glyphRange.start > 0
264                ? clusterIndex(glyphRange.start - 1)
265                : fCurrentRun->fTextRange.end);
266    }
267}
268
269void OneLineShaper::addFullyResolved() {
270    if (this->fCurrentRun->size() == 0) {
271        return;
272    }
273    RunBlock resolved(fCurrentRun,
274                      this->fCurrentRun->fTextRange,
275                      GlyphRange(0, this->fCurrentRun->size()),
276                      this->fCurrentRun->size());
277    fResolvedBlocks.emplace_back(resolved);
278}
279
280void OneLineShaper::addUnresolvedWithRun(GlyphRange glyphRange) {
281    auto extendedText = this->clusteredText(glyphRange); // It also modifies glyphRange if needed
282    RunBlock unresolved(fCurrentRun, extendedText, glyphRange, 0);
283    if (unresolved.fGlyphs.width() == fCurrentRun->size()) {
284        SkASSERT(unresolved.fText.width() == fCurrentRun->fTextRange.width());
285    } else if (fUnresolvedBlocks.size() > 0) {
286        auto& lastUnresolved = fUnresolvedBlocks.back();
287        if (lastUnresolved.fRun != nullptr &&
288            lastUnresolved.fRun->fIndex == fCurrentRun->fIndex) {
289
290            if (lastUnresolved.fText.end == unresolved.fText.start) {
291              // Two pieces next to each other - can join them
292              lastUnresolved.fText.end = unresolved.fText.end;
293              lastUnresolved.fGlyphs.end = glyphRange.end;
294              return;
295            } else if(lastUnresolved.fText == unresolved.fText) {
296                // Nothing was resolved; ignore it
297                return;
298            } else if (lastUnresolved.fText.contains(unresolved.fText)) {
299                // We get here for the very first unresolved piece
300                return;
301            } else if (lastUnresolved.fText.intersects(unresolved.fText)) {
302                // Few pieces of the same unresolved text block can ignore the second one
303                lastUnresolved.fGlyphs.start = std::min(lastUnresolved.fGlyphs.start, glyphRange.start);
304                lastUnresolved.fGlyphs.end = std::max(lastUnresolved.fGlyphs.end, glyphRange.end);
305                lastUnresolved.fText = this->clusteredText(lastUnresolved.fGlyphs);
306                return;
307            }
308        }
309    }
310    fUnresolvedBlocks.emplace_back(unresolved);
311}
312
313// Glue whitespaces to the next/prev unresolved blocks
314// (so we don't have chinese text with english whitespaces broken into millions of tiny runs)
315void OneLineShaper::sortOutGlyphs(std::function<void(GlyphRange)>&& sortOutUnresolvedBLock) {
316
317    GlyphRange block = EMPTY_RANGE;
318    bool graphemeResolved = false;
319    TextIndex graphemeStart = EMPTY_INDEX;
320    for (size_t i = 0; i < fCurrentRun->size(); ++i) {
321
322        ClusterIndex ci = clusterIndex(i);
323        // Removing all pretty optimizations for whitespaces
324        // because they get in a way of grapheme rounding
325        // Inspect the glyph
326        auto glyph = fCurrentRun->fGlyphs[i];
327
328        GraphemeIndex gi = fParagraph->findPreviousGraphemeBoundary(ci);
329        if ((fCurrentRun->leftToRight() ? gi > graphemeStart : gi < graphemeStart) || graphemeStart == EMPTY_INDEX) {
330            // This is the Flutter change
331            // Do not count control codepoints as unresolved
332            bool isControl8 = fParagraph->codeUnitHasProperty(ci,
333                                                              SkUnicode::CodeUnitFlags::kControl);
334            // We only count glyph resolved if all the glyphs in its grapheme are resolved
335            graphemeResolved = glyph != 0 || isControl8;
336            graphemeStart = gi;
337        } else if (glyph == 0) {
338            // Found unresolved glyph - the entire grapheme is unresolved now
339            graphemeResolved = false;
340        }
341
342        if (!graphemeResolved) { // Unresolved glyph and not control codepoint
343            if (block.start == EMPTY_INDEX) {
344                // Start new unresolved block
345                block.start = i;
346                block.end = EMPTY_INDEX;
347            } else {
348                // Keep skipping unresolved block
349            }
350        } else { // Resolved glyph or control codepoint
351            if (block.start == EMPTY_INDEX) {
352                // Keep skipping resolved code points
353            } else {
354                // This is the end of unresolved block
355                block.end = i;
356                sortOutUnresolvedBLock(block);
357                block = EMPTY_RANGE;
358            }
359        }
360    }
361
362    // One last block could have been left
363    if (block.start != EMPTY_INDEX) {
364        block.end = fCurrentRun->size();
365        sortOutUnresolvedBLock(block);
366    }
367}
368
369BlockRange OneLineShaper::generateBlockRange(const Block& block, const TextRange& textRange)
370{
371    size_t start = std::max(block.fRange.start, textRange.start);
372    size_t end = std::min(block.fRange.end, textRange.end);
373    if (fParagraph->fParagraphStyle.getMaxLines() == 1 &&
374        fParagraph->fParagraphStyle.getEllipsisMod() == EllipsisModal::MIDDLE &&
375        !fParagraph->getEllipsisState()) {
376        end = fParagraph->fText.size();
377    }
378    return BlockRange(start, end);
379}
380
381void OneLineShaper::iterateThroughFontStyles(TextRange textRange,
382                                             SkSpan<Block> styleSpan,
383                                             const ShapeSingleFontVisitor& visitor) {
384    Block combinedBlock;
385    SkTArray<SkShaper::Feature> features;
386
387    auto addFeatures = [&features](const Block& block) {
388        for (auto& ff : block.fStyle.getFontFeatures()) {
389            if (ff.fName.size() != 4) {
390                SkDEBUGF("Incorrect font feature: %s=%d\n", ff.fName.c_str(), ff.fValue);
391                continue;
392            }
393            SkShaper::Feature feature = {
394                SkSetFourByteTag(ff.fName[0], ff.fName[1], ff.fName[2], ff.fName[3]),
395                SkToU32(ff.fValue),
396                block.fRange.start,
397                block.fRange.end
398            };
399            features.emplace_back(feature);
400        }
401        // Disable ligatures if letter spacing is enabled.
402        if (block.fStyle.getLetterSpacing() > 0) {
403            features.emplace_back(SkShaper::Feature{
404                SkSetFourByteTag('l', 'i', 'g', 'a'), 0, block.fRange.start, block.fRange.end
405            });
406        }
407    };
408
409    for (auto& block : styleSpan) {
410        BlockRange blockRange = generateBlockRange(block, textRange);
411        if (blockRange.empty()) {
412            continue;
413        }
414        SkASSERT(combinedBlock.fRange.width() == 0 || combinedBlock.fRange.end == block.fRange.start);
415
416        if (!combinedBlock.fRange.empty()) {
417            if (block.fStyle.matchOneAttribute(StyleType::kFont, combinedBlock.fStyle)) {
418                combinedBlock.add(blockRange);
419                addFeatures(block);
420                continue;
421            }
422            // Resolve all characters in the block for this style
423            visitor(combinedBlock, features);
424        }
425
426        combinedBlock.fRange = blockRange;
427        combinedBlock.fStyle = block.fStyle;
428        features.reset();
429        addFeatures(block);
430    }
431
432    visitor(combinedBlock, features);
433#ifdef SK_DEBUG
434    //printState();
435#endif
436}
437
438void OneLineShaper::matchResolvedFonts(const TextStyle& textStyle,
439                                       const TypefaceVisitor& visitor) {
440#ifdef OHOS_SUPPORT
441    TEXT_TRACE_FUNC();
442#endif
443#ifndef USE_SKIA_TXT
444    std::vector<sk_sp<SkTypeface>> typefaces = fParagraph->fFontCollection->findTypefaces(textStyle.getFontFamilies(), textStyle.getFontStyle(), textStyle.getFontArguments());
445#else
446    std::vector<std::shared_ptr<RSTypeface>> typefaces = fParagraph->fFontCollection->findTypefaces(
447        textStyle.getFontFamilies(), textStyle.getFontStyle(), textStyle.getFontArguments());
448#endif
449
450    for (const auto& typeface : typefaces) {
451        if (visitor(typeface) == Resolved::Everything) {
452            // Resolved everything
453            return;
454        }
455    }
456
457    if (fParagraph->fFontCollection->fontFallbackEnabled()) {
458        // Give fallback a clue
459        // Some unresolved subblocks might be resolved with different fallback fonts
460        std::vector<RunBlock> hopelessBlocks;
461        while (!fUnresolvedBlocks.empty()) {
462            auto unresolvedRange = fUnresolvedBlocks.front().fText;
463            auto unresolvedText = fParagraph->text(unresolvedRange);
464            const char* ch = unresolvedText.begin();
465            // We have the global cache for all already found typefaces for SkUnichar
466            // but we still need to keep track of all SkUnichars used in this unresolved block
467            SkTHashSet<SkUnichar> alreadyTriedCodepoints;
468            SkTHashSet<uint32_t> alreadyTriedTypefaces;
469            while (true) {
470
471                if (ch == unresolvedText.end()) {
472                    // Not a single codepoint could be resolved but we finished the block
473                    hopelessBlocks.push_back(fUnresolvedBlocks.front());
474                    fUnresolvedBlocks.pop_front();
475                    break;
476                }
477
478                // See if we can switch to the next DIFFERENT codepoint
479                SkUnichar unicode = -1;
480                while (ch != unresolvedText.end()) {
481                    unicode = nextUtf8Unit(&ch, unresolvedText.end());
482                    if (!alreadyTriedCodepoints.contains(unicode)) {
483                        alreadyTriedCodepoints.add(unicode);
484                        break;
485                    }
486                }
487                SkASSERT(unicode != -1);
488
489                // First try to find in in a cache
490#ifndef USE_SKIA_TXT
491                sk_sp<SkTypeface> typeface;
492#else
493                std::shared_ptr<RSTypeface> typeface;
494#endif
495                FontKey fontKey(unicode, textStyle.getFontStyle(), textStyle.getLocale());
496                auto found = fFallbackFonts.find(fontKey);
497#ifndef USE_SKIA_TXT
498                if (found != nullptr) {
499                    typeface = *found;
500#else
501                if (found != fFallbackFonts.end()) {
502                    typeface = found->second;
503#endif
504                } else {
505                    typeface = fParagraph->fFontCollection->defaultFallback(
506                            unicode, textStyle.getFontStyle(), textStyle.getLocale());
507
508                    if (typeface == nullptr) {
509                        // There is no fallback font for this character, so move on to the next character.
510                        continue;
511                    }
512#ifndef USE_SKIA_TXT
513                    fFallbackFonts.set(fontKey, typeface);
514#else
515                    fFallbackFonts.emplace(fontKey, typeface);
516#endif
517                }
518
519                // Check if we already tried this font on this text range
520#ifndef USE_SKIA_TXT
521                if (!alreadyTriedTypefaces.contains(typeface->uniqueID())) {
522                    alreadyTriedTypefaces.add(typeface->uniqueID());
523#else
524                if (!alreadyTriedTypefaces.contains(typeface->GetUniqueID())) {
525                    alreadyTriedTypefaces.add(typeface->GetUniqueID());
526#endif
527                } else {
528                    continue;
529                }
530
531                if (typeface && textStyle.getFontArguments()) {
532                    typeface = fParagraph->fFontCollection->CloneTypeface(typeface, textStyle.getFontArguments());
533                }
534
535                auto resolvedBlocksBefore = fResolvedBlocks.size();
536                auto resolved = visitor(typeface);
537                if (resolved == Resolved::Everything) {
538                    if (hopelessBlocks.empty()) {
539                        // Resolved everything, no need to try another font
540                        return;
541                    } else if (resolvedBlocksBefore < fResolvedBlocks.size()) {
542                        // There are some resolved blocks
543                        resolved = Resolved::Something;
544                    } else {
545                        // All blocks are hopeless
546                        resolved = Resolved::Nothing;
547                    }
548                }
549
550                if (resolved == Resolved::Something) {
551                    // Resolved something, no need to try another codepoint
552                    break;
553                }
554            }
555        }
556
557        // Return hopeless blocks back
558        for (auto& block : hopelessBlocks) {
559            fUnresolvedBlocks.emplace_front(block);
560        }
561    }
562}
563
564bool OneLineShaper::iterateThroughShapingRegions(const ShapeVisitor& shape) {
565
566    size_t bidiIndex = 0;
567
568    SkScalar advanceX = 0;
569    for (auto& placeholder : fParagraph->fPlaceholders) {
570
571        if (placeholder.fTextBefore.width() > 0) {
572            // Shape the text by bidi regions
573            while (bidiIndex < fParagraph->fBidiRegions.size()) {
574                SkUnicode::BidiRegion& bidiRegion = fParagraph->fBidiRegions[bidiIndex];
575                auto start = std::max(bidiRegion.start, placeholder.fTextBefore.start);
576                auto end = std::min(bidiRegion.end, placeholder.fTextBefore.end);
577                if (fParagraph->fParagraphStyle.getMaxLines() == 1
578                    && fParagraph->fParagraphStyle.getEllipsisMod() == EllipsisModal::MIDDLE
579                    && !fParagraph->getEllipsisState()) {
580                    end = fParagraph->fText.size();
581                }
582
583                // Set up the iterators (the style iterator points to a bigger region that it could
584                TextRange textRange(start, end);
585                auto blockRange = fParagraph->findAllBlocks(textRange);
586                if (!blockRange.empty()) {
587                    SkSpan<Block> styleSpan(fParagraph->blocks(blockRange));
588
589                    // Shape the text between placeholders
590                    if (!shape(textRange, styleSpan, advanceX, start, bidiRegion.level)) {
591                        return false;
592                    }
593                }
594
595                if (end == bidiRegion.end) {
596                    ++bidiIndex;
597                } else /*if (end == placeholder.fTextBefore.end)*/ {
598                    break;
599                }
600            }
601        }
602
603        if (placeholder.fRange.width() == 0) {
604            continue;
605        }
606
607        // Get the placeholder font
608        auto typefaces = fParagraph->fFontCollection->findTypefaces(
609            placeholder.fTextStyle.getFontFamilies(),
610            placeholder.fTextStyle.getFontStyle(),
611            placeholder.fTextStyle.getFontArguments());
612        auto typeface = typefaces.size() ? typefaces.front() : nullptr;
613#ifndef USE_SKIA_TXT
614        SkFont font(typeface, placeholder.fTextStyle.getFontSize());
615#else
616        RSFont font(typeface, placeholder.fTextStyle.getFontSize(), 1, 0);
617#endif
618
619        // "Shape" the placeholder
620        uint8_t bidiLevel = (bidiIndex < fParagraph->fBidiRegions.size())
621            ? fParagraph->fBidiRegions[bidiIndex].level
622            : 2;
623        const SkShaper::RunHandler::RunInfo runInfo = {
624            font,
625            bidiLevel,
626            SkPoint::Make(placeholder.fStyle.fWidth, placeholder.fStyle.fHeight),
627            1,
628            SkShaper::RunHandler::Range(0, placeholder.fRange.width())
629        };
630        auto& run = fParagraph->fRuns.emplace_back(this->fParagraph,
631                                       runInfo,
632                                       placeholder.fRange.start,
633                                       0.0f,
634                                       0.0f,
635                                       false,
636                                       fParagraph->fRuns.size(),
637                                       advanceX);
638
639        run.fPositions[0] = { advanceX, 0 };
640        run.fOffsets[0] = {0, 0};
641        run.fClusterIndexes[0] = 0;
642        run.fPlaceholderIndex = &placeholder - fParagraph->fPlaceholders.begin();
643        advanceX += placeholder.fStyle.fWidth;
644    }
645    return true;
646}
647
648bool OneLineShaper::shape() {
649#ifdef OHOS_SUPPORT
650    TEXT_TRACE_FUNC();
651#endif
652    // The text can be broken into many shaping sequences
653    // (by place holders, possibly, by hard line breaks or tabs, too)
654    auto limitlessWidth = std::numeric_limits<SkScalar>::max();
655
656    auto result = iterateThroughShapingRegions(
657            [this, limitlessWidth]
658            (TextRange textRange, SkSpan<Block> styleSpan, SkScalar& advanceX, TextIndex textStart, uint8_t defaultBidiLevel) {
659
660        // Set up the shaper and shape the next
661        auto shaper = SkShaper::MakeShapeDontWrapOrReorder(fParagraph->fUnicode->copy());
662        if (shaper == nullptr) {
663            // For instance, loadICU does not work. We have to stop the process
664            return false;
665        }
666
667        iterateThroughFontStyles(textRange, styleSpan,
668                [this, &shaper, defaultBidiLevel, limitlessWidth, &advanceX]
669                (Block block, SkTArray<SkShaper::Feature> features) {
670            auto blockSpan = SkSpan<Block>(&block, 1);
671
672            // Start from the beginning (hoping that it's a simple case one block - one run)
673            fHeight = block.fStyle.getHeightOverride() ? block.fStyle.getHeight() : 0;
674            fUseHalfLeading = block.fStyle.getHalfLeading();
675            fBaselineShift = block.fStyle.getBaselineShift();
676            fAdvance = SkVector::Make(advanceX, 0);
677            fCurrentText = block.fRange;
678            fUnresolvedBlocks.emplace_back(RunBlock(block.fRange));
679
680#ifndef USE_SKIA_TXT
681            this->matchResolvedFonts(block.fStyle, [&](sk_sp<SkTypeface> typeface) {
682#else
683            this->matchResolvedFonts(block.fStyle, [&](std::shared_ptr<RSTypeface> typeface) {
684#endif
685#ifdef OHOS_SUPPORT
686                TEXT_TRACE("OneLineShaper::HandleTypeface");
687#endif
688                // Create one more font to try
689#ifndef USE_SKIA_TXT
690                SkFont font(std::move(typeface), block.fStyle.getFontSize());
691                font.setEdging(SkFont::Edging::kAntiAlias);
692                font.setHinting(SkFontHinting::kNone);
693                font.setSubpixel(true);
694                font.setBaselineSnap(false);
695#else
696                RSFont font(std::move(typeface), block.fStyle.getFontSize(), 1, 0);
697                font.SetEdging(RSDrawing::FontEdging::ANTI_ALIAS);
698                font.SetHinting(RSDrawing::FontHinting::NONE);
699                font.SetSubpixel(true);
700                font.SetBaselineSnap(false);
701#endif
702
703#ifdef OHOS_SUPPORT
704                scaleFontWithCompressionConfig(font, ScaleOP::COMPRESS);
705#endif
706                // Apply fake bold and/or italic settings to the font if the
707                // typeface's attributes do not match the intended font style.
708#ifndef USE_SKIA_TXT
709                int wantedWeight = block.fStyle.getFontStyle().weight();
710                bool fakeBold =
711                    wantedWeight >= SkFontStyle::kSemiBold_Weight &&
712                    wantedWeight - font.getTypeface()->fontStyle().weight() >= 200;
713                bool fakeItalic =
714                    block.fStyle.getFontStyle().slant() == SkFontStyle::kItalic_Slant &&
715                    font.getTypeface()->fontStyle().slant() != SkFontStyle::kItalic_Slant;
716                font.setEmbolden(fakeBold);
717                font.setSkewX(fakeItalic ? -SK_Scalar1 / 4 : 0);
718#else
719                int wantedWeight = block.fStyle.getFontStyle().GetWeight();
720                bool fakeBold =
721                    wantedWeight >= RSFontStyle::SEMI_BOLD_WEIGHT &&
722                    wantedWeight - font.GetTypeface()->GetFontStyle().GetWeight() >= 200;
723                bool fakeItalic =
724                    block.fStyle.getFontStyle().GetSlant() == RSFontStyle::ITALIC_SLANT &&
725                    font.GetTypeface()->GetFontStyle().GetSlant() != RSFontStyle::ITALIC_SLANT;
726                font.SetEmbolden(fakeBold);
727                font.SetSkewX(fakeItalic ? -SK_Scalar1 / 4 : 0);
728#endif
729
730                // Walk through all the currently unresolved blocks
731                // (ignoring those that appear later)
732                auto resolvedCount = fResolvedBlocks.size();
733                auto unresolvedCount = fUnresolvedBlocks.size();
734                while (unresolvedCount-- > 0) {
735                    auto unresolvedRange = fUnresolvedBlocks.front().fText;
736                    if (unresolvedRange == EMPTY_TEXT) {
737                        // Duplicate blocks should be ignored
738                        fUnresolvedBlocks.pop_front();
739                        continue;
740                    }
741                    auto unresolvedText = fParagraph->text(unresolvedRange);
742
743                    SkShaper::TrivialFontRunIterator fontIter(font, unresolvedText.size());
744                    LangIterator langIter(unresolvedText, blockSpan,
745                                      fParagraph->paragraphStyle().getTextStyle());
746                    SkShaper::TrivialBiDiRunIterator bidiIter(defaultBidiLevel, unresolvedText.size());
747                    auto scriptIter = SkShaper::MakeSkUnicodeHbScriptRunIterator(
748                            unresolvedText.begin(), unresolvedText.size());
749                    fCurrentText = unresolvedRange;
750
751                    // Map the block's features to subranges within the unresolved range.
752                    SkTArray<SkShaper::Feature> adjustedFeatures(features.size());
753                    for (const SkShaper::Feature& feature : features) {
754                        SkRange<size_t> featureRange(feature.start, feature.end);
755                        if (unresolvedRange.intersects(featureRange)) {
756                            SkRange<size_t> adjustedRange = unresolvedRange.intersection(featureRange);
757                            adjustedRange.Shift(-static_cast<std::make_signed_t<size_t>>(unresolvedRange.start));
758                            adjustedFeatures.push_back({feature.tag, feature.value, adjustedRange.start, adjustedRange.end});
759                        }
760                    }
761
762                    shaper->shape(unresolvedText.begin(), unresolvedText.size(),
763                            fontIter, bidiIter,*scriptIter, langIter,
764                            adjustedFeatures.data(), adjustedFeatures.size(),
765                            limitlessWidth, this);
766
767                    // Take off the queue the block we tried to resolved -
768                    // whatever happened, we have now smaller pieces of it to deal with
769                    fUnresolvedBlocks.pop_front();
770                }
771
772                if (fUnresolvedBlocks.empty()) {
773                    // In some cases it does not mean everything
774                    // (when we excluded some hopeless blocks from the list)
775                    return Resolved::Everything;
776                } else if (resolvedCount < fResolvedBlocks.size()) {
777                    return Resolved::Something;
778                } else {
779                    return Resolved::Nothing;
780                }
781            });
782
783            this->finish(block, fHeight, advanceX);
784        });
785
786        return true;
787    });
788
789    return result;
790}
791
792// When we extend TextRange to the grapheme edges, we also extend glyphs range
793TextRange OneLineShaper::clusteredText(GlyphRange& glyphs) {
794
795    enum class Dir { left, right };
796    enum class Pos { inclusive, exclusive };
797
798    // [left: right)
799    auto findBaseChar = [&](TextIndex index, Dir dir) -> TextIndex {
800
801        if (dir == Dir::right) {
802            while (index < fCurrentRun->fTextRange.end) {
803                if (this->fParagraph->codeUnitHasProperty(index,
804                                                      SkUnicode::CodeUnitFlags::kGraphemeStart)) {
805                    return index;
806                }
807                ++index;
808            }
809            return fCurrentRun->fTextRange.end;
810        } else {
811            while (index > fCurrentRun->fTextRange.start) {
812                if (this->fParagraph->codeUnitHasProperty(index,
813                                                      SkUnicode::CodeUnitFlags::kGraphemeStart)) {
814                    return index;
815                }
816                --index;
817            }
818            return fCurrentRun->fTextRange.start;
819        }
820    };
821
822    TextRange textRange(normalizeTextRange(glyphs));
823    textRange.start = findBaseChar(textRange.start, Dir::left);
824    textRange.end = findBaseChar(textRange.end, Dir::right);
825
826    // Correct the glyphRange in case we extended the text to the grapheme edges
827    // TODO: code it without if (as a part of LTR/RTL refactoring)
828    if (fCurrentRun->leftToRight()) {
829        while (glyphs.start > 0 && clusterIndex(glyphs.start) > textRange.start) {
830          glyphs.start--;
831        }
832        while (glyphs.end < fCurrentRun->size() && clusterIndex(glyphs.end) < textRange.end) {
833          glyphs.end++;
834        }
835    } else {
836        while (glyphs.start > 0 && clusterIndex(glyphs.start - 1) < textRange.end) {
837          glyphs.start--;
838        }
839        while (glyphs.end < fCurrentRun->size() && clusterIndex(glyphs.end) > textRange.start) {
840          glyphs.end++;
841        }
842    }
843
844    return { textRange.start, textRange.end };
845}
846
847bool OneLineShaper::FontKey::operator==(const OneLineShaper::FontKey& other) const {
848    return fUnicode == other.fUnicode && fFontStyle == other.fFontStyle && fLocale == other.fLocale;
849}
850
851uint32_t OneLineShaper::FontKey::Hasher::operator()(const OneLineShaper::FontKey& key) const {
852    return SkGoodHash()(key.fUnicode) ^
853           SkGoodHash()(key.fFontStyle) ^
854           SkGoodHash()(key.fLocale);
855}
856
857}  // namespace textlayout
858}  // namespace skia
859