1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "Minikin"
18
19 #include "BidiUtils.h"
20
21 #include <algorithm>
22
23 #include <unicode/ubidi.h>
24 #include <unicode/utf16.h>
25
26 #include "minikin/Emoji.h"
27
28 #include "MinikinInternal.h"
29
30 namespace minikin {
31
bidiToUBidiLevel(Bidi bidi)32 static inline UBiDiLevel bidiToUBidiLevel(Bidi bidi) {
33 switch (bidi) {
34 case Bidi::LTR:
35 return 0x00;
36 case Bidi::RTL:
37 return 0x01;
38 case Bidi::DEFAULT_LTR:
39 return UBIDI_DEFAULT_LTR;
40 case Bidi::DEFAULT_RTL:
41 return UBIDI_DEFAULT_RTL;
42 case Bidi::FORCE_LTR:
43 case Bidi::FORCE_RTL:
44 MINIKIN_NOT_REACHED("FORCE_LTR/FORCE_RTL can not be converted to UBiDiLevel.");
45 return 0x00;
46 default:
47 MINIKIN_NOT_REACHED("Unknown Bidi value.");
48 return 0x00;
49 }
50 }
51
getRunInfoAt(uint32_t runOffset) const52 BidiText::RunInfo BidiText::getRunInfoAt(uint32_t runOffset) const {
53 MINIKIN_ASSERT(runOffset < mRunCount, "Out of range access. %d/%d", runOffset, mRunCount);
54 if (mRunCount == 1) {
55 // Single run. No need to iteract with UBiDi.
56 return {mRange, mIsRtl};
57 }
58
59 int32_t startRun = -1;
60 int32_t lengthRun = -1;
61 const UBiDiDirection runDir = ubidi_getVisualRun(mBidi.get(), runOffset, &startRun, &lengthRun);
62 if (startRun == -1 || lengthRun == -1) {
63 ALOGE("invalid visual run");
64 return {Range::invalidRange(), false};
65 }
66 const uint32_t runStart = std::max(static_cast<uint32_t>(startRun), mRange.getStart());
67 const uint32_t runEnd = std::min(static_cast<uint32_t>(startRun + lengthRun), mRange.getEnd());
68 if (runEnd <= runStart) {
69 // skip the empty run.
70 return {Range::invalidRange(), false};
71 }
72 return {Range(runStart, runEnd), (runDir == UBIDI_RTL)};
73 }
74
BidiText(const U16StringPiece & textBuf,const Range & range,Bidi bidiFlags)75 BidiText::BidiText(const U16StringPiece& textBuf, const Range& range, Bidi bidiFlags)
76 : mRange(range), mIsRtl(isRtl(bidiFlags)), mRunCount(1 /* by default, single run */) {
77 if (isOverride(bidiFlags)) {
78 // force single run.
79 return;
80 }
81
82 mBidi.reset(ubidi_open());
83 if (!mBidi) {
84 ALOGE("error creating bidi object");
85 return;
86 }
87 UErrorCode status = U_ZERO_ERROR;
88 // Set callbacks to override bidi classes of new emoji
89 ubidi_setClassCallback(mBidi.get(), emojiBidiOverride, nullptr, nullptr, nullptr, &status);
90 if (!U_SUCCESS(status)) {
91 ALOGE("error setting bidi callback function, status = %d", status);
92 return;
93 }
94
95 const UBiDiLevel bidiReq = bidiToUBidiLevel(bidiFlags);
96 ubidi_setPara(mBidi.get(), reinterpret_cast<const UChar*>(textBuf.data()), textBuf.size(),
97 bidiReq, nullptr, &status);
98 if (!U_SUCCESS(status)) {
99 ALOGE("error calling ubidi_setPara, status = %d", status);
100 return;
101 }
102 // RTL paragraphs get an odd level, while LTR paragraphs get an even level,
103 const bool paraIsRTL = ubidi_getParaLevel(mBidi.get()) & 0x01;
104 const ssize_t rc = ubidi_countRuns(mBidi.get(), &status);
105 if (!U_SUCCESS(status) || rc < 0) {
106 ALOGW("error counting bidi runs, status = %d", status);
107 return;
108 }
109 if (rc == 0) {
110 mIsRtl = paraIsRTL;
111 return;
112 }
113 if (rc == 1) {
114 // If the paragraph is a single run, override the paragraph dirction with the run
115 // (actually the whole text) direction.
116 const UBiDiDirection runDir = ubidi_getVisualRun(mBidi.get(), 0, nullptr, nullptr);
117 mIsRtl = (runDir == UBIDI_RTL);
118 return;
119 }
120 mRunCount = rc;
121 }
122
123 } // namespace minikin
124