Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkUnicode_icu_bidi.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2022 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
8
12#include "src/base/SkUTF.h"
13
14#include <cstdint>
15#include <memory>
16#include <string>
17#include <utility>
18#include <vector>
19
20namespace {
21using SkUnicodeBidi = std::unique_ptr<UBiDi, SkBidiFactory::BidiCloseCallback>;
22
23class SkBidiIterator_icu : public SkBidiIterator {
24public:
25 SkBidiIterator_icu(SkUnicodeBidi bidi, sk_sp<SkBidiFactory> fact)
26 : fBidi(std::move(bidi)), fBidiFact(std::move(fact)) {}
27
28 Position getLength() override { return fBidiFact->bidi_getLength(fBidi.get()); }
29
30 Level getLevelAt(Position pos) override { return fBidiFact->bidi_getLevelAt(fBidi.get(), pos); }
31
32private:
33 SkUnicodeBidi fBidi;
34 sk_sp<SkBidiFactory> fBidiFact;
35};
36} // namespace
37
38std::unique_ptr<SkBidiIterator> SkBidiFactory::MakeIterator(const uint16_t utf16[],
39 int utf16Units,
40 SkBidiIterator::Direction dir) const {
41 UErrorCode status = U_ZERO_ERROR;
42 SkUnicodeBidi bidi(this->bidi_openSized(utf16Units, 0, &status), this->bidi_close_callback());
43 if (U_FAILURE(status)) {
44 SkDEBUGF("Bidi error: %s", this->errorName(status));
45 return nullptr;
46 }
47 SkASSERT(bidi);
48 uint8_t bidiLevel = (dir == SkBidiIterator::kLTR) ? UBIDI_LTR : UBIDI_RTL;
49 // The required lifetime of utf16 isn't well documented.
50 // It appears it isn't used after ubidi_setPara except through ubidi_getText.
51 this->bidi_setPara(bidi.get(), (const UChar*)utf16, utf16Units, bidiLevel, nullptr, &status);
52 if (U_FAILURE(status)) {
53 SkDEBUGF("Bidi error: %s", this->errorName(status));
54 return nullptr;
55 }
56 return std::unique_ptr<SkBidiIterator>(
57 new SkBidiIterator_icu(std::move(bidi), sk_ref_sp(this)));
58}
59
60std::unique_ptr<SkBidiIterator> SkBidiFactory::MakeIterator(const char utf8[],
61 int utf8Units,
62 SkBidiIterator::Direction dir) const {
63 // Convert utf8 into utf16 since ubidi only accepts utf16
64 if (!SkTFitsIn<int32_t>(utf8Units)) {
65 SkDEBUGF("Bidi error: text too long");
66 return nullptr;
67 }
68
69 // Getting the length like this seems to always set U_BUFFER_OVERFLOW_ERROR
70 int utf16Units = SkUTF::UTF8ToUTF16(nullptr, 0, utf8, utf8Units);
71 if (utf16Units < 0) {
72 SkDEBUGF("Bidi error: Invalid utf8 input");
73 return nullptr;
74 }
75 std::unique_ptr<uint16_t[]> utf16(new uint16_t[utf16Units]);
76 SkDEBUGCODE(int dstLen =) SkUTF::UTF8ToUTF16(utf16.get(), utf16Units, utf8, utf8Units);
77 SkASSERT(dstLen == utf16Units);
78
79 return MakeIterator(utf16.get(), utf16Units, dir);
80}
81
82/** Replaces invalid utf-8 sequences with REPLACEMENT CHARACTER U+FFFD. */
83static inline SkUnichar utf8_next(const char** ptr, const char* end) {
84 SkUnichar val = SkUTF::NextUTF8(ptr, end);
85 return val < 0 ? 0xFFFD : val;
86}
87
88bool SkBidiFactory::ExtractBidi(const char utf8[],
89 int utf8Units,
91 std::vector<SkUnicode::BidiRegion>* bidiRegions) const {
92 // Convert to UTF16 since for now bidi iterator only operates on utf16
93 auto utf16 = SkUnicode::convertUtf8ToUtf16(utf8, utf8Units);
94
95 // Create bidi iterator
96 UErrorCode status = U_ZERO_ERROR;
97 SkUnicodeBidi bidi(this->bidi_openSized(utf16.size(), 0, &status), this->bidi_close_callback());
98 if (U_FAILURE(status)) {
99 SkDEBUGF("Bidi error: %s", this->errorName(status));
100 return false;
101 }
102 SkASSERT(bidi);
103 uint8_t bidiLevel = (dir == SkUnicode::TextDirection::kLTR) ? UBIDI_LTR : UBIDI_RTL;
104 // The required lifetime of utf16 isn't well documented.
105 // It appears it isn't used after ubidi_setPara except through ubidi_getText.
106 this->bidi_setPara(
107 bidi.get(), (const UChar*)utf16.c_str(), utf16.size(), bidiLevel, nullptr, &status);
108 if (U_FAILURE(status)) {
109 SkDEBUGF("Bidi error: %s", this->errorName(status));
110 return false;
111 }
112
113 // Iterate through bidi regions and the result positions into utf8
114 const char* start8 = utf8;
115 const char* end8 = utf8 + utf8Units;
116 SkUnicode::BidiLevel currentLevel = 0;
117
118 SkUnicode::Position pos8 = 0;
119 SkUnicode::Position pos16 = 0;
120 SkUnicode::Position end16 = this->bidi_getLength(bidi.get());
121
122 if (end16 == 0) {
123 return true;
124 }
125 if (this->bidi_getDirection(bidi.get()) != UBIDI_MIXED) {
126 // The entire paragraph is unidirectional.
127 bidiRegions->emplace_back(0, utf8Units, this->bidi_getLevelAt(bidi.get(), 0));
128 return true;
129 }
130
131 while (pos16 < end16) {
132 auto level = this->bidi_getLevelAt(bidi.get(), pos16);
133 if (pos16 == 0) {
134 currentLevel = level;
135 } else if (level != currentLevel) {
136 SkUnicode::Position end = start8 - utf8;
137 bidiRegions->emplace_back(pos8, end, currentLevel);
138 currentLevel = level;
139 pos8 = end;
140 }
141 SkUnichar u = utf8_next(&start8, end8);
142 pos16 += SkUTF::ToUTF16(u);
143 }
144 SkUnicode::Position end = start8 - utf8;
145 if (end != pos8) {
146 bidiRegions->emplace_back(pos8, end, currentLevel);
147 }
148 return true;
149}
SkPoint pos
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkDEBUGF(...)
Definition SkDebug.h:24
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
sk_sp< T > sk_ref_sp(T *obj)
Definition SkRefCnt.h:381
static SkUnichar utf8_next(const char **ptr, const char *end)
Definition SkShaper.cpp:84
std::unique_ptr< SkBidiIterator > SkUnicodeBidi
int32_t SkUnichar
Definition SkTypes.h:175
static SkUnichar utf8_next(const char **ptr, const char *end)
bool ExtractBidi(const char utf8[], int utf8Units, SkUnicode::TextDirection dir, std::vector< SkUnicode::BidiRegion > *bidiRegions) const
virtual void bidi_setPara(UBiDi *bidi, const UChar *text, int32_t length, UBiDiLevel paraLevel, UBiDiLevel *embeddingLevels, UErrorCode *status) const =0
virtual SkBidiIterator::Level bidi_getLevelAt(const UBiDi *bidi, int pos) const =0
virtual SkBidiIterator::Position bidi_getLength(const UBiDi *bidi) const =0
std::unique_ptr< SkBidiIterator > MakeIterator(const uint16_t utf16[], int utf16Units, SkBidiIterator::Direction dir) const
virtual UBiDi * bidi_openSized(int32_t maxLength, int32_t maxRunCount, UErrorCode *pErrorCode) const =0
virtual BidiCloseCallback bidi_close_callback() const =0
virtual UBiDiDirection bidi_getDirection(const UBiDi *bidi) const =0
virtual const char * errorName(UErrorCode status) const =0
virtual Level getLevelAt(Position)=0
virtual Position getLength()=0
static std::u16string convertUtf8ToUtf16(const char *utf8, int utf8Units)
Definition SkUnicode.cpp:32
size_t Position
Definition SkUnicode.h:98
uint8_t BidiLevel
Definition SkUnicode.h:99
glong glong end
SK_SPI int UTF8ToUTF16(uint16_t dst[], int dstCapacity, const char src[], size_t srcByteLength)
Definition SkUTF.cpp:259
SK_SPI SkUnichar NextUTF8(const char **ptr, const char *end)
Definition SkUTF.cpp:118
SK_SPI size_t ToUTF16(SkUnichar uni, uint16_t utf16[2]=nullptr)
Definition SkUTF.cpp:243
Definition ref_ptr.h:256