| /* | |
| * Copyright 2023 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/private/base/SkAssert.h" | |
| #include "include/private/base/SkDebug.h" | |
| #include "include/private/base/SkTFitsIn.h" | |
| #include "modules/skshaper/include/SkShaper.h" | |
| #include "modules/skshaper/include/SkShaper_skunicode.h" | |
| #include "modules/skunicode/include/SkUnicode.h" | |
| #include "src/base/SkUTF.h" | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <utility> | |
| using SkUnicodeBidi = std::unique_ptr<SkBidiIterator>; | |
| /** Replaces invalid utf-8 sequences with REPLACEMENT CHARACTER U+FFFD. */ | |
| static inline SkUnichar utf8_next(const char** ptr, const char* end) { | |
| SkUnichar val = SkUTF::NextUTF8(ptr, end); | |
| return val < 0 ? 0xFFFD : val; | |
| } | |
| class SkUnicodeBidiRunIterator final : public SkShaper::BiDiRunIterator { | |
| public: | |
| SkUnicodeBidiRunIterator(const char* utf8, const char* end, SkUnicodeBidi bidi) | |
| : fBidi(std::move(bidi)) | |
| , fEndOfCurrentRun(utf8) | |
| , fBegin(utf8) | |
| , fEnd(end) | |
| , fUTF16LogicalPosition(0) | |
| , fLevel(SkBidiIterator::kLTR) | |
| {} | |
| void consume() override { | |
| SkASSERT(fUTF16LogicalPosition < fBidi->getLength()); | |
| int32_t endPosition = fBidi->getLength(); | |
| fLevel = fBidi->getLevelAt(fUTF16LogicalPosition); | |
| SkUnichar u = utf8_next(&fEndOfCurrentRun, fEnd); | |
| fUTF16LogicalPosition += SkUTF::ToUTF16(u); | |
| SkBidiIterator::Level level; | |
| while (fUTF16LogicalPosition < endPosition) { | |
| level = fBidi->getLevelAt(fUTF16LogicalPosition); | |
| if (level != fLevel) { | |
| break; | |
| } | |
| u = utf8_next(&fEndOfCurrentRun, fEnd); | |
| fUTF16LogicalPosition += SkUTF::ToUTF16(u); | |
| } | |
| } | |
| size_t endOfCurrentRun() const override { | |
| return fEndOfCurrentRun - fBegin; | |
| } | |
| bool atEnd() const override { | |
| return fUTF16LogicalPosition == fBidi->getLength(); | |
| } | |
| SkBidiIterator::Level currentLevel() const override { | |
| return fLevel; | |
| } | |
| private: | |
| SkUnicodeBidi fBidi; | |
| char const * fEndOfCurrentRun; | |
| char const * const fBegin; | |
| char const * const fEnd; | |
| int32_t fUTF16LogicalPosition; | |
| SkBidiIterator::Level fLevel; | |
| }; | |
| #if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS) | |
| #if defined(SK_UNICODE_ICU_IMPLEMENTATION) | |
| #include "modules/skunicode/include/SkUnicode_icu.h" | |
| #endif | |
| #if defined(SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION) | |
| #include "modules/skunicode/include/SkUnicode_libgrapheme.h" | |
| #endif | |
| #if defined(SK_UNICODE_ICU4X_IMPLEMENTATION) | |
| #include "modules/skunicode/include/SkUnicode_icu4x.h" | |
| #endif | |
| sk_sp<SkUnicode> get_unicode() { | |
| #if defined(SK_UNICODE_ICU_IMPLEMENTATION) | |
| if (auto unicode = SkUnicodes::ICU::Make()) { | |
| return unicode; | |
| } | |
| #endif // defined(SK_UNICODE_ICU_IMPLEMENTATION) | |
| #if defined(SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION) | |
| if (auto unicode = SkUnicodes::Libgrapheme::Make()) { | |
| return unicode; | |
| } | |
| #endif | |
| #if defined(SK_UNICODE_ICU4X_IMPLEMENTATION) | |
| if (auto unicode = SkUnicodes::ICU4X::Make()) { | |
| return unicode; | |
| } | |
| #endif | |
| return nullptr; | |
| } | |
| std::unique_ptr<SkShaper::BiDiRunIterator> SkShaper::MakeIcuBiDiRunIterator(const char* utf8, | |
| size_t utf8Bytes, | |
| uint8_t bidiLevel) { | |
| static auto unicode = get_unicode(); | |
| if (!unicode) { | |
| return nullptr; | |
| } | |
| return SkShapers::unicode::BidiRunIterator(unicode, utf8, utf8Bytes, bidiLevel); | |
| } | |
| #endif // !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS) | |
| namespace SkShapers::unicode { | |
| std::unique_ptr<SkShaper::BiDiRunIterator> BidiRunIterator(sk_sp<SkUnicode> unicode, | |
| const char* utf8, | |
| size_t utf8Bytes, | |
| uint8_t bidiLevel) { | |
| if (!unicode) { | |
| return nullptr; | |
| } | |
| // ubidi only accepts utf16 (though internally it basically works on utf32 chars). | |
| // We want an ubidi_setPara(UBiDi*, UText*, UBiDiLevel, UBiDiLevel*, UErrorCode*); | |
| if (!SkTFitsIn<int32_t>(utf8Bytes)) { | |
| SkDEBUGF("Bidi error: text too long"); | |
| return nullptr; | |
| } | |
| int32_t utf16Units = SkUTF::UTF8ToUTF16(nullptr, 0, utf8, utf8Bytes); | |
| if (utf16Units < 0) { | |
| SkDEBUGF("Invalid utf8 input\n"); | |
| return nullptr; | |
| } | |
| std::unique_ptr<uint16_t[]> utf16(new uint16_t[utf16Units]); | |
| (void)SkUTF::UTF8ToUTF16(utf16.get(), utf16Units, utf8, utf8Bytes); | |
| auto bidiDir = (bidiLevel % 2 == 0) ? SkBidiIterator::kLTR : SkBidiIterator::kRTL; | |
| SkUnicodeBidi bidi = unicode->makeBidiIterator(utf16.get(), utf16Units, bidiDir); | |
| if (!bidi) { | |
| SkDEBUGF("Bidi error\n"); | |
| return nullptr; | |
| } | |
| return std::make_unique<SkUnicodeBidiRunIterator>(utf8, utf8 + utf8Bytes, std::move(bidi)); | |
| } | |
| } // namespace SkShapers::unicode |