void FontCollection::itemize(const uint16_t* string, size_t string_size, FontStyle style, vector<Run>* result) const { const uint32_t langListId = style.getLanguageListId(); int variant = style.getVariant(); const FontFamily* lastFamily = nullptr; Run* run = NULL; if (string_size == 0) { return; } const uint32_t kEndOfString = 0xFFFFFFFF; uint32_t nextCh = 0; uint32_t prevCh = 0; size_t nextUtf16Pos = 0; size_t readLength = 0; U16_NEXT(string, readLength, string_size, nextCh); do { const uint32_t ch = nextCh; const size_t utf16Pos = nextUtf16Pos; nextUtf16Pos = readLength; if (readLength < string_size) { U16_NEXT(string, readLength, string_size, nextCh); } else { nextCh = kEndOfString; } bool shouldContinueRun = false; if (lastFamily != nullptr) { if (isStickyWhitelisted(ch)) { // Continue using existing font as long as it has coverage and is // whitelisted shouldContinueRun = lastFamily->getCoverage().get(ch); } else if (ch == SOFT_HYPHEN || isVariationSelector(ch)) { // Always continue if the character is the soft hyphen or a variation // selector. shouldContinueRun = true; } } if (!shouldContinueRun) { const std::shared_ptr<FontFamily>& family = getFamilyForChar( ch, isVariationSelector(nextCh) ? nextCh : 0, langListId, variant); if (utf16Pos == 0 || family.get() != lastFamily) { size_t start = utf16Pos; // Workaround for combining marks and emoji modifiers until we implement // per-cluster font selection: if a combining mark or an emoji modifier // is found in a different font that also supports the previous // character, attach previous character to the new run. U+20E3 COMBINING // ENCLOSING KEYCAP, used in emoji, is handled properly by this since // it's a combining mark too. if (utf16Pos != 0 && ((U_GET_GC_MASK(ch) & U_GC_M_MASK) != 0 || (isEmojiModifier(ch) && isEmojiBase(prevCh))) && family != nullptr && family->getCoverage().get(prevCh)) { const size_t prevChLength = U16_LENGTH(prevCh); run->end -= prevChLength; if (run->start == run->end) { result->pop_back(); } start -= prevChLength; } result->push_back( {family->getClosestMatch(style), static_cast<int>(start), 0}); run = &result->back(); lastFamily = family.get(); } } prevCh = ch; run->end = nextUtf16Pos; // exclusive } while (nextCh != kEndOfString); }