1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/i18n/bidi_line_iterator.h"
6
7 #include "base/logging.h"
8
9 namespace base {
10 namespace i18n {
11
12 namespace {
13
GetParagraphLevelForDirection(TextDirection direction)14 UBiDiLevel GetParagraphLevelForDirection(TextDirection direction) {
15 switch (direction) {
16 case UNKNOWN_DIRECTION:
17 return UBIDI_DEFAULT_LTR;
18 break;
19 case RIGHT_TO_LEFT:
20 return 1; // Highest RTL level.
21 break;
22 case LEFT_TO_RIGHT:
23 return 0; // Highest LTR level.
24 break;
25 default:
26 NOTREACHED();
27 return 0;
28 }
29 }
30
31 // Overrides the default bidi class for a given character, for the custom
32 // "AS_URL" behavior. Returns U_BIDI_CLASS_DEFAULT to defer to the default ICU
33 // behavior.
34 //
35 // Matches the C callback interface of ICU's UBiDiClassCallback type (which is
36 // why there is an unused argument).
GetURLBiDiClassCallback(const void *,UChar32 c)37 UCharDirection GetURLBiDiClassCallback(const void* /*unused*/, UChar32 c) {
38 // Note: Use a switch statement instead of strchr() to avoid iterating over a
39 // string for each character (the switch allows for much better compiler
40 // optimization).
41 switch (c) {
42 // The set of characters that delimit URL components (separating the scheme,
43 // username, password, domain labels, host, path segments, query
44 // names/values and fragment).
45 case '#':
46 case '&':
47 case '.':
48 case '/':
49 case ':':
50 case '=':
51 case '?':
52 case '@':
53 // Treat all of these characters as strong LTR, which effectively
54 // surrounds all of the text components of a URL (e.g., the domain labels
55 // and path segments) in a left-to-right embedding. This ensures that the
56 // URL components read from left to right, regardless of any RTL
57 // characters. (Within each component, RTL sequences are rendered from
58 // right to left as expected.)
59 return U_LEFT_TO_RIGHT;
60 default:
61 return U_BIDI_CLASS_DEFAULT;
62 }
63 }
64
65 } // namespace
66
BiDiLineIterator()67 BiDiLineIterator::BiDiLineIterator() : bidi_(nullptr) {}
68
~BiDiLineIterator()69 BiDiLineIterator::~BiDiLineIterator() {
70 if (bidi_) {
71 ubidi_close(bidi_);
72 bidi_ = nullptr;
73 }
74 }
75
Open(const string16 & text,TextDirection direction,CustomBehavior behavior)76 bool BiDiLineIterator::Open(const string16& text,
77 TextDirection direction,
78 CustomBehavior behavior) {
79 DCHECK(!bidi_);
80 UErrorCode error = U_ZERO_ERROR;
81 bidi_ = ubidi_openSized(static_cast<int>(text.length()), 0, &error);
82 if (U_FAILURE(error))
83 return false;
84
85 if (behavior == CustomBehavior::AS_URL) {
86 ubidi_setClassCallback(bidi_, GetURLBiDiClassCallback, nullptr, nullptr,
87 nullptr, &error);
88 if (U_FAILURE(error))
89 return false;
90 }
91
92 ubidi_setPara(bidi_, text.data(), static_cast<int>(text.length()),
93 GetParagraphLevelForDirection(direction), nullptr, &error);
94 return (U_SUCCESS(error));
95 }
96
CountRuns() const97 int BiDiLineIterator::CountRuns() const {
98 DCHECK(bidi_ != nullptr);
99 UErrorCode error = U_ZERO_ERROR;
100 const int runs = ubidi_countRuns(bidi_, &error);
101 return U_SUCCESS(error) ? runs : 0;
102 }
103
GetVisualRun(int index,int * start,int * length) const104 UBiDiDirection BiDiLineIterator::GetVisualRun(int index,
105 int* start,
106 int* length) const {
107 DCHECK(bidi_ != nullptr);
108 return ubidi_getVisualRun(bidi_, index, start, length);
109 }
110
GetLogicalRun(int start,int * end,UBiDiLevel * level) const111 void BiDiLineIterator::GetLogicalRun(int start,
112 int* end,
113 UBiDiLevel* level) const {
114 DCHECK(bidi_ != nullptr);
115 ubidi_getLogicalRun(bidi_, start, end, level);
116 }
117
118 } // namespace i18n
119 } // namespace base
120