blob: 3f7f8686614b07f00db63af77fd790e4c7a1b949 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/i18n/bidi_line_iterator.h"
#include "base/logging.h"
namespace base {
namespace i18n {
namespace {
UBiDiLevel GetParagraphLevelForDirection(TextDirection direction) {
switch (direction) {
case UNKNOWN_DIRECTION:
return UBIDI_DEFAULT_LTR;
break;
case RIGHT_TO_LEFT:
return 1; // Highest RTL level.
break;
case LEFT_TO_RIGHT:
return 0; // Highest LTR level.
break;
default:
NOTREACHED();
return 0;
}
}
// Overrides the default bidi class for a given character, for the custom
// "AS_URL" behavior. Returns U_BIDI_CLASS_DEFAULT to defer to the default ICU
// behavior.
//
// Matches the C callback interface of ICU's UBiDiClassCallback type (which is
// why there is an unused argument).
UCharDirection GetURLBiDiClassCallback(const void* /*unused*/, UChar32 c) {
// Note: Use a switch statement instead of strchr() to avoid iterating over a
// string for each character (the switch allows for much better compiler
// optimization).
switch (c) {
// The set of characters that delimit URL components (separating the scheme,
// username, password, domain labels, host, path segments, query
// names/values and fragment).
case '#':
case '&':
case '.':
case '/':
case ':':
case '=':
case '?':
case '@':
// Treat all of these characters as strong LTR, which effectively
// surrounds all of the text components of a URL (e.g., the domain labels
// and path segments) in a left-to-right embedding. This ensures that the
// URL components read from left to right, regardless of any RTL
// characters. (Within each component, RTL sequences are rendered from
// right to left as expected.)
return U_LEFT_TO_RIGHT;
default:
return U_BIDI_CLASS_DEFAULT;
}
}
} // namespace
BiDiLineIterator::BiDiLineIterator() : bidi_(nullptr) {}
BiDiLineIterator::~BiDiLineIterator() {
if (bidi_) {
ubidi_close(bidi_);
bidi_ = nullptr;
}
}
bool BiDiLineIterator::Open(const string16& text,
TextDirection direction,
CustomBehavior behavior) {
DCHECK(!bidi_);
UErrorCode error = U_ZERO_ERROR;
bidi_ = ubidi_openSized(static_cast<int>(text.length()), 0, &error);
if (U_FAILURE(error))
return false;
if (behavior == CustomBehavior::AS_URL) {
ubidi_setClassCallback(bidi_, GetURLBiDiClassCallback, nullptr, nullptr,
nullptr, &error);
if (U_FAILURE(error))
return false;
}
ubidi_setPara(bidi_, text.data(), static_cast<int>(text.length()),
GetParagraphLevelForDirection(direction), nullptr, &error);
return (U_SUCCESS(error));
}
int BiDiLineIterator::CountRuns() const {
DCHECK(bidi_ != nullptr);
UErrorCode error = U_ZERO_ERROR;
const int runs = ubidi_countRuns(bidi_, &error);
return U_SUCCESS(error) ? runs : 0;
}
UBiDiDirection BiDiLineIterator::GetVisualRun(int index,
int* start,
int* length) const {
DCHECK(bidi_ != nullptr);
return ubidi_getVisualRun(bidi_, index, start, length);
}
void BiDiLineIterator::GetLogicalRun(int start,
int* end,
UBiDiLevel* level) const {
DCHECK(bidi_ != nullptr);
ubidi_getLogicalRun(bidi_, start, end, level);
}
} // namespace i18n
} // namespace base