blob: 84e8306143ab606b30e551683eb6fdddf7d8d495 [file] [log] [blame]
/*
* 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