// Copyright 2019 Google LLC. #include "include/core/SkTypes.h" #include "modules/skparagraph/include/FontCollection.h" #include "modules/skparagraph/include/Paragraph.h" #include "modules/skparagraph/include/ParagraphBuilder.h" #include "modules/skparagraph/include/ParagraphStyle.h" #include "modules/skparagraph/include/TextStyle.h" #include "modules/skparagraph/src/ParagraphBuilderImpl.h" #include "modules/skparagraph/src/ParagraphImpl.h" #include #include #include "src/core/SkStringUtils.h" namespace skia { namespace textlayout { std::unique_ptr ParagraphBuilder::make( const ParagraphStyle& style, sk_sp fontCollection) { return ParagraphBuilderImpl::make(style, fontCollection); } std::unique_ptr ParagraphBuilderImpl::make( const ParagraphStyle& style, sk_sp fontCollection) { return std::make_unique(style, fontCollection); } std::unique_ptr ParagraphBuilderImpl::make( const ParagraphStyle& style, sk_sp fontCollection, std::unique_ptr unicode) { if (nullptr == unicode) { return nullptr; } return std::make_unique(style, fontCollection, std::move(unicode)); } ParagraphBuilderImpl::ParagraphBuilderImpl( const ParagraphStyle& style, sk_sp fontCollection, std::unique_ptr unicode) : ParagraphBuilder(style, fontCollection) , fUtf8() , fFontCollection(std::move(fontCollection)) , fParagraphStyle(style) , fUnicode(std::move(unicode)) #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION) , fTextIsFinalized(false) , fUsingClientInfo(false) #endif { startStyledBlock(); } ParagraphBuilderImpl::ParagraphBuilderImpl( const ParagraphStyle& style, sk_sp fontCollection) : ParagraphBuilderImpl(style, fontCollection, SkUnicode::Make()) { } ParagraphBuilderImpl::~ParagraphBuilderImpl() = default; void ParagraphBuilderImpl::pushStyle(const TextStyle& style) { fTextStyles.push_back(style); if (!fStyledBlocks.empty() && fStyledBlocks.back().fRange.end == fUtf8.size() && fStyledBlocks.back().fStyle == style) { // Just continue with the same style } else { // Go with the new style startStyledBlock(); } } void ParagraphBuilderImpl::pop() { if (!fTextStyles.empty()) { fTextStyles.pop_back(); } else { // In this case we use paragraph style and skip Pop operation SkDEBUGF("SkParagraphBuilder.Pop() called too many times.\n"); } this->startStyledBlock(); } const TextStyle& ParagraphBuilderImpl::internalPeekStyle() { if (fTextStyles.empty()) { return fParagraphStyle.getTextStyle(); } else { return fTextStyles.back(); } } TextStyle ParagraphBuilderImpl::peekStyle() { return this->internalPeekStyle(); } void ParagraphBuilderImpl::addText(const std::u16string& text) { #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION) SkASSERT(!fTextIsFinalized); #endif auto utf8 = SkUnicode::convertUtf16ToUtf8(text); fUtf8.append(utf8); } void ParagraphBuilderImpl::addText(const char* text) { #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION) SkASSERT(!fTextIsFinalized); #endif fUtf8.append(text); } void ParagraphBuilderImpl::addText(const char* text, size_t len) { #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION) SkASSERT(!fTextIsFinalized); #endif fUtf8.append(text, len); } void ParagraphBuilderImpl::addPlaceholder(const PlaceholderStyle& placeholderStyle) { #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION) SkASSERT(!fTextIsFinalized); #endif addPlaceholder(placeholderStyle, false); } void ParagraphBuilderImpl::addPlaceholder(const PlaceholderStyle& placeholderStyle, bool lastOne) { #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION) // The very last placeholder is added automatically // and only AFTER finalize() is called SkASSERT(!fTextIsFinalized && !lastOne); #endif if (!fUtf8.isEmpty() && !lastOne) { // We keep the very last text style this->endRunIfNeeded(); } BlockRange stylesBefore(fPlaceholders.empty() ? 0 : fPlaceholders.back().fBlocksBefore.end + 1, fStyledBlocks.size()); TextRange textBefore(fPlaceholders.empty() ? 0 : fPlaceholders.back().fRange.end, fUtf8.size()); auto start = fUtf8.size(); auto topStyle = internalPeekStyle(); if (!lastOne) { pushStyle(topStyle.cloneForPlaceholder()); addText(std::u16string(1ull, 0xFFFC)); pop(); } auto end = fUtf8.size(); fPlaceholders.emplace_back(start, end, placeholderStyle, topStyle, stylesBefore, textBefore); } void ParagraphBuilderImpl::endRunIfNeeded() { if (fStyledBlocks.empty()) { return; } auto& last = fStyledBlocks.back(); if (last.fRange.start == fUtf8.size()) { fStyledBlocks.pop_back(); } else { last.fRange.end = fUtf8.size(); } } void ParagraphBuilderImpl::startStyledBlock() { endRunIfNeeded(); fStyledBlocks.emplace_back(fUtf8.size(), fUtf8.size(), internalPeekStyle()); } void ParagraphBuilderImpl::finalize() { #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION) if (fTextIsFinalized) { return; } #endif if (!fUtf8.isEmpty()) { this->endRunIfNeeded(); } #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION) fTextIsFinalized = true; #endif } std::unique_ptr ParagraphBuilderImpl::Build() { this->finalize(); // Add one fake placeholder with the rest of the text this->addPlaceholder(PlaceholderStyle(), true); #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION) SkASSERT(fUsingClientInfo); fUTF8IndexForUTF16Index.clear(); fUTF16IndexForUTF8Index.clear(); // This is the place where SkUnicode is paired with SkParagraph fUnicode = SkUnicode::MakeClientBasedUnicode(this->getText(), std::move(fWordsUtf16), std::move(fGraphemeBreaksUtf8), std::move(fLineBreaksUtf8)); #endif SkASSERT(fUnicode); return std::make_unique( fUtf8, fParagraphStyle, fStyledBlocks, fPlaceholders, fFontCollection, fUnicode); } SkSpan ParagraphBuilderImpl::getText() { this->finalize(); return SkSpan(fUtf8.isEmpty() ? nullptr : const_cast(fUtf8.c_str()), fUtf8.size()); } const ParagraphStyle& ParagraphBuilderImpl::getParagraphStyle() const { return fParagraphStyle; } void ParagraphBuilderImpl::ensureUTF16Mapping() { fillUTF16MappingOnce([&] { SkUnicode::extractUtfConversionMapping( this->getText(), [&](size_t index) { fUTF8IndexForUTF16Index.emplace_back(index); }, [&](size_t index) { fUTF16IndexForUTF8Index.emplace_back(index); }); }); } #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION) void ParagraphBuilderImpl::setWordsUtf8(std::vector wordsUtf8) { ensureUTF16Mapping(); std::vector wordsUtf16; for (SkUnicode::Position indexUtf8: wordsUtf8) { wordsUtf16.emplace_back(fUTF16IndexForUTF8Index[indexUtf8]); } setWordsUtf16(wordsUtf16); } void ParagraphBuilderImpl::setWordsUtf16(std::vector wordsUtf16) { fUsingClientInfo = true; fWordsUtf16 = std::move(wordsUtf16); } void ParagraphBuilderImpl::setGraphemeBreaksUtf8(std::vector graphemeBreaksUtf8) { fUsingClientInfo = true; fGraphemeBreaksUtf8 = std::move(graphemeBreaksUtf8); } void ParagraphBuilderImpl::setGraphemeBreaksUtf16(std::vector graphemeBreaksUtf16) { ensureUTF16Mapping(); std::vector graphemeBreaksUtf8; for (SkUnicode::Position indexUtf16: graphemeBreaksUtf16) { graphemeBreaksUtf8.emplace_back(fUTF8IndexForUTF16Index[indexUtf16]); } setGraphemeBreaksUtf8(graphemeBreaksUtf8); } void ParagraphBuilderImpl::setLineBreaksUtf8(std::vector lineBreaksUtf8) { fUsingClientInfo = true; fLineBreaksUtf8 = std::move(lineBreaksUtf8); } void ParagraphBuilderImpl::setLineBreaksUtf16(std::vector lineBreaksUtf16) { ensureUTF16Mapping(); std::vector lineBreaksUtf8; for (SkUnicode::LineBreakBefore lineBreakUtf16: lineBreaksUtf16) { lineBreaksUtf8.emplace_back(SkUnicode::LineBreakBefore( fUTF8IndexForUTF16Index[lineBreakUtf16.pos], lineBreakUtf16.breakType)); } setLineBreaksUtf8(lineBreaksUtf8); } #else void ParagraphBuilderImpl::setWordsUtf8(std::vector wordsUtf8) { SkASSERT(false); } void ParagraphBuilderImpl::setWordsUtf16(std::vector wordsUtf16) { SkASSERT(false); } void ParagraphBuilderImpl::setGraphemeBreaksUtf8(std::vector graphemesUtf8) { SkASSERT(false); } void ParagraphBuilderImpl::setGraphemeBreaksUtf16(std::vector graphemesUtf16) { SkASSERT(false); } void ParagraphBuilderImpl::setLineBreaksUtf8(std::vector lineBreaksUtf8) { SkASSERT(false); } void ParagraphBuilderImpl::setLineBreaksUtf16(std::vector lineBreaksUtf16) { SkASSERT(false); } #endif void ParagraphBuilderImpl::Reset() { fTextStyles.reset(); fUtf8.reset(); fStyledBlocks.reset(); fPlaceholders.reset(); #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION) fUTF8IndexForUTF16Index.clear(); fUTF16IndexForUTF8Index.clear(); fWordsUtf16.clear(); fGraphemeBreaksUtf8.clear(); fLineBreaksUtf8.clear(); fTextIsFinalized = false; #endif startStyledBlock(); } bool ParagraphBuilderImpl::RequiresClientICU() { #if defined(SK_UNICODE_CLIENT_IMPLEMENTATION) return true; #else return false; #endif } } // namespace textlayout } // namespace skia