1cb93a386Sopenharmony_ci// Copyright 2021 Google LLC. 2cb93a386Sopenharmony_ci#include "experimental/sktext/include/Text.h" 3cb93a386Sopenharmony_ci#include "experimental/sktext/src/LogicalRun.h" 4cb93a386Sopenharmony_ci#include "experimental/sktext/src/VisualRun.h" 5cb93a386Sopenharmony_ci#include <memory> 6cb93a386Sopenharmony_ci#include <stack> 7cb93a386Sopenharmony_cinamespace skia { 8cb93a386Sopenharmony_cinamespace text { 9cb93a386Sopenharmony_ciUnicodeText::UnicodeText(std::unique_ptr<SkUnicode> unicode, SkSpan<uint16_t> utf16) 10cb93a386Sopenharmony_ci : fText16(std::u16string((char16_t*)utf16.data(), utf16.size())) 11cb93a386Sopenharmony_ci , fUnicode(std::move(unicode)) { 12cb93a386Sopenharmony_ci initialize(utf16); 13cb93a386Sopenharmony_ci} 14cb93a386Sopenharmony_ci 15cb93a386Sopenharmony_ciUnicodeText::UnicodeText(std::unique_ptr<SkUnicode> unicode, const SkString& utf8) 16cb93a386Sopenharmony_ci : fUnicode(std::move(unicode)) { 17cb93a386Sopenharmony_ci fText16 = fUnicode->convertUtf8ToUtf16(utf8); 18cb93a386Sopenharmony_ci initialize(SkSpan<uint16_t>((uint16_t*)fText16.data(), fText16.size())); 19cb93a386Sopenharmony_ci} 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_cibool UnicodeText::isWhitespaces(TextRange range) const { 22cb93a386Sopenharmony_ci for (auto i = range.fStart; i < range.fEnd; ++i) { 23cb93a386Sopenharmony_ci if (!this->hasProperty(i, CodeUnitFlags::kPartOfWhiteSpace)) { 24cb93a386Sopenharmony_ci return false; 25cb93a386Sopenharmony_ci } 26cb93a386Sopenharmony_ci } 27cb93a386Sopenharmony_ci return true; 28cb93a386Sopenharmony_ci} 29cb93a386Sopenharmony_ci 30cb93a386Sopenharmony_civoid UnicodeText::initialize(SkSpan<uint16_t> utf16) { 31cb93a386Sopenharmony_ci if (!fUnicode) { 32cb93a386Sopenharmony_ci SkASSERT(fUnicode); 33cb93a386Sopenharmony_ci return; 34cb93a386Sopenharmony_ci } 35cb93a386Sopenharmony_ci // Get white spaces 36cb93a386Sopenharmony_ci fCodeUnitProperties.push_back_n(utf16.size() + 1, CodeUnitFlags::kNoCodeUnitFlag); 37cb93a386Sopenharmony_ci this->fUnicode->forEachCodepoint((char16_t*)utf16.data(), utf16.size(), 38cb93a386Sopenharmony_ci [this](SkUnichar unichar, int32_t start, int32_t end) { 39cb93a386Sopenharmony_ci if (this->fUnicode->isWhitespace(unichar)) { 40cb93a386Sopenharmony_ci for (auto i = start; i < end; ++i) { 41cb93a386Sopenharmony_ci fCodeUnitProperties[i] |= CodeUnitFlags::kPartOfWhiteSpace; 42cb93a386Sopenharmony_ci } 43cb93a386Sopenharmony_ci } 44cb93a386Sopenharmony_ci }); 45cb93a386Sopenharmony_ci // Get graphemes 46cb93a386Sopenharmony_ci this->fUnicode->forEachBreak((char16_t*)utf16.data(), utf16.size(), SkUnicode::BreakType::kGraphemes, 47cb93a386Sopenharmony_ci [this](SkBreakIterator::Position pos, SkBreakIterator::Status) { 48cb93a386Sopenharmony_ci fCodeUnitProperties[pos]|= CodeUnitFlags::kGraphemeStart; 49cb93a386Sopenharmony_ci }); 50cb93a386Sopenharmony_ci // Get line breaks 51cb93a386Sopenharmony_ci this->fUnicode->forEachBreak((char16_t*)utf16.data(), utf16.size(), SkUnicode::BreakType::kLines, 52cb93a386Sopenharmony_ci [this](SkBreakIterator::Position pos, SkBreakIterator::Status status) { 53cb93a386Sopenharmony_ci if (status == (SkBreakIterator::Status)SkUnicode::LineBreakType::kHardLineBreak) { 54cb93a386Sopenharmony_ci // Hard line breaks clears off all the other flags 55cb93a386Sopenharmony_ci // TODO: Treat \n as a formatting mark and do not pass it to SkShaper 56cb93a386Sopenharmony_ci fCodeUnitProperties[pos - 1] = CodeUnitFlags::kHardLineBreakBefore; 57cb93a386Sopenharmony_ci } else { 58cb93a386Sopenharmony_ci fCodeUnitProperties[pos] |= CodeUnitFlags::kSoftLineBreakBefore; 59cb93a386Sopenharmony_ci } 60cb93a386Sopenharmony_ci }); 61cb93a386Sopenharmony_ci} 62cb93a386Sopenharmony_ci 63cb93a386Sopenharmony_cistd::unique_ptr<FontResolvedText> UnicodeText::resolveFonts(SkSpan<FontBlock> blocks) { 64cb93a386Sopenharmony_ci 65cb93a386Sopenharmony_ci auto fontResolvedText = std::make_unique<FontResolvedText>(); 66cb93a386Sopenharmony_ci 67cb93a386Sopenharmony_ci TextRange adjustedBlock(0, 0); 68cb93a386Sopenharmony_ci TextIndex index = 0; 69cb93a386Sopenharmony_ci for (auto& block : blocks) { 70cb93a386Sopenharmony_ci 71cb93a386Sopenharmony_ci index += block.charCount; 72cb93a386Sopenharmony_ci adjustedBlock.fStart = adjustedBlock.fEnd; 73cb93a386Sopenharmony_ci adjustedBlock.fEnd = index; 74cb93a386Sopenharmony_ci if (adjustedBlock.fStart >= adjustedBlock.fEnd) { 75cb93a386Sopenharmony_ci // The last block adjustment went over the entire block 76cb93a386Sopenharmony_ci continue; 77cb93a386Sopenharmony_ci } 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci // Move the end of the block to the right until it's on the grapheme edge 80cb93a386Sopenharmony_ci while (adjustedBlock.fEnd < this->fText16.size() && !this->hasProperty(adjustedBlock.fEnd, CodeUnitFlags::kGraphemeStart)) { 81cb93a386Sopenharmony_ci ++adjustedBlock.fEnd; 82cb93a386Sopenharmony_ci } 83cb93a386Sopenharmony_ci SkASSERT(block.type == BlockType::kFontChain); 84cb93a386Sopenharmony_ci fontResolvedText->resolveChain(this, adjustedBlock, *block.chain); 85cb93a386Sopenharmony_ci } 86cb93a386Sopenharmony_ci 87cb93a386Sopenharmony_ci std::sort(fontResolvedText->fResolvedFonts.begin(), fontResolvedText->fResolvedFonts.end(), 88cb93a386Sopenharmony_ci [](const ResolvedFontBlock& a, const ResolvedFontBlock& b) { 89cb93a386Sopenharmony_ci return a.textRange.fStart < b.textRange.fStart; 90cb93a386Sopenharmony_ci }); 91cb93a386Sopenharmony_ci/* 92cb93a386Sopenharmony_ci SkDebugf("Resolved:\n"); 93cb93a386Sopenharmony_ci for (auto& f : fontResolvedText->fResolvedFonts) { 94cb93a386Sopenharmony_ci SkDebugf("[%d:%d)\n", f.textRange.fStart, f.textRange.fEnd); 95cb93a386Sopenharmony_ci } 96cb93a386Sopenharmony_ci*/ 97cb93a386Sopenharmony_ci return std::move(fontResolvedText); 98cb93a386Sopenharmony_ci} 99cb93a386Sopenharmony_ci 100cb93a386Sopenharmony_cibool FontResolvedText::resolveChain(UnicodeText* unicodeText, TextRange textRange, const FontChain& fontChain) { 101cb93a386Sopenharmony_ci 102cb93a386Sopenharmony_ci std::deque<TextRange> unresolvedTexts; 103cb93a386Sopenharmony_ci unresolvedTexts.push_back(textRange); 104cb93a386Sopenharmony_ci for (auto fontIndex = 0; fontIndex < fontChain.count(); ++fontIndex) { 105cb93a386Sopenharmony_ci auto typeface = fontChain[fontIndex]; 106cb93a386Sopenharmony_ci 107cb93a386Sopenharmony_ci std::deque<TextRange> newUnresolvedTexts; 108cb93a386Sopenharmony_ci // Check all text range that have not been resolved yet 109cb93a386Sopenharmony_ci while (!unresolvedTexts.empty()) { 110cb93a386Sopenharmony_ci // Take the first unresolved 111cb93a386Sopenharmony_ci auto unresolvedText = unresolvedTexts.front(); 112cb93a386Sopenharmony_ci unresolvedTexts.pop_front(); 113cb93a386Sopenharmony_ci 114cb93a386Sopenharmony_ci // Resolve font for the entire grapheme 115cb93a386Sopenharmony_ci auto start = newUnresolvedTexts.size(); 116cb93a386Sopenharmony_ci unicodeText->forEachGrapheme(unresolvedText, [&](TextRange grapheme) { 117cb93a386Sopenharmony_ci auto count = typeface->textToGlyphs(unicodeText->getText16().data() + grapheme.fStart, grapheme.width() * 2, SkTextEncoding::kUTF16, nullptr, 0); 118cb93a386Sopenharmony_ci SkAutoTArray<SkGlyphID> glyphs(count); 119cb93a386Sopenharmony_ci typeface->textToGlyphs(unicodeText->getText16().data() + grapheme.fStart, grapheme.width() * 2, SkTextEncoding::kUTF16, glyphs.data(), count); 120cb93a386Sopenharmony_ci for (auto i = 0; i < count; ++i) { 121cb93a386Sopenharmony_ci if (glyphs[i] == 0) { 122cb93a386Sopenharmony_ci if (newUnresolvedTexts.empty() || newUnresolvedTexts.back().fEnd < grapheme.fStart) { 123cb93a386Sopenharmony_ci // It's a new unresolved block 124cb93a386Sopenharmony_ci newUnresolvedTexts.push_back(grapheme); 125cb93a386Sopenharmony_ci } else { 126cb93a386Sopenharmony_ci // Let's extend the last unresolved block 127cb93a386Sopenharmony_ci newUnresolvedTexts.back().fEnd = grapheme.fEnd; 128cb93a386Sopenharmony_ci } 129cb93a386Sopenharmony_ci break; 130cb93a386Sopenharmony_ci } 131cb93a386Sopenharmony_ci } 132cb93a386Sopenharmony_ci }); 133cb93a386Sopenharmony_ci // Let's fill the resolved blocks with the current font 134cb93a386Sopenharmony_ci TextRange resolvedText(unresolvedText.fStart, unresolvedText.fStart); 135cb93a386Sopenharmony_ci for (auto newUnresolvedText : newUnresolvedTexts) { 136cb93a386Sopenharmony_ci if (start > 0) { 137cb93a386Sopenharmony_ci --start; 138cb93a386Sopenharmony_ci continue; 139cb93a386Sopenharmony_ci } 140cb93a386Sopenharmony_ci resolvedText.fEnd = newUnresolvedText.fStart; 141cb93a386Sopenharmony_ci if (resolvedText.width() > 0) { 142cb93a386Sopenharmony_ci // Add another resolved block 143cb93a386Sopenharmony_ci fResolvedFonts.emplace_back(resolvedText, typeface, fontChain.fontSize(), SkFontStyle::Normal()); 144cb93a386Sopenharmony_ci } 145cb93a386Sopenharmony_ci resolvedText.fStart = newUnresolvedText.fEnd; 146cb93a386Sopenharmony_ci } 147cb93a386Sopenharmony_ci resolvedText.fEnd = unresolvedText.fEnd; 148cb93a386Sopenharmony_ci if (resolvedText.width() > 0) { 149cb93a386Sopenharmony_ci // Add the last resolved block 150cb93a386Sopenharmony_ci fResolvedFonts.emplace_back(resolvedText, typeface, fontChain.fontSize(), SkFontStyle::Normal()); 151cb93a386Sopenharmony_ci } 152cb93a386Sopenharmony_ci } 153cb93a386Sopenharmony_ci 154cb93a386Sopenharmony_ci // Try the next font in chain 155cb93a386Sopenharmony_ci SkASSERT(unresolvedTexts.empty()); 156cb93a386Sopenharmony_ci unresolvedTexts = std::move(newUnresolvedTexts); 157cb93a386Sopenharmony_ci } 158cb93a386Sopenharmony_ci 159cb93a386Sopenharmony_ci return unresolvedTexts.empty(); 160cb93a386Sopenharmony_ci} 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_ci// Font iterator that finds all formatting marks 163cb93a386Sopenharmony_ci// and breaks runs on them (so we can select and interpret them later) 164cb93a386Sopenharmony_ciclass FormattingFontIterator final : public SkShaper::FontRunIterator { 165cb93a386Sopenharmony_cipublic: 166cb93a386Sopenharmony_ci FormattingFontIterator(TextIndex textCount, 167cb93a386Sopenharmony_ci SkSpan<ResolvedFontBlock> fontBlocks, 168cb93a386Sopenharmony_ci SkSpan<TextIndex> marks) 169cb93a386Sopenharmony_ci : fTextCount(textCount) 170cb93a386Sopenharmony_ci , fFontBlocks(fontBlocks) 171cb93a386Sopenharmony_ci , fFormattingMarks(marks) 172cb93a386Sopenharmony_ci , fCurrentBlock(fontBlocks.begin()) 173cb93a386Sopenharmony_ci , fCurrentMark(marks.begin()) 174cb93a386Sopenharmony_ci , fCurrentFontIndex(fCurrentBlock->textRange.fEnd) { 175cb93a386Sopenharmony_ci fCurrentFont = this->createFont(*fCurrentBlock); 176cb93a386Sopenharmony_ci } 177cb93a386Sopenharmony_ci void consume() override { 178cb93a386Sopenharmony_ci SkASSERT(fCurrentBlock < fFontBlocks.end()); 179cb93a386Sopenharmony_ci SkASSERT(fCurrentMark < fFormattingMarks.end()); 180cb93a386Sopenharmony_ci if (fCurrentFontIndex > *fCurrentMark) { 181cb93a386Sopenharmony_ci ++fCurrentMark; 182cb93a386Sopenharmony_ci return; 183cb93a386Sopenharmony_ci } 184cb93a386Sopenharmony_ci if (fCurrentFontIndex == *fCurrentMark) { 185cb93a386Sopenharmony_ci ++fCurrentMark; 186cb93a386Sopenharmony_ci } 187cb93a386Sopenharmony_ci ++fCurrentBlock; 188cb93a386Sopenharmony_ci if (fCurrentBlock < fFontBlocks.end()) { 189cb93a386Sopenharmony_ci fCurrentFontIndex = fCurrentBlock->textRange.fEnd; 190cb93a386Sopenharmony_ci fCurrentFont = this->createFont(*fCurrentBlock); 191cb93a386Sopenharmony_ci } 192cb93a386Sopenharmony_ci } 193cb93a386Sopenharmony_ci size_t endOfCurrentRun() const override { 194cb93a386Sopenharmony_ci SkASSERT(fCurrentMark != fFormattingMarks.end() || fCurrentBlock != fFontBlocks.end()); 195cb93a386Sopenharmony_ci if (fCurrentMark == fFormattingMarks.end()) { 196cb93a386Sopenharmony_ci return fCurrentFontIndex; 197cb93a386Sopenharmony_ci } else if (fCurrentBlock == fFontBlocks.end()) { 198cb93a386Sopenharmony_ci return *fCurrentMark; 199cb93a386Sopenharmony_ci } else { 200cb93a386Sopenharmony_ci return std::min(fCurrentFontIndex, *fCurrentMark); 201cb93a386Sopenharmony_ci } 202cb93a386Sopenharmony_ci } 203cb93a386Sopenharmony_ci bool atEnd() const override { 204cb93a386Sopenharmony_ci return (fCurrentBlock == fFontBlocks.end() || fCurrentFontIndex == fTextCount) && 205cb93a386Sopenharmony_ci (fCurrentMark == fFormattingMarks.end() || *fCurrentMark == fTextCount); 206cb93a386Sopenharmony_ci } 207cb93a386Sopenharmony_ci const SkFont& currentFont() const override { return fCurrentFont; } 208cb93a386Sopenharmony_ci SkFont createFont(const ResolvedFontBlock& resolvedFont) const { 209cb93a386Sopenharmony_ci SkFont font(resolvedFont.typeface, resolvedFont.size); 210cb93a386Sopenharmony_ci font.setEdging(SkFont::Edging::kAntiAlias); 211cb93a386Sopenharmony_ci font.setHinting(SkFontHinting::kSlight); 212cb93a386Sopenharmony_ci font.setSubpixel(true); 213cb93a386Sopenharmony_ci return font; 214cb93a386Sopenharmony_ci } 215cb93a386Sopenharmony_ciprivate: 216cb93a386Sopenharmony_ci TextIndex const fTextCount; 217cb93a386Sopenharmony_ci SkSpan<ResolvedFontBlock> fFontBlocks; 218cb93a386Sopenharmony_ci SkSpan<TextIndex> fFormattingMarks; 219cb93a386Sopenharmony_ci ResolvedFontBlock* fCurrentBlock; 220cb93a386Sopenharmony_ci TextIndex* fCurrentMark; 221cb93a386Sopenharmony_ci TextIndex fCurrentFontIndex; 222cb93a386Sopenharmony_ci SkFont fCurrentFont; 223cb93a386Sopenharmony_ci}; 224cb93a386Sopenharmony_ci 225cb93a386Sopenharmony_cistd::unique_ptr<ShapedText> FontResolvedText::shape(UnicodeText* unicodeText, 226cb93a386Sopenharmony_ci TextDirection textDirection) { 227cb93a386Sopenharmony_ci // Get utf8 <-> utf16 conversion tables. 228cb93a386Sopenharmony_ci // We need to pass to SkShaper indices in utf8 and then convert them back to utf16 for SkText 229cb93a386Sopenharmony_ci auto text16 = unicodeText->getText16(); 230cb93a386Sopenharmony_ci auto text8 = SkUnicode::convertUtf16ToUtf8(std::u16string(text16.data(), text16.size())); 231cb93a386Sopenharmony_ci size_t utf16Index = 0; 232cb93a386Sopenharmony_ci SkTArray<size_t, true> UTF16FromUTF8; 233cb93a386Sopenharmony_ci SkTArray<size_t, true> UTF8FromUTF16; 234cb93a386Sopenharmony_ci UTF16FromUTF8.push_back_n(text8.size() + 1, utf16Index); 235cb93a386Sopenharmony_ci UTF8FromUTF16.push_back_n(text16.size() + 1, utf16Index); 236cb93a386Sopenharmony_ci unicodeText->getUnicode()->forEachCodepoint(text8.c_str(), text8.size(), 237cb93a386Sopenharmony_ci [&](SkUnichar unichar, int32_t start, int32_t end, int32_t count) { 238cb93a386Sopenharmony_ci // utf8 index group of 1, 2 or 3 can be represented with one utf16 index group 239cb93a386Sopenharmony_ci for (auto i = start; i < end; ++i) { 240cb93a386Sopenharmony_ci UTF16FromUTF8[i] = utf16Index; 241cb93a386Sopenharmony_ci } 242cb93a386Sopenharmony_ci // utf16 index group of 1 or 2 can refer to the same group of utf8 indices 243cb93a386Sopenharmony_ci for (; count != 0; --count) { 244cb93a386Sopenharmony_ci UTF8FromUTF16[utf16Index++] = start; 245cb93a386Sopenharmony_ci } 246cb93a386Sopenharmony_ci }); 247cb93a386Sopenharmony_ci UTF16FromUTF8[text8.size()] = text16.size(); 248cb93a386Sopenharmony_ci UTF8FromUTF16[text16.size()] = text8.size(); 249cb93a386Sopenharmony_ci // Break text into pieces by font blocks and by formatting marks 250cb93a386Sopenharmony_ci // Formatting marks: \n (and possibly some other later) 251cb93a386Sopenharmony_ci std::vector<size_t> formattingMarks; 252cb93a386Sopenharmony_ci for (size_t i = 0; i < text16.size(); ++i) { 253cb93a386Sopenharmony_ci if (unicodeText->isHardLineBreak(i)) { 254cb93a386Sopenharmony_ci formattingMarks.emplace_back(UTF8FromUTF16[i]); 255cb93a386Sopenharmony_ci formattingMarks.emplace_back(UTF8FromUTF16[i + 1]); 256cb93a386Sopenharmony_ci ++i; 257cb93a386Sopenharmony_ci } 258cb93a386Sopenharmony_ci } 259cb93a386Sopenharmony_ci formattingMarks.emplace_back(text8.size()/* UTF8FromUTF16[text16.size() */); 260cb93a386Sopenharmony_ci // Convert fontBlocks from utf16 to utf8 261cb93a386Sopenharmony_ci SkTArray<ResolvedFontBlock, true> fontBlocks8; 262cb93a386Sopenharmony_ci TextIndex index8 = 0; 263cb93a386Sopenharmony_ci for (auto& fb : fResolvedFonts) { 264cb93a386Sopenharmony_ci TextRange text8(UTF8FromUTF16[fb.textRange.fStart], UTF8FromUTF16[fb.textRange.fEnd]); 265cb93a386Sopenharmony_ci fontBlocks8.emplace_back(text8, fb.typeface, fb.size, fb.style); 266cb93a386Sopenharmony_ci } 267cb93a386Sopenharmony_ci auto shapedText = std::make_unique<ShapedText>(); 268cb93a386Sopenharmony_ci // Shape the text 269cb93a386Sopenharmony_ci FormattingFontIterator fontIter(text8.size(), 270cb93a386Sopenharmony_ci SkSpan<ResolvedFontBlock>(fontBlocks8.data(), fontBlocks8.size()), 271cb93a386Sopenharmony_ci SkSpan<TextIndex>(&formattingMarks[0], formattingMarks.size())); 272cb93a386Sopenharmony_ci SkShaper::TrivialLanguageRunIterator langIter(text8.c_str(), text8.size()); 273cb93a386Sopenharmony_ci std::unique_ptr<SkShaper::BiDiRunIterator> bidiIter( 274cb93a386Sopenharmony_ci SkShaper::MakeSkUnicodeBidiRunIterator( 275cb93a386Sopenharmony_ci unicodeText->getUnicode(), text8.c_str(), text8.size(), textDirection == TextDirection::kLtr ? 0 : 1)); 276cb93a386Sopenharmony_ci std::unique_ptr<SkShaper::ScriptRunIterator> scriptIter( 277cb93a386Sopenharmony_ci SkShaper::MakeSkUnicodeHbScriptRunIterator(unicodeText->getUnicode(), text8.c_str(), text8.size())); 278cb93a386Sopenharmony_ci auto shaper = SkShaper::MakeShapeDontWrapOrReorder(); 279cb93a386Sopenharmony_ci if (shaper == nullptr) { 280cb93a386Sopenharmony_ci // For instance, loadICU does not work. We have to stop the process 281cb93a386Sopenharmony_ci return nullptr; 282cb93a386Sopenharmony_ci } 283cb93a386Sopenharmony_ci shaper->shape( 284cb93a386Sopenharmony_ci text8.c_str(), text8.size(), 285cb93a386Sopenharmony_ci fontIter, *bidiIter, *scriptIter, langIter, 286cb93a386Sopenharmony_ci std::numeric_limits<SkScalar>::max(), shapedText.get()); 287cb93a386Sopenharmony_ci if (shapedText->fLogicalRuns.empty()) { 288cb93a386Sopenharmony_ci // Create a fake run for an empty text (to avoid all the checks) 289cb93a386Sopenharmony_ci SkShaper::RunHandler::RunInfo emptyInfo { 290cb93a386Sopenharmony_ci fontIter.createFont(fResolvedFonts.front()), 291cb93a386Sopenharmony_ci 0, 292cb93a386Sopenharmony_ci SkVector::Make(0.0f, 0.0f), 293cb93a386Sopenharmony_ci 0, 294cb93a386Sopenharmony_ci SkShaper::RunHandler::Range(0, 0) 295cb93a386Sopenharmony_ci }; 296cb93a386Sopenharmony_ci shapedText->fLogicalRuns.emplace_back(emptyInfo, 0, 0.0f); 297cb93a386Sopenharmony_ci shapedText->fLogicalRuns.front().commit(); 298cb93a386Sopenharmony_ci } 299cb93a386Sopenharmony_ci // Fill out all code unit properties 300cb93a386Sopenharmony_ci for (auto& logicalRun : shapedText->fLogicalRuns) { 301cb93a386Sopenharmony_ci // Convert utf8 range into utf16 range 302cb93a386Sopenharmony_ci logicalRun.convertUtf16Range([&](unsigned long index8) { 303cb93a386Sopenharmony_ci return UTF16FromUTF8[index8]; 304cb93a386Sopenharmony_ci }); 305cb93a386Sopenharmony_ci // Convert all utf8 indexes into utf16 indexes (and also shift them to be on the entire text scale, too) 306cb93a386Sopenharmony_ci logicalRun.convertClusterIndexes([&](TextIndex clusterIndex8) { 307cb93a386Sopenharmony_ci return UTF16FromUTF8[clusterIndex8]; 308cb93a386Sopenharmony_ci }); 309cb93a386Sopenharmony_ci // Detect and mark line break runs 310cb93a386Sopenharmony_ci if (logicalRun.getTextRange().width() == 1 && 311cb93a386Sopenharmony_ci logicalRun.size() == 1 && 312cb93a386Sopenharmony_ci unicodeText->isHardLineBreak(logicalRun.getTextRange().fStart)) { 313cb93a386Sopenharmony_ci logicalRun.setRunType(LogicalRunType::kLineBreak); 314cb93a386Sopenharmony_ci } 315cb93a386Sopenharmony_ci } 316cb93a386Sopenharmony_ci return std::move(shapedText); 317cb93a386Sopenharmony_ci} 318cb93a386Sopenharmony_ci 319cb93a386Sopenharmony_ci// TODO: Implement the vertical restriction (height) and add ellipsis 320cb93a386Sopenharmony_cistd::unique_ptr<WrappedText> ShapedText::wrap(UnicodeText* unicodeText, float width, float height) { 321cb93a386Sopenharmony_ci auto wrappedText = std::unique_ptr<WrappedText>(new WrappedText()); 322cb93a386Sopenharmony_ci // line + spaces + clusters 323cb93a386Sopenharmony_ci Stretch line; 324cb93a386Sopenharmony_ci Stretch spaces; 325cb93a386Sopenharmony_ci Stretch clusters; 326cb93a386Sopenharmony_ci Stretch cluster; 327cb93a386Sopenharmony_ci for (size_t runIndex = 0; runIndex < this->fLogicalRuns.size(); ++runIndex ) { 328cb93a386Sopenharmony_ci auto& run = this->fLogicalRuns[runIndex]; 329cb93a386Sopenharmony_ci if (run.getRunType() == LogicalRunType::kLineBreak) { 330cb93a386Sopenharmony_ci // This is the end of the word, the end of the line 331cb93a386Sopenharmony_ci if (!clusters.isEmpty()) { 332cb93a386Sopenharmony_ci line.moveTo(spaces); 333cb93a386Sopenharmony_ci line.moveTo(clusters); 334cb93a386Sopenharmony_ci spaces = clusters; 335cb93a386Sopenharmony_ci } 336cb93a386Sopenharmony_ci this->addLine(wrappedText.get(), unicodeText->getUnicode(), line, spaces, true); 337cb93a386Sopenharmony_ci line = spaces; 338cb93a386Sopenharmony_ci clusters = spaces; 339cb93a386Sopenharmony_ci continue; 340cb93a386Sopenharmony_ci } 341cb93a386Sopenharmony_ci TextMetrics runMetrics(run.fFont); 342cb93a386Sopenharmony_ci 343cb93a386Sopenharmony_ci // Let's wrap the text 344cb93a386Sopenharmony_ci GlyphRange clusterGlyphs; 345cb93a386Sopenharmony_ci DirTextRange clusterText(EMPTY_RANGE, run.leftToRight()); 346cb93a386Sopenharmony_ci for (size_t glyphIndex = 0; glyphIndex < run.fPositions.size(); ++glyphIndex) { 347cb93a386Sopenharmony_ci auto textIndex = run.fClusters[glyphIndex]; 348cb93a386Sopenharmony_ci 349cb93a386Sopenharmony_ci if (clusterText == EMPTY_RANGE) { 350cb93a386Sopenharmony_ci // The beginning of a new line (or the first one) 351cb93a386Sopenharmony_ci clusterText = DirTextRange(textIndex, textIndex, run.leftToRight()); 352cb93a386Sopenharmony_ci clusterGlyphs = GlyphRange(glyphIndex, glyphIndex); 353cb93a386Sopenharmony_ci 354cb93a386Sopenharmony_ci Stretch empty(GlyphPos(runIndex, glyphIndex), textIndex, runMetrics); 355cb93a386Sopenharmony_ci line = empty; 356cb93a386Sopenharmony_ci spaces = empty; 357cb93a386Sopenharmony_ci clusters = empty; 358cb93a386Sopenharmony_ci continue; 359cb93a386Sopenharmony_ci } 360cb93a386Sopenharmony_ci 361cb93a386Sopenharmony_ci if (textIndex == clusterText.fStart) { 362cb93a386Sopenharmony_ci // Skip until the next cluster 363cb93a386Sopenharmony_ci continue; 364cb93a386Sopenharmony_ci } 365cb93a386Sopenharmony_ci 366cb93a386Sopenharmony_ci // Finish the cluster (notice that it belongs to a single run) 367cb93a386Sopenharmony_ci clusterText.fStart = clusterText.fEnd; 368cb93a386Sopenharmony_ci clusterText.fEnd = textIndex; 369cb93a386Sopenharmony_ci clusterGlyphs.fStart = clusterGlyphs.fEnd; 370cb93a386Sopenharmony_ci clusterGlyphs.fEnd = glyphIndex; 371cb93a386Sopenharmony_ci cluster = Stretch(runIndex, clusterGlyphs, clusterText.normalized(), run.calculateWidth(clusterGlyphs), runMetrics); 372cb93a386Sopenharmony_ci 373cb93a386Sopenharmony_ci auto isSoftLineBreak = unicodeText->isSoftLineBreak(cluster.textStart()); 374cb93a386Sopenharmony_ci auto isWhitespaces = unicodeText->isWhitespaces(cluster.textRange()); 375cb93a386Sopenharmony_ci auto isEndOfText = run.leftToRight() ? textIndex == run.fUtf16Range.fEnd : textIndex == run.fUtf16Range.fStart; 376cb93a386Sopenharmony_ci // line + spaces + clusters + cluster 377cb93a386Sopenharmony_ci if (isWhitespaces) { 378cb93a386Sopenharmony_ci // This is the end of the word 379cb93a386Sopenharmony_ci if (!clusters.isEmpty()) { 380cb93a386Sopenharmony_ci line.moveTo(spaces); 381cb93a386Sopenharmony_ci line.moveTo(clusters); 382cb93a386Sopenharmony_ci spaces = clusters; 383cb93a386Sopenharmony_ci } 384cb93a386Sopenharmony_ci spaces.moveTo(cluster); 385cb93a386Sopenharmony_ci clusters = cluster; 386cb93a386Sopenharmony_ci // Whitespaces do not extend the line width so no wrapping 387cb93a386Sopenharmony_ci continue; 388cb93a386Sopenharmony_ci } else if (!SkScalarIsFinite(width)) { 389cb93a386Sopenharmony_ci // No wrapping - the endless line 390cb93a386Sopenharmony_ci clusters.moveTo(cluster); 391cb93a386Sopenharmony_ci continue; 392cb93a386Sopenharmony_ci } 393cb93a386Sopenharmony_ci // Now let's find out if we can add the cluster to the line 394cb93a386Sopenharmony_ci auto currentWidth = line.width() + spaces.width() + clusters.width() + cluster.width(); 395cb93a386Sopenharmony_ci if (currentWidth > width) { 396cb93a386Sopenharmony_ci // Finally, the wrapping case 397cb93a386Sopenharmony_ci if (line.isEmpty()) { 398cb93a386Sopenharmony_ci if (spaces.isEmpty() && clusters.isEmpty()) { 399cb93a386Sopenharmony_ci // There is only this cluster and it's too long; we are drawing it anyway 400cb93a386Sopenharmony_ci line.moveTo(cluster); 401cb93a386Sopenharmony_ci } else { 402cb93a386Sopenharmony_ci // We break the only one word on the line by this cluster 403cb93a386Sopenharmony_ci line.moveTo(clusters); 404cb93a386Sopenharmony_ci } 405cb93a386Sopenharmony_ci } else { 406cb93a386Sopenharmony_ci // We move clusters + cluster on the next line 407cb93a386Sopenharmony_ci // TODO: Parametrise possible ways of breaking too long word 408cb93a386Sopenharmony_ci // (start it from a new line or squeeze the part of it on this line) 409cb93a386Sopenharmony_ci } 410cb93a386Sopenharmony_ci this->addLine(wrappedText.get(), unicodeText->getUnicode(), line, spaces, false); 411cb93a386Sopenharmony_ci line = spaces; 412cb93a386Sopenharmony_ci } 413cb93a386Sopenharmony_ci clusters.moveTo(cluster); 414cb93a386Sopenharmony_ci 415cb93a386Sopenharmony_ci clusterGlyphs.fStart = clusterGlyphs.fEnd; 416cb93a386Sopenharmony_ci clusterText.fStart = clusterText.fEnd; 417cb93a386Sopenharmony_ci } 418cb93a386Sopenharmony_ci } 419cb93a386Sopenharmony_ci 420cb93a386Sopenharmony_ci // Deal with the last line 421cb93a386Sopenharmony_ci if (!clusters.isEmpty()) { 422cb93a386Sopenharmony_ci line.moveTo(spaces); 423cb93a386Sopenharmony_ci line.moveTo(clusters); 424cb93a386Sopenharmony_ci spaces = clusters; 425cb93a386Sopenharmony_ci } else if (wrappedText->fVisualLines.empty()) { 426cb93a386Sopenharmony_ci // Empty text; we still need a line to avoid checking for empty lines every time 427cb93a386Sopenharmony_ci line.moveTo(cluster); 428cb93a386Sopenharmony_ci spaces.moveTo(cluster); 429cb93a386Sopenharmony_ci } 430cb93a386Sopenharmony_ci this->addLine(wrappedText.get(), unicodeText->getUnicode(), line, spaces, false); 431cb93a386Sopenharmony_ci wrappedText->fActualSize.fWidth = width; 432cb93a386Sopenharmony_ci return std::move(wrappedText); 433cb93a386Sopenharmony_ci} 434cb93a386Sopenharmony_ci 435cb93a386Sopenharmony_ciSkTArray<int32_t> ShapedText::getVisualOrder(SkUnicode* unicode, RunIndex startRun, RunIndex endRun) { 436cb93a386Sopenharmony_ci auto numRuns = endRun - startRun + 1; 437cb93a386Sopenharmony_ci SkTArray<int32_t> results; 438cb93a386Sopenharmony_ci results.push_back_n(numRuns); 439cb93a386Sopenharmony_ci if (numRuns == 0) { 440cb93a386Sopenharmony_ci return results; 441cb93a386Sopenharmony_ci } 442cb93a386Sopenharmony_ci SkTArray<SkUnicode::BidiLevel> runLevels; 443cb93a386Sopenharmony_ci runLevels.push_back_n(numRuns); 444cb93a386Sopenharmony_ci size_t runLevelsIndex = 0; 445cb93a386Sopenharmony_ci for (RunIndex runIndex = startRun; runIndex <= endRun; ++runIndex) { 446cb93a386Sopenharmony_ci runLevels[runLevelsIndex++] = fLogicalRuns[runIndex].bidiLevel(); 447cb93a386Sopenharmony_ci } 448cb93a386Sopenharmony_ci SkASSERT(runLevelsIndex == numRuns); 449cb93a386Sopenharmony_ci unicode->reorderVisual(runLevels.data(), numRuns, results.data()); 450cb93a386Sopenharmony_ci return results; 451cb93a386Sopenharmony_ci} 452cb93a386Sopenharmony_ci 453cb93a386Sopenharmony_ci// TODO: Fill line fOffset.fY 454cb93a386Sopenharmony_civoid ShapedText::addLine(WrappedText* wrappedText, SkUnicode* unicode, Stretch& stretch, Stretch& spaces, bool hardLineBreak) { 455cb93a386Sopenharmony_ci auto spacesStart = spaces.glyphStart(); 456cb93a386Sopenharmony_ci Stretch lineStretch = stretch; 457cb93a386Sopenharmony_ci lineStretch.moveTo(spaces); 458cb93a386Sopenharmony_ci auto startRun = lineStretch.glyphStart().runIndex(); 459cb93a386Sopenharmony_ci auto endRun = lineStretch.glyphEnd().runIndex(); 460cb93a386Sopenharmony_ci // Reorder and cut (if needed) runs so they fit the line 461cb93a386Sopenharmony_ci auto visualOrder = std::move(this->getVisualOrder(unicode, startRun, endRun)); 462cb93a386Sopenharmony_ci // Walk through the line's runs in visual order 463cb93a386Sopenharmony_ci auto firstRunIndex = startRun; 464cb93a386Sopenharmony_ci auto runStart = wrappedText->fVisualRuns.size(); 465cb93a386Sopenharmony_ci SkScalar runOffsetInLine = 0.0f; 466cb93a386Sopenharmony_ci for (auto visualIndex : visualOrder) { 467cb93a386Sopenharmony_ci auto& logicalRun = fLogicalRuns[firstRunIndex + visualIndex]; 468cb93a386Sopenharmony_ci if (logicalRun.getRunType() == LogicalRunType::kLineBreak) { 469cb93a386Sopenharmony_ci SkASSERT(false); 470cb93a386Sopenharmony_ci } 471cb93a386Sopenharmony_ci bool isFirstRun = startRun == (firstRunIndex + visualIndex); 472cb93a386Sopenharmony_ci bool isLastRun = endRun == (firstRunIndex + visualIndex); 473cb93a386Sopenharmony_ci bool isSpaceRun = spacesStart.runIndex() == (firstRunIndex + visualIndex); 474cb93a386Sopenharmony_ci auto glyphStart = isFirstRun ? lineStretch.glyphStart().glyphIndex() : 0; 475cb93a386Sopenharmony_ci auto glyphEnd = isLastRun ? lineStretch.glyphEnd().glyphIndex() : logicalRun.size(); 476cb93a386Sopenharmony_ci auto glyphSize = glyphEnd - glyphStart; 477cb93a386Sopenharmony_ci auto glyphSpaces = isSpaceRun ? spacesStart.glyphIndex() : glyphEnd; 478cb93a386Sopenharmony_ci if (glyphSpaces > glyphStart) { 479cb93a386Sopenharmony_ci auto textStart = isFirstRun ? lineStretch.textRange().fStart : logicalRun.fUtf16Range.fStart; 480cb93a386Sopenharmony_ci auto textEnd = isLastRun ? lineStretch.textRange().fEnd : logicalRun.fUtf16Range.fEnd; 481cb93a386Sopenharmony_ci wrappedText->fVisualRuns.emplace_back(TextRange(textStart, textEnd), 482cb93a386Sopenharmony_ci glyphSpaces - glyphStart, 483cb93a386Sopenharmony_ci logicalRun.fFont, 484cb93a386Sopenharmony_ci lineStretch.textMetrics().baseline(), 485cb93a386Sopenharmony_ci SkPoint::Make(runOffsetInLine, wrappedText->fActualSize.fHeight), 486cb93a386Sopenharmony_ci logicalRun.leftToRight(), 487cb93a386Sopenharmony_ci SkSpan<SkPoint>(&logicalRun.fPositions[glyphStart], glyphSize + 1), 488cb93a386Sopenharmony_ci SkSpan<SkGlyphID>(&logicalRun.fGlyphs[glyphStart], glyphSize), 489cb93a386Sopenharmony_ci SkSpan<uint32_t>((uint32_t*)&logicalRun.fClusters[glyphStart], glyphSize + 1)); 490cb93a386Sopenharmony_ci } 491cb93a386Sopenharmony_ci runOffsetInLine += logicalRun.calculateWidth(glyphStart, glyphEnd); 492cb93a386Sopenharmony_ci } 493cb93a386Sopenharmony_ci auto runRange = wrappedText->fVisualRuns.size() == runStart 494cb93a386Sopenharmony_ci ? SkSpan<VisualRun>(nullptr, 0) 495cb93a386Sopenharmony_ci : SkSpan<VisualRun>(&wrappedText->fVisualRuns[runStart], wrappedText->fVisualRuns.size() - runStart); 496cb93a386Sopenharmony_ci wrappedText->fVisualLines.emplace_back(lineStretch.textRange(), hardLineBreak, wrappedText->fActualSize.fHeight, runRange); 497cb93a386Sopenharmony_ci wrappedText->fActualSize.fHeight += lineStretch.textMetrics().height(); 498cb93a386Sopenharmony_ci wrappedText->fActualSize.fWidth = std::max(wrappedText->fActualSize.fWidth, lineStretch.width()); 499cb93a386Sopenharmony_ci stretch.clean(); 500cb93a386Sopenharmony_ci spaces.clean(); 501cb93a386Sopenharmony_ci} 502cb93a386Sopenharmony_ci 503cb93a386Sopenharmony_civoid WrappedText::format(TextAlign textAlign, TextDirection textDirection) { 504cb93a386Sopenharmony_ci if (fAligned == textAlign) { 505cb93a386Sopenharmony_ci return; 506cb93a386Sopenharmony_ci } 507cb93a386Sopenharmony_ci SkScalar verticalOffset = 0.0f; 508cb93a386Sopenharmony_ci for (auto& line : this->fVisualLines) { 509cb93a386Sopenharmony_ci if (textAlign == TextAlign::kLeft) { 510cb93a386Sopenharmony_ci // Good by default 511cb93a386Sopenharmony_ci } else if (textAlign == TextAlign::kCenter) { 512cb93a386Sopenharmony_ci line.fOffset.fX = (this->fActualSize.width() - line.fActualWidth) / 2.0f; 513cb93a386Sopenharmony_ci } else { 514cb93a386Sopenharmony_ci // TODO: Implement all formatting features 515cb93a386Sopenharmony_ci } 516cb93a386Sopenharmony_ci line.fOffset.fY = verticalOffset; 517cb93a386Sopenharmony_ci verticalOffset += line.fTextMetrics.height(); 518cb93a386Sopenharmony_ci } 519cb93a386Sopenharmony_ci} 520cb93a386Sopenharmony_ci 521cb93a386Sopenharmony_civoid WrappedText::visit(Visitor* visitor) const { 522cb93a386Sopenharmony_ci size_t lineIndex = 0; 523cb93a386Sopenharmony_ci SkScalar verticalOffset = 0.0f; 524cb93a386Sopenharmony_ci for (auto& line : fVisualLines) { 525cb93a386Sopenharmony_ci visitor->onBeginLine(lineIndex, line.text(), line.isHardBreak(), SkRect::MakeXYWH(0, verticalOffset, line.fActualWidth, line.fTextMetrics.height())); 526cb93a386Sopenharmony_ci // Select the runs that are on the line 527cb93a386Sopenharmony_ci size_t glyphCount = 0ul; 528cb93a386Sopenharmony_ci for (auto& run : line.fRuns) { 529cb93a386Sopenharmony_ci auto diff = line.fTextMetrics.above() - run.fTextMetrics.above(); 530cb93a386Sopenharmony_ci SkRect boundingRect = SkRect::MakeXYWH(line.fOffset.fX + run.fPositions[0].fX, line.fOffset.fY + diff, run.width(), run.fTextMetrics.height()); 531cb93a386Sopenharmony_ci visitor->onGlyphRun(run.fFont, run.dirTextRange(), boundingRect, run.trailingSpacesStart(), 532cb93a386Sopenharmony_ci run.size(), run.fGlyphs.data(), run.fPositions.data(), run.fClusters.data()); 533cb93a386Sopenharmony_ci glyphCount += run.size(); 534cb93a386Sopenharmony_ci } 535cb93a386Sopenharmony_ci visitor->onEndLine(lineIndex, line.text(), line.trailingSpaces(), glyphCount); 536cb93a386Sopenharmony_ci verticalOffset += line.fTextMetrics.height(); 537cb93a386Sopenharmony_ci ++lineIndex; 538cb93a386Sopenharmony_ci } 539cb93a386Sopenharmony_ci} 540cb93a386Sopenharmony_ci 541cb93a386Sopenharmony_cistd::vector<TextIndex> WrappedText::chunksToBlocks(SkSpan<size_t> chunks) { 542cb93a386Sopenharmony_ci std::vector<TextIndex> blocks; 543cb93a386Sopenharmony_ci blocks.reserve(chunks.size() + 1); 544cb93a386Sopenharmony_ci TextIndex index = 0; 545cb93a386Sopenharmony_ci for (auto chunk : chunks) { 546cb93a386Sopenharmony_ci blocks.emplace_back(index); 547cb93a386Sopenharmony_ci index += chunk; 548cb93a386Sopenharmony_ci } 549cb93a386Sopenharmony_ci blocks.emplace_back(index); 550cb93a386Sopenharmony_ci return std::move(blocks); 551cb93a386Sopenharmony_ci} 552cb93a386Sopenharmony_ci 553cb93a386Sopenharmony_ciSkSpan<TextIndex> WrappedText::limitBlocks(TextRange textRange, SkSpan<TextIndex> blocks) { 554cb93a386Sopenharmony_ci TextRange limited = EMPTY_RANGE; 555cb93a386Sopenharmony_ci for (auto i = 0ul; i < blocks.size(); ++i) { 556cb93a386Sopenharmony_ci auto block = blocks[i]; 557cb93a386Sopenharmony_ci if (textRange.fEnd < block) { 558cb93a386Sopenharmony_ci continue; 559cb93a386Sopenharmony_ci } else if (textRange.fStart >= block) { 560cb93a386Sopenharmony_ci break; 561cb93a386Sopenharmony_ci } else if (limited.fStart == EMPTY_INDEX) { 562cb93a386Sopenharmony_ci limited.fStart = i; 563cb93a386Sopenharmony_ci } 564cb93a386Sopenharmony_ci limited.fEnd = i; 565cb93a386Sopenharmony_ci } 566cb93a386Sopenharmony_ci 567cb93a386Sopenharmony_ci return SkSpan<TextIndex>(&blocks[textRange.fStart], textRange.width()); 568cb93a386Sopenharmony_ci} 569cb93a386Sopenharmony_ci 570cb93a386Sopenharmony_civoid WrappedText::visit(UnicodeText* unicodeText, Visitor* visitor, PositionType positionType, SkSpan<size_t> chunks) const { 571cb93a386Sopenharmony_ci // Decor blocks have to be sorted by text cannot intersect but can skip some parts of the text 572cb93a386Sopenharmony_ci // (in which case we use default text style from paragraph style) 573cb93a386Sopenharmony_ci // The edges of the decor blocks don't have to match glyph, grapheme or even unicode code point edges 574cb93a386Sopenharmony_ci // It's out responsibility to adjust them to some reasonable values 575cb93a386Sopenharmony_ci // [a:b) -> [c:d) where 576cb93a386Sopenharmony_ci // c is closest GG cluster edge to a from the left and d is closest GG cluster edge to b from the left 577cb93a386Sopenharmony_ci auto textBlocks = WrappedText::chunksToBlocks(chunks); 578cb93a386Sopenharmony_ci SkScalar verticalOffset = 0.0f; 579cb93a386Sopenharmony_ci LineIndex lineIndex = 0ul; 580cb93a386Sopenharmony_ci size_t glyphCount = 0ul; 581cb93a386Sopenharmony_ci for (auto& line : fVisualLines) { 582cb93a386Sopenharmony_ci visitor->onBeginLine(lineIndex, line.text(), line.isHardBreak(), SkRect::MakeXYWH(0, verticalOffset, line.fActualWidth, line.fTextMetrics.height())); 583cb93a386Sopenharmony_ci RunIndex runIndex = 0ul; 584cb93a386Sopenharmony_ci auto lineBlocks = WrappedText::limitBlocks(line.fText, SkSpan<TextIndex>(textBlocks.data(), textBlocks.size())); 585cb93a386Sopenharmony_ci for (auto& run : fVisualRuns) { 586cb93a386Sopenharmony_ci run.forEachTextBlockInGlyphRange(lineBlocks, [&](DirTextRange dirTextRange) { 587cb93a386Sopenharmony_ci GlyphRange glyphRange = this->textToGlyphs(unicodeText, positionType, runIndex, dirTextRange); 588cb93a386Sopenharmony_ci auto diff = line.fTextMetrics.above() - run.fTextMetrics.above(); 589cb93a386Sopenharmony_ci SkRect boundingRect = 590cb93a386Sopenharmony_ci SkRect::MakeXYWH(line.fOffset.fX + run.fPositions[glyphRange.fStart].fX, 591cb93a386Sopenharmony_ci line.fOffset.fY + diff, 592cb93a386Sopenharmony_ci run.calculateWidth(glyphRange), 593cb93a386Sopenharmony_ci run.fTextMetrics.height()); 594cb93a386Sopenharmony_ci visitor->onGlyphRun(run.fFont, 595cb93a386Sopenharmony_ci dirTextRange, 596cb93a386Sopenharmony_ci boundingRect, 597cb93a386Sopenharmony_ci run.trailingSpacesStart(), 598cb93a386Sopenharmony_ci glyphRange.width(), 599cb93a386Sopenharmony_ci &run.fGlyphs[glyphRange.fStart], 600cb93a386Sopenharmony_ci &run.fPositions[glyphRange.fStart], 601cb93a386Sopenharmony_ci &run.fClusters[glyphRange.fStart]); 602cb93a386Sopenharmony_ci }); 603cb93a386Sopenharmony_ci ++runIndex; 604cb93a386Sopenharmony_ci glyphCount += run.size(); 605cb93a386Sopenharmony_ci } 606cb93a386Sopenharmony_ci visitor->onEndLine(lineIndex, line.text(), line.trailingSpaces(), glyphCount); 607cb93a386Sopenharmony_ci verticalOffset += line.fTextMetrics.height(); 608cb93a386Sopenharmony_ci ++lineIndex; 609cb93a386Sopenharmony_ci } 610cb93a386Sopenharmony_ci} 611cb93a386Sopenharmony_ci 612cb93a386Sopenharmony_ci// TODO: Implement more effective search 613cb93a386Sopenharmony_ciGlyphRange WrappedText::textToGlyphs(UnicodeText* unicodeText, PositionType positionType, RunIndex runIndex, DirTextRange dirTextRange) const { 614cb93a386Sopenharmony_ci SkASSERT(runIndex < fVisualRuns.size()); 615cb93a386Sopenharmony_ci auto& run = fVisualRuns[runIndex]; 616cb93a386Sopenharmony_ci SkASSERT(run.fDirTextRange.contains(dirTextRange)); 617cb93a386Sopenharmony_ci GlyphRange glyphRange(0, run.size()); 618cb93a386Sopenharmony_ci for (GlyphIndex glyph = 0; glyph < run.size(); ++glyph) { 619cb93a386Sopenharmony_ci auto textIndex = run.fClusters[glyph]; 620cb93a386Sopenharmony_ci if (positionType == PositionType::kGraphemeCluster && unicodeText->hasProperty(textIndex, CodeUnitFlags::kGraphemeStart)) { 621cb93a386Sopenharmony_ci if (dirTextRange.after(textIndex)) { 622cb93a386Sopenharmony_ci glyphRange.fStart = glyph; 623cb93a386Sopenharmony_ci } else if (dirTextRange.before(textIndex)) { 624cb93a386Sopenharmony_ci glyphRange.fEnd = glyph; 625cb93a386Sopenharmony_ci } else { 626cb93a386Sopenharmony_ci return glyphRange; 627cb93a386Sopenharmony_ci } 628cb93a386Sopenharmony_ci } 629cb93a386Sopenharmony_ci } 630cb93a386Sopenharmony_ci SkASSERT(false); 631cb93a386Sopenharmony_ci return glyphRange; 632cb93a386Sopenharmony_ci} 633cb93a386Sopenharmony_ci 634cb93a386Sopenharmony_cistd::unique_ptr<SelectableText> WrappedText::prepareToEdit(UnicodeText* unicodeText) const { 635cb93a386Sopenharmony_ci auto selectableText = std::make_unique<SelectableText>(); 636cb93a386Sopenharmony_ci this->visit(selectableText.get()); 637cb93a386Sopenharmony_ci selectableText->fGlyphUnitProperties.push_back_n(unicodeText->getText16().size() + 1, GlyphUnitFlags::kNoGlyphUnitFlag); 638cb93a386Sopenharmony_ci for (auto index = 0; index < unicodeText->getText16().size(); ++index) { 639cb93a386Sopenharmony_ci if (unicodeText->hasProperty(index, CodeUnitFlags::kHardLineBreakBefore)) { 640cb93a386Sopenharmony_ci selectableText->fGlyphUnitProperties[index] = GlyphUnitFlags::kGraphemeClusterStart; 641cb93a386Sopenharmony_ci } 642cb93a386Sopenharmony_ci } 643cb93a386Sopenharmony_ci for (const auto& run : fVisualRuns) { 644cb93a386Sopenharmony_ci for (auto& cluster : run.fClusters) { 645cb93a386Sopenharmony_ci if (unicodeText->hasProperty(cluster, CodeUnitFlags::kGraphemeStart)) { 646cb93a386Sopenharmony_ci selectableText->fGlyphUnitProperties[cluster] = GlyphUnitFlags::kGraphemeClusterStart; 647cb93a386Sopenharmony_ci } 648cb93a386Sopenharmony_ci } 649cb93a386Sopenharmony_ci } 650cb93a386Sopenharmony_ci return selectableText; 651cb93a386Sopenharmony_ci} 652cb93a386Sopenharmony_ci 653cb93a386Sopenharmony_civoid SelectableText::onBeginLine(size_t index, TextRange lineText, bool hardBreak, SkRect bounds) { 654cb93a386Sopenharmony_ci SkASSERT(fBoxLines.size() == index); 655cb93a386Sopenharmony_ci fBoxLines.emplace_back(index, lineText, hardBreak, bounds); 656cb93a386Sopenharmony_ci} 657cb93a386Sopenharmony_ci 658cb93a386Sopenharmony_civoid SelectableText::onEndLine(size_t index, TextRange lineText, GlyphRange trailingSpaces, size_t glyphCount) { 659cb93a386Sopenharmony_ci auto& line = fBoxLines.back(); 660cb93a386Sopenharmony_ci line.fTextEnd = trailingSpaces.fStart; 661cb93a386Sopenharmony_ci line.fTrailingSpacesEnd = trailingSpaces.fEnd; 662cb93a386Sopenharmony_ci SkASSERT(line.fTextByGlyph.size() == glyphCount); 663cb93a386Sopenharmony_ci line.fBoxGlyphs.emplace_back(SkRect::MakeXYWH(line.fBounds.fRight, line.fBounds.fTop, 0.0f, line.fBounds.height())); 664cb93a386Sopenharmony_ci if (line.fTextByGlyph.empty()) { 665cb93a386Sopenharmony_ci // Let's create an empty fake box to avoid all the checks 666cb93a386Sopenharmony_ci line.fTextByGlyph.emplace_back(lineText.fEnd); 667cb93a386Sopenharmony_ci } 668cb93a386Sopenharmony_ci line.fTextByGlyph.emplace_back(lineText.fEnd); 669cb93a386Sopenharmony_ci} 670cb93a386Sopenharmony_ci 671cb93a386Sopenharmony_civoid SelectableText::onGlyphRun(const SkFont& font, 672cb93a386Sopenharmony_ci DirTextRange dirTextRange, 673cb93a386Sopenharmony_ci SkRect bounds, 674cb93a386Sopenharmony_ci TextIndex trailingSpaces, 675cb93a386Sopenharmony_ci size_t glyphCount, 676cb93a386Sopenharmony_ci const uint16_t glyphs[], 677cb93a386Sopenharmony_ci const SkPoint positions[], 678cb93a386Sopenharmony_ci const TextIndex clusters[]) { 679cb93a386Sopenharmony_ci auto& line = fBoxLines.back(); 680cb93a386Sopenharmony_ci auto start = line.fTextByGlyph.size(); 681cb93a386Sopenharmony_ci line.fBoxGlyphs.push_back_n(glyphCount); 682cb93a386Sopenharmony_ci line.fTextByGlyph.push_back_n(glyphCount); 683cb93a386Sopenharmony_ci for (auto i = 0; i < glyphCount; ++i) { 684cb93a386Sopenharmony_ci auto pos = positions[i]; 685cb93a386Sopenharmony_ci auto pos1 = positions[i + 1]; 686cb93a386Sopenharmony_ci line.fBoxGlyphs[start + i] = SkRect::MakeXYWH(pos.fX, bounds.fTop, pos1.fX - pos.fX, bounds.height()); 687cb93a386Sopenharmony_ci line.fTextByGlyph[start + i] = clusters[i]; 688cb93a386Sopenharmony_ci } 689cb93a386Sopenharmony_ci} 690cb93a386Sopenharmony_ci 691cb93a386Sopenharmony_ci// TODO: Do something (logN) that is not a linear search 692cb93a386Sopenharmony_ciPosition SelectableText::findPosition(PositionType positionType, const BoxLine& line, SkScalar x) const { 693cb93a386Sopenharmony_ci Position position(positionType); 694cb93a386Sopenharmony_ci position.fGlyphRange = GlyphRange(0, line.fBoxGlyphs.size() - 1); 695cb93a386Sopenharmony_ci position.fTextRange = line.fTextRange; 696cb93a386Sopenharmony_ci position.fBoundaries.fTop = line.fBounds.fTop; 697cb93a386Sopenharmony_ci position.fBoundaries.fBottom = line.fBounds.fBottom; 698cb93a386Sopenharmony_ci // We look for the narrowest glyph range adjusted to positionType that contains the point. 699cb93a386Sopenharmony_ci // So far we made sure that one unit of any positionType does not cross the run edges 700cb93a386Sopenharmony_ci // Therefore it's going to be represented by a single text range only 701cb93a386Sopenharmony_ci for (; position.fGlyphRange.fStart < position.fGlyphRange.fEnd; ++position.fGlyphRange.fStart) { 702cb93a386Sopenharmony_ci auto glyphBox = line.fBoxGlyphs[position.fGlyphRange.fStart]; 703cb93a386Sopenharmony_ci if (glyphBox.fLeft > x) { 704cb93a386Sopenharmony_ci break; 705cb93a386Sopenharmony_ci } 706cb93a386Sopenharmony_ci if (position.fPositionType == PositionType::kGraphemeCluster) { 707cb93a386Sopenharmony_ci auto textIndex = line.fTextByGlyph[position.fGlyphRange.fStart]; 708cb93a386Sopenharmony_ci if (this->hasProperty(textIndex, GlyphUnitFlags::kGraphemeClusterStart)) { 709cb93a386Sopenharmony_ci position.fTextRange.fStart = textIndex; 710cb93a386Sopenharmony_ci } 711cb93a386Sopenharmony_ci } else { 712cb93a386Sopenharmony_ci // TODO: Implement 713cb93a386Sopenharmony_ci SkASSERT(false); 714cb93a386Sopenharmony_ci } 715cb93a386Sopenharmony_ci } 716cb93a386Sopenharmony_ci for (; position.fGlyphRange.fEnd > position.fGlyphRange.fStart ; --position.fGlyphRange.fEnd) { 717cb93a386Sopenharmony_ci auto glyphBox = line.fBoxGlyphs[position.fGlyphRange.fStart]; 718cb93a386Sopenharmony_ci if (glyphBox.fRight <= x) { 719cb93a386Sopenharmony_ci break; 720cb93a386Sopenharmony_ci } 721cb93a386Sopenharmony_ci if (position.fPositionType == PositionType::kGraphemeCluster) { 722cb93a386Sopenharmony_ci auto textIndex = line.fTextByGlyph[position.fGlyphRange.fEnd]; 723cb93a386Sopenharmony_ci if (this->hasProperty(textIndex, GlyphUnitFlags::kGraphemeClusterStart)) { 724cb93a386Sopenharmony_ci position.fTextRange.fEnd = textIndex; 725cb93a386Sopenharmony_ci break; 726cb93a386Sopenharmony_ci } 727cb93a386Sopenharmony_ci } else { 728cb93a386Sopenharmony_ci // TODO: Implement 729cb93a386Sopenharmony_ci SkASSERT(false); 730cb93a386Sopenharmony_ci } 731cb93a386Sopenharmony_ci } 732cb93a386Sopenharmony_ci position.fLineIndex = line.fIndex; 733cb93a386Sopenharmony_ci position.fBoundaries.fLeft = line.fBoxGlyphs[position.fGlyphRange.fStart].fLeft; 734cb93a386Sopenharmony_ci position.fBoundaries.fRight = line.fBoxGlyphs[position.fGlyphRange.fEnd].fRight; 735cb93a386Sopenharmony_ci return position; 736cb93a386Sopenharmony_ci} 737cb93a386Sopenharmony_ci 738cb93a386Sopenharmony_ciPosition SelectableText::adjustedPosition(PositionType positionType, SkPoint xy) const { 739cb93a386Sopenharmony_ci xy.fX = std::min(xy.fX, this->fActualSize.fWidth); 740cb93a386Sopenharmony_ci xy.fY = std::min(xy.fY, this->fActualSize.fHeight); 741cb93a386Sopenharmony_ci Position position(positionType); 742cb93a386Sopenharmony_ci for (auto& line : fBoxLines) { 743cb93a386Sopenharmony_ci if (line.fBounds.fTop > xy.fY) { 744cb93a386Sopenharmony_ci // We are past the point vertically 745cb93a386Sopenharmony_ci break; 746cb93a386Sopenharmony_ci } else if (line.fBounds.fBottom <= xy.fY) { 747cb93a386Sopenharmony_ci // We haven't reached the point vertically yet 748cb93a386Sopenharmony_ci continue; 749cb93a386Sopenharmony_ci } 750cb93a386Sopenharmony_ci return this->findPosition(positionType, line, xy.fX); 751cb93a386Sopenharmony_ci } 752cb93a386Sopenharmony_ci return this->lastPosition(positionType); 753cb93a386Sopenharmony_ci} 754cb93a386Sopenharmony_ci 755cb93a386Sopenharmony_ciPosition SelectableText::previousPosition(Position current) const { 756cb93a386Sopenharmony_ci const BoxLine* currentLine = &fBoxLines[current.fLineIndex]; 757cb93a386Sopenharmony_ci if (this->isFirstOnTheLine(current)) { 758cb93a386Sopenharmony_ci // Go to the previous line 759cb93a386Sopenharmony_ci if (current.fLineIndex == 0) { 760cb93a386Sopenharmony_ci // We reached the end; there is nowhere to move 761cb93a386Sopenharmony_ci current.fGlyphRange = GlyphRange(0, 0); 762cb93a386Sopenharmony_ci return current; 763cb93a386Sopenharmony_ci } else { 764cb93a386Sopenharmony_ci current.fLineIndex -= 1; 765cb93a386Sopenharmony_ci currentLine = &fBoxLines[current.fLineIndex]; 766cb93a386Sopenharmony_ci current.fGlyphRange.fStart = currentLine->fBoxGlyphs.size(); 767cb93a386Sopenharmony_ci } 768cb93a386Sopenharmony_ci } 769cb93a386Sopenharmony_ci auto position = this->findPosition(current.fPositionType, *currentLine, currentLine->fBoxGlyphs[current.fGlyphRange.fStart].centerX()); 770cb93a386Sopenharmony_ci if (current.fPositionType == PositionType::kGraphemeCluster) { 771cb93a386Sopenharmony_ci // Either way we found us a grapheme cluster (just make sure of it) 772cb93a386Sopenharmony_ci SkASSERT(this->hasProperty(current.fTextRange.fStart, GlyphUnitFlags::kGraphemeClusterStart)); 773cb93a386Sopenharmony_ci } 774cb93a386Sopenharmony_ci return position; 775cb93a386Sopenharmony_ci} 776cb93a386Sopenharmony_ci 777cb93a386Sopenharmony_ciPosition SelectableText::nextPosition(Position current) const { 778cb93a386Sopenharmony_ci const BoxLine* currentLine = &fBoxLines[current.fLineIndex]; 779cb93a386Sopenharmony_ci if (this->isLastOnTheLine(current)) { 780cb93a386Sopenharmony_ci // Go to the next line 781cb93a386Sopenharmony_ci if (current.fLineIndex == this->fBoxLines.size() - 1) { 782cb93a386Sopenharmony_ci // We reached the end; there is nowhere to move 783cb93a386Sopenharmony_ci current.fGlyphRange = GlyphRange(currentLine->fBoxGlyphs.size(), currentLine->fBoxGlyphs.size()); 784cb93a386Sopenharmony_ci return current; 785cb93a386Sopenharmony_ci } else { 786cb93a386Sopenharmony_ci current.fLineIndex += 1; 787cb93a386Sopenharmony_ci currentLine = &fBoxLines[current.fLineIndex]; 788cb93a386Sopenharmony_ci current.fGlyphRange.fEnd = 0; 789cb93a386Sopenharmony_ci } 790cb93a386Sopenharmony_ci } 791cb93a386Sopenharmony_ci auto position = this->findPosition(current.fPositionType, *currentLine, currentLine->fBoxGlyphs[current.fGlyphRange.fStart].centerX()); 792cb93a386Sopenharmony_ci if (current.fPositionType == PositionType::kGraphemeCluster) { 793cb93a386Sopenharmony_ci // Either way we found us a grapheme cluster (just make sure of it) 794cb93a386Sopenharmony_ci SkASSERT(this->hasProperty(current.fTextRange.fEnd, GlyphUnitFlags::kGraphemeClusterStart)); 795cb93a386Sopenharmony_ci } 796cb93a386Sopenharmony_ci return position; 797cb93a386Sopenharmony_ci} 798cb93a386Sopenharmony_ci 799cb93a386Sopenharmony_ciPosition SelectableText::upPosition(Position current) const { 800cb93a386Sopenharmony_ci 801cb93a386Sopenharmony_ci if (current.fLineIndex == 0) { 802cb93a386Sopenharmony_ci // We are on the first line; just move to the first position 803cb93a386Sopenharmony_ci return this->firstPosition(current.fPositionType); 804cb93a386Sopenharmony_ci } 805cb93a386Sopenharmony_ci 806cb93a386Sopenharmony_ci // Go to the previous line 807cb93a386Sopenharmony_ci const BoxLine* currentLine = &fBoxLines[current.fLineIndex]; 808cb93a386Sopenharmony_ci auto position = this->findPosition(current.fPositionType, fBoxLines[current.fLineIndex - 1], currentLine->fBoxGlyphs[current.fGlyphRange.fStart].centerX()); 809cb93a386Sopenharmony_ci if (current.fPositionType == PositionType::kGraphemeCluster) { 810cb93a386Sopenharmony_ci // Either way we found us a grapheme cluster (just make sure of it) 811cb93a386Sopenharmony_ci SkASSERT(this->hasProperty(current.fTextRange.fEnd, GlyphUnitFlags::kGraphemeClusterStart)); 812cb93a386Sopenharmony_ci } 813cb93a386Sopenharmony_ci return position; 814cb93a386Sopenharmony_ci} 815cb93a386Sopenharmony_ci 816cb93a386Sopenharmony_ciPosition SelectableText::downPosition(Position current) const { 817cb93a386Sopenharmony_ci 818cb93a386Sopenharmony_ci if (current.fLineIndex == this->countLines() - 1) { 819cb93a386Sopenharmony_ci // We are on the last line; just move to the last position 820cb93a386Sopenharmony_ci return this->lastPosition(current.fPositionType); 821cb93a386Sopenharmony_ci } 822cb93a386Sopenharmony_ci 823cb93a386Sopenharmony_ci // Go to the next line 824cb93a386Sopenharmony_ci const BoxLine* currentLine = &fBoxLines[current.fLineIndex]; 825cb93a386Sopenharmony_ci auto position = this->findPosition(current.fPositionType, fBoxLines[current.fLineIndex + 1], currentLine->fBoxGlyphs[current.fGlyphRange.fStart].centerX()); 826cb93a386Sopenharmony_ci if (current.fPositionType == PositionType::kGraphemeCluster) { 827cb93a386Sopenharmony_ci // Either way we found us a grapheme cluster (just make sure of it) 828cb93a386Sopenharmony_ci SkASSERT(this->hasProperty(current.fTextRange.fEnd, GlyphUnitFlags::kGraphemeClusterStart)); 829cb93a386Sopenharmony_ci } 830cb93a386Sopenharmony_ci return position; 831cb93a386Sopenharmony_ci} 832cb93a386Sopenharmony_ci 833cb93a386Sopenharmony_ciPosition SelectableText::firstPosition(PositionType positionType) const { 834cb93a386Sopenharmony_ci auto firstLine = fBoxLines.front(); 835cb93a386Sopenharmony_ci auto firstGlyph = firstLine.fBoxGlyphs.front(); 836cb93a386Sopenharmony_ci Position beginningOfText(positionType); 837cb93a386Sopenharmony_ci // Set the glyph range after the last glyph 838cb93a386Sopenharmony_ci beginningOfText.fGlyphRange = GlyphRange { 0, 0}; 839cb93a386Sopenharmony_ci beginningOfText.fLineIndex = 0; 840cb93a386Sopenharmony_ci beginningOfText.fBoundaries = SkRect::MakeXYWH(firstGlyph.fLeft, firstGlyph.fTop, 0, firstGlyph.height()); 841cb93a386Sopenharmony_ci beginningOfText.fTextRange = this->glyphsToText(beginningOfText); 842cb93a386Sopenharmony_ci beginningOfText.fLineIndex = 0; 843cb93a386Sopenharmony_ci return beginningOfText; 844cb93a386Sopenharmony_ci} 845cb93a386Sopenharmony_ci 846cb93a386Sopenharmony_ciPosition SelectableText::lastPosition(PositionType positionType) const { 847cb93a386Sopenharmony_ci auto lastLine = fBoxLines.back(); 848cb93a386Sopenharmony_ci auto lastGlyph = lastLine.fBoxGlyphs.back(); 849cb93a386Sopenharmony_ci Position endOfText(positionType); 850cb93a386Sopenharmony_ci endOfText.fLineIndex = lastLine.fIndex; 851cb93a386Sopenharmony_ci endOfText.fGlyphRange = GlyphRange(lastLine.fBoxGlyphs.size() - 1, lastLine.fBoxGlyphs.size() - 1); 852cb93a386Sopenharmony_ci endOfText.fBoundaries = SkRect::MakeXYWH(lastGlyph.fRight, lastGlyph.fTop, 0, lastGlyph.height()); 853cb93a386Sopenharmony_ci endOfText.fTextRange = this->glyphsToText(endOfText); 854cb93a386Sopenharmony_ci endOfText.fLineIndex = lastLine.fIndex; 855cb93a386Sopenharmony_ci return endOfText; 856cb93a386Sopenharmony_ci} 857cb93a386Sopenharmony_ci 858cb93a386Sopenharmony_ciPosition SelectableText::firstInLinePosition(PositionType positionType, LineIndex lineIndex) const { 859cb93a386Sopenharmony_ci SkASSERT(lineIndex >= 0 && lineIndex < fBoxLines.size()); 860cb93a386Sopenharmony_ci auto& line = fBoxLines[lineIndex]; 861cb93a386Sopenharmony_ci return this->findPosition(positionType, line, line.fBounds.left()); 862cb93a386Sopenharmony_ci} 863cb93a386Sopenharmony_ci 864cb93a386Sopenharmony_ciPosition SelectableText::lastInLinePosition(PositionType positionType, LineIndex lineIndex) const { 865cb93a386Sopenharmony_ci auto& line = fBoxLines[lineIndex]; 866cb93a386Sopenharmony_ci return this->findPosition(positionType, line, line.fBounds.right()); 867cb93a386Sopenharmony_ci} 868cb93a386Sopenharmony_ci 869cb93a386Sopenharmony_ci 870cb93a386Sopenharmony_ciTextRange SelectableText::glyphsToText(Position position) const { 871cb93a386Sopenharmony_ci SkASSERT(position.fPositionType != PositionType::kRandomText); 872cb93a386Sopenharmony_ci auto line = this->getLine(position.fLineIndex); 873cb93a386Sopenharmony_ci TextRange textRange = EMPTY_RANGE; 874cb93a386Sopenharmony_ci for (auto glyph = position.fGlyphRange.fStart; glyph <= position.fGlyphRange.fEnd; ++glyph) { 875cb93a386Sopenharmony_ci if (textRange.fStart == EMPTY_INDEX) { 876cb93a386Sopenharmony_ci textRange.fStart = line.fTextByGlyph[glyph]; 877cb93a386Sopenharmony_ci } 878cb93a386Sopenharmony_ci textRange.fEnd = line.fTextByGlyph[glyph]; 879cb93a386Sopenharmony_ci } 880cb93a386Sopenharmony_ci return textRange; 881cb93a386Sopenharmony_ci} 882cb93a386Sopenharmony_ci} // namespace text 883cb93a386Sopenharmony_ci} // namespace skia 884