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 #include "BidiUtils.h"
18
19 #include <algorithm>
20
21 #include <unicode/ubidi.h>
22 #include <unicode/utf16.h>
23
24 #include "minikin/Emoji.h"
25
26 #include "MinikinInternal.h"
27
28 namespace minikin {
29
bidiToUBidiLevel(Bidi bidi)30 static inline UBiDiLevel bidiToUBidiLevel(Bidi bidi) {
31 switch (bidi) {
32 case Bidi::LTR:
33 return 0x00;
34 case Bidi::RTL:
35 return 0x01;
36 case Bidi::DEFAULT_LTR:
37 return UBIDI_DEFAULT_LTR;
38 case Bidi::DEFAULT_RTL:
39 return UBIDI_DEFAULT_RTL;
40 case Bidi::FORCE_LTR:
41 case Bidi::FORCE_RTL:
42 MINIKIN_NOT_REACHED("FORCE_LTR/FORCE_RTL can not be converted to UBiDiLevel.");
43 return 0x00;
44 default:
45 MINIKIN_NOT_REACHED("Unknown Bidi value.");
46 return 0x00;
47 }
48 }
49
getRunInfoAt(uint32_t runOffset) const50 BidiText::RunInfo BidiText::getRunInfoAt(uint32_t runOffset) const {
51 MINIKIN_ASSERT(runOffset < mRunCount, "Out of range access. %d/%d", runOffset, mRunCount);
52 if (mRunCount == 1) {
53 // Single run. No need to iteract with UBiDi.
54 return {0, 1, mRange, mIsRtl};
55 }
56
57 int32_t startRun = -1;
58 int32_t lengthRun = -1;
59 const UBiDiDirection runDir = ubidi_getVisualRun(mBidi.get(), runOffset, &startRun, &lengthRun);
60 if (startRun == -1 || lengthRun == -1) {
61 ALOGE("invalid visual run");
62 return {0, 1, Range::invalidRange(), false};
63 }
64 const uint32_t runStart = std::max(static_cast<uint32_t>(startRun), mRange.getStart());
65 const uint32_t runEnd = std::min(static_cast<uint32_t>(startRun + lengthRun), mRange.getEnd());
66 if (runEnd <= runStart) {
67 // skip the empty run.
68 return {0, 1, Range::invalidRange(), false};
69 }
70 return {runOffset, mRunCount, Range(runStart, runEnd), (runDir == UBIDI_RTL)};
71 }
72
BidiText(const U16StringPiece & textBuf,const Range & range,Bidi bidiFlags)73 BidiText::BidiText(const U16StringPiece& textBuf, const Range& range, Bidi bidiFlags)
74 : mRange(range), mIsRtl(isRtl(bidiFlags)), mRunCount(1 /* by default, single run */) {
75 if (isOverride(bidiFlags)) {
76 // force single run.
77 return;
78 }
79
80 mBidi.reset(ubidi_open());
81 if (!mBidi) {
82 ALOGE("error creating bidi object");
83 return;
84 }
85 UErrorCode status = U_ZERO_ERROR;
86 // Set callbacks to override bidi classes of new emoji
87 ubidi_setClassCallback(mBidi.get(), emojiBidiOverride, nullptr, nullptr, nullptr, &status);
88 if (!U_SUCCESS(status)) {
89 ALOGE("error setting bidi callback function, status = %d", status);
90 return;
91 }
92
93 const UBiDiLevel bidiReq = bidiToUBidiLevel(bidiFlags);
94 ubidi_setPara(mBidi.get(), reinterpret_cast<const UChar*>(textBuf.data()), textBuf.size(),
95 bidiReq, nullptr, &status);
96 if (!U_SUCCESS(status)) {
97 ALOGE("error calling ubidi_setPara, status = %d", status);
98 return;
99 }
100 // RTL paragraphs get an odd level, while LTR paragraphs get an even level,
101 const bool paraIsRTL = ubidi_getParaLevel(mBidi.get()) & 0x01;
102 const ssize_t rc = ubidi_countRuns(mBidi.get(), &status);
103 if (!U_SUCCESS(status) || rc < 0) {
104 ALOGW("error counting bidi runs, status = %d", status);
105 return;
106 }
107 if (rc == 0) {
108 mIsRtl = paraIsRTL;
109 return;
110 }
111 if (rc == 1) {
112 // If the paragraph is a single run, override the paragraph dirction with the run
113 // (actually the whole text) direction.
114 const UBiDiDirection runDir = ubidi_getVisualRun(mBidi.get(), 0, nullptr, nullptr);
115 mIsRtl = (runDir == UBIDI_RTL);
116 return;
117 }
118 mRunCount = rc;
119 }
120
121 } // namespace minikin
122