1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "mozilla/intl/Bidi.h"
6 #include "mozilla/Casting.h"
7 #include "mozilla/intl/ICU4CGlue.h"
9 #include "unicode/ubidi.h"
11 namespace mozilla::intl
{
13 Bidi::Bidi() { mBidi
= ubidi_open(); }
14 Bidi::~Bidi() { ubidi_close(mBidi
.GetMut()); }
16 ICUResult
Bidi::SetParagraph(Span
<const char16_t
> aParagraph
,
17 BidiEmbeddingLevel aLevel
) {
18 // Do not allow any reordering of the runs, as this can change the
19 // performance characteristics of working with runs. In the default mode,
20 // the levels can be iterated over directly, rather than relying on computing
21 // logical runs on the fly. This can have negative performance characteristics
22 // compared to iterating over the levels.
24 // In the UBIDI_REORDER_RUNS_ONLY the levels are encoded with additional
25 // information which can be safely ignored in this Bidi implementation.
26 // Note that this check is here since setting the mode must be done before
27 // calls to setting the paragraph.
28 MOZ_ASSERT(ubidi_getReorderingMode(mBidi
.GetMut()) == UBIDI_REORDER_DEFAULT
);
30 UErrorCode status
= U_ZERO_ERROR
;
31 ubidi_setPara(mBidi
.GetMut(), aParagraph
.Elements(),
32 AssertedCast
<int32_t>(aParagraph
.Length()), aLevel
, nullptr,
37 return ToICUResult(status
);
40 Bidi::ParagraphDirection
Bidi::GetParagraphDirection() const {
41 switch (ubidi_getDirection(mBidi
.GetConst())) {
43 return Bidi::ParagraphDirection::LTR
;
45 return Bidi::ParagraphDirection::RTL
;
47 return Bidi::ParagraphDirection::Mixed
;
49 // This is only used in `ubidi_getBaseDirection` which is unused in this
51 MOZ_ASSERT_UNREACHABLE("Unexpected UBiDiDirection value.");
53 return Bidi::ParagraphDirection::Mixed
;
57 void Bidi::ReorderVisual(const BidiEmbeddingLevel
* aLevels
, int32_t aLength
,
59 ubidi_reorderVisual(reinterpret_cast<const uint8_t*>(aLevels
), aLength
,
64 Bidi::BaseDirection
Bidi::GetBaseDirection(Span
<const char16_t
> aParagraph
) {
65 UBiDiDirection direction
= ubidi_getBaseDirection(
66 aParagraph
.Elements(), AssertedCast
<int32_t>(aParagraph
.Length()));
70 return Bidi::BaseDirection::LTR
;
72 return Bidi::BaseDirection::RTL
;
74 return Bidi::BaseDirection::Neutral
;
76 MOZ_ASSERT_UNREACHABLE("Unexpected UBiDiDirection value.");
79 return Bidi::BaseDirection::Neutral
;
82 static BidiDirection
ToBidiDirection(UBiDiDirection aDirection
) {
85 return BidiDirection::LTR
;
87 return BidiDirection::RTL
;
90 MOZ_ASSERT_UNREACHABLE("Unexpected UBiDiDirection value.");
92 return BidiDirection::LTR
;
95 Result
<int32_t, ICUError
> Bidi::CountRuns() {
96 UErrorCode status
= U_ZERO_ERROR
;
97 int32_t runCount
= ubidi_countRuns(mBidi
.GetMut(), &status
);
98 if (U_FAILURE(status
)) {
99 return Err(ToICUError(status
));
102 mLength
= ubidi_getProcessedLength(mBidi
.GetConst());
103 mLevels
= mLength
> 0 ? reinterpret_cast<const BidiEmbeddingLevel
*>(
104 ubidi_getLevels(mBidi
.GetMut(), &status
))
106 if (U_FAILURE(status
)) {
107 return Err(ToICUError(status
));
113 void Bidi::GetLogicalRun(int32_t aLogicalStart
, int32_t* aLogicalLimitOut
,
114 BidiEmbeddingLevel
* aLevelOut
) {
115 MOZ_ASSERT(mLevels
, "CountRuns hasn't been run?");
116 MOZ_RELEASE_ASSERT(aLogicalStart
< mLength
, "Out of bound");
117 BidiEmbeddingLevel level
= mLevels
[aLogicalStart
];
119 for (limit
= aLogicalStart
+ 1; limit
< mLength
; limit
++) {
120 if (mLevels
[limit
] != level
) {
124 *aLogicalLimitOut
= limit
;
128 BidiEmbeddingLevel
Bidi::GetParagraphEmbeddingLevel() const {
129 return BidiEmbeddingLevel(ubidi_getParaLevel(mBidi
.GetConst()));
132 BidiDirection
Bidi::GetVisualRun(int32_t aRunIndex
, int32_t* aLogicalStart
,
134 return ToBidiDirection(
135 ubidi_getVisualRun(mBidi
.GetMut(), aRunIndex
, aLogicalStart
, aLength
));
138 } // namespace mozilla::intl