blob: 174c83ee69c1029b55b00f5596f32f58d5a818b2 [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights
* reserved.
* Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
* Copyright (C) 2010 Daniel Bates (dbates@intudata.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "third_party/blink/renderer/core/layout/list_marker_text.h"
#include "third_party/blink/renderer/core/layout/text_run_constructor.h"
#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
namespace list_marker_text {
enum SequenceType { kNumericSequence, kAlphabeticSequence };
static String ToRoman(int number, bool upper) {
// FIXME: CSS3 describes how to make this work for much larger numbers,
// using overbars and special characters. It also specifies the characters
// in the range U+2160 to U+217F instead of standard ASCII ones.
DCHECK_GE(number, 1);
DCHECK_LE(number, 3999);
// Big enough to store largest roman number less than 3999 which
// is 3888 (MMMDCCCLXXXVIII)
const int kLettersSize = 15;
LChar letters[kLettersSize];
int length = 0;
const LChar kLdigits[] = {'i', 'v', 'x', 'l', 'c', 'd', 'm'};
const LChar kUdigits[] = {'I', 'V', 'X', 'L', 'C', 'D', 'M'};
const LChar* digits = upper ? kUdigits : kLdigits;
int d = 0;
do {
int num = number % 10;
if (num % 5 < 4)
for (int i = num % 5; i > 0; i--)
letters[kLettersSize - ++length] = digits[d];
if (num >= 4 && num <= 8)
letters[kLettersSize - ++length] = digits[d + 1];
if (num == 9)
letters[kLettersSize - ++length] = digits[d + 2];
if (num % 5 == 4)
letters[kLettersSize - ++length] = digits[d];
number /= 10;
d += 2;
} while (number);
DCHECK_LE(length, kLettersSize);
return String(&letters[kLettersSize - length], length);
}
// The typedef is needed because taking sizeof(number) in the const expression
// below doesn't work with some compilers. This is likely the case because of
// the template.
typedef int numberType;
template <typename CharacterType>
static inline String ToAlphabeticOrNumeric(numberType number,
const CharacterType* sequence,
unsigned sequence_size,
SequenceType type) {
DCHECK_GE(sequence_size, 2u);
// Binary is the worst case; requires one character per bit plus a minus sign.
const int kLettersSize = sizeof(numberType) * 8 + 1;
CharacterType letters[kLettersSize];
bool is_negative_number = false;
unsigned number_shadow = number;
if (type == kAlphabeticSequence) {
DCHECK_GT(number, 0);
--number_shadow;
} else if (number < 0) {
number_shadow = -number;
is_negative_number = true;
}
letters[kLettersSize - 1] = sequence[number_shadow % sequence_size];
int length = 1;
if (type == kAlphabeticSequence) {
while ((number_shadow /= sequence_size) > 0) {
--number_shadow;
letters[kLettersSize - ++length] =
sequence[number_shadow % sequence_size];
}
} else {
while ((number_shadow /= sequence_size) > 0)
letters[kLettersSize - ++length] =
sequence[number_shadow % sequence_size];
}
if (is_negative_number)
letters[kLettersSize - ++length] = kHyphenMinusCharacter;
DCHECK_LE(length, kLettersSize);
return String(&letters[kLettersSize - length], length);
}
template <typename CharacterType>
static String ToAlphabetic(int number,
const CharacterType* alphabet,
unsigned alphabet_size) {
return ToAlphabeticOrNumeric(number, alphabet, alphabet_size,
kAlphabeticSequence);
}
template <typename CharacterType>
static String ToNumeric(int number,
const CharacterType* numerals,
unsigned numerals_size) {
return ToAlphabeticOrNumeric(number, numerals, numerals_size,
kNumericSequence);
}
template <typename CharacterType, size_t size>
static inline String ToAlphabetic(int number,
const CharacterType (&alphabet)[size]) {
return ToAlphabetic(number, alphabet, size);
}
template <typename CharacterType, size_t size>
static inline String ToNumeric(int number,
const CharacterType (&alphabet)[size]) {
return ToNumeric(number, alphabet, size);
}
static void ToHebrewUnder1000(int number, Vector<UChar>& letters) {
// FIXME: CSS3 mentions various refinements not implemented here.
// FIXME: Should take a look at Mozilla's HebrewToText function (in
// nsBulletFrame).
DCHECK_GE(number, 0);
DCHECK_LT(number, 1000);
int four_hundreds = number / 400;
for (int i = 0; i < four_hundreds; i++)
letters.push_front(1511 + 3);
number %= 400;
if (number / 100)
letters.push_front(1511 + (number / 100) - 1);
number %= 100;
if (number == 15 || number == 16) {
letters.push_front(1487 + 9);
letters.push_front(1487 + number - 9);
} else {
if (int tens = number / 10) {
static const UChar kHebrewTens[9] = {1497, 1499, 1500, 1502, 1504,
1505, 1506, 1508, 1510};
letters.push_front(kHebrewTens[tens - 1]);
}
if (int ones = number % 10)
letters.push_front(1487 + ones);
}
}
static String ToHebrew(int number) {
// FIXME: CSS3 mentions ways to make this work for much larger numbers.
DCHECK_GE(number, 0);
DCHECK_LE(number, 999999);
if (number == 0) {
static const UChar kHebrewZero[3] = {0x05E1, 0x05E4, 0x05D0};
return String(kHebrewZero, 3);
}
Vector<UChar> letters;
if (number > 999) {
ToHebrewUnder1000(number / 1000, letters);
letters.push_front('\'');
number = number % 1000;
}
ToHebrewUnder1000(number, letters);
return String(letters);
}
static int ToArmenianUnder10000(int number,
bool upper,
bool add_circumflex,
UChar letters[9]) {
DCHECK_GE(number, 0);
DCHECK_LT(number, 10000);
int length = 0;
int lower_offset = upper ? 0 : 0x0030;
if (int thousands = number / 1000) {
if (thousands == 7) {
letters[length++] = 0x0552 + lower_offset;
if (add_circumflex)
letters[length++] = 0x0302;
} else {
letters[length++] = (0x054C - 1 + lower_offset) + thousands;
if (add_circumflex)
letters[length++] = 0x0302;
}
}
if (int hundreds = (number / 100) % 10) {
letters[length++] = (0x0543 - 1 + lower_offset) + hundreds;
if (add_circumflex)
letters[length++] = 0x0302;
}
if (int tens = (number / 10) % 10) {
letters[length++] = (0x053A - 1 + lower_offset) + tens;
if (add_circumflex)
letters[length++] = 0x0302;
}
if (int ones = number % 10) {
letters[length++] = (0x531 - 1 + lower_offset) + ones;
if (add_circumflex)
letters[length++] = 0x0302;
}
return length;
}
static String ToArmenian(int number, bool upper) {
DCHECK_GE(number, 1);
DCHECK_LE(number, 99999999);
const int kLettersSize = 18; // twice what toArmenianUnder10000 needs
UChar letters[kLettersSize];
int length = ToArmenianUnder10000(number / 10000, upper, true, letters);
length +=
ToArmenianUnder10000(number % 10000, upper, false, letters + length);
DCHECK_LE(length, kLettersSize);
return String(letters, length);
}
static String ToGeorgian(int number) {
DCHECK_GE(number, 1);
DCHECK_LE(number, 19999);
const int kLettersSize = 5;
UChar letters[kLettersSize];
int length = 0;
if (number > 9999)
letters[length++] = 0x10F5;
if (int thousands = (number / 1000) % 10) {
static const UChar kGeorgianThousands[9] = {
0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0};
letters[length++] = kGeorgianThousands[thousands - 1];
}
if (int hundreds = (number / 100) % 10) {
static const UChar kGeorgianHundreds[9] = {
0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8};
letters[length++] = kGeorgianHundreds[hundreds - 1];
}
if (int tens = (number / 10) % 10) {
static const UChar kGeorgianTens[9] = {
0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF};
letters[length++] = kGeorgianTens[tens - 1];
}
if (int ones = number % 10) {
static const UChar kGeorgianOnes[9] = {
0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7};
letters[length++] = kGeorgianOnes[ones - 1];
}
DCHECK_LE(length, kLettersSize);
return String(letters, length);
}
enum CJKLang { kChinese = 1, kKorean, kJapanese };
enum CJKStyle { kFormal, kInformal };
// The table uses the order from the CSS3 specification:
// first 3 group markers, then 3 digit markers, then ten digits, then negative
// symbols.
static String ToCJKIdeographic(int number,
const UChar table[26],
CJKStyle cjk_style) {
enum AbstractCJKChar {
kNoChar = 0,
kLang = 0,
// FourthGroupMarker for simplified chinese has two codepoints, to simplify
// the main algorithm below use two codepoints for all group markers.
kSecondGroupMarker = 1,
kThirdGroupMarker = 3,
kFourthGroupMarker = 5,
kSecondDigitMarker = 7,
kThirdDigitMarker,
kFourthDigitMarker,
kDigit0,
kDigit1,
kDigit2,
kDigit3,
kDigit4,
kDigit5,
kDigit6,
kDigit7,
kDigit8,
kDigit9,
kNeg1,
kNeg2,
kNeg3,
kNeg4,
kNeg5
};
if (number == 0)
return String(&table[kDigit0], 1);
const bool negative = number < 0;
if (negative) {
// Negating the most negative integer (INT_MIN) doesn't work, since it has
// no positive counterpart. Deal with that here, manually.
if (UNLIKELY(number == INT_MIN))
number = INT_MAX;
else
number = -number;
}
const int kGroupLength =
9; // 4 digits, 3 digit markers, group marker of size 2.
const int kBufferLength = 4 * kGroupLength;
AbstractCJKChar buffer[kBufferLength] = {kNoChar};
for (int i = 0; i < 4; ++i) {
int group_value = number % 10000;
number /= 10000;
// Process least-significant group first, but put it in the buffer last.
AbstractCJKChar* group = &buffer[(3 - i) * kGroupLength];
if (group_value && i) {
group[8] = static_cast<AbstractCJKChar>(kSecondGroupMarker + i);
group[7] = static_cast<AbstractCJKChar>(kSecondGroupMarker - 1 + i);
}
// Put in the four digits and digit markers for any non-zero digits.
int digit_value = (group_value % 10);
bool trailing_zero = table[kLang] == kChinese && !digit_value;
if (digit_value) {
bool drop_one = table[kLang] == kKorean && cjk_style == kInformal &&
digit_value == 1 && i > 0;
if (!drop_one)
group[6] = static_cast<AbstractCJKChar>(kDigit0 + (group_value % 10));
}
if (number != 0 || group_value > 9) {
digit_value = ((group_value / 10) % 10);
bool drop_one =
table[kLang] == kKorean && cjk_style == kInformal && digit_value == 1;
if ((digit_value && !drop_one) || (!digit_value && !trailing_zero))
group[4] = static_cast<AbstractCJKChar>(kDigit0 + digit_value);
trailing_zero &= !digit_value;
if (digit_value)
group[5] = kSecondDigitMarker;
}
if (number != 0 || group_value > 99) {
digit_value = ((group_value / 100) % 10);
bool drop_one =
table[kLang] == kKorean && cjk_style == kInformal && digit_value == 1;
if ((digit_value && !drop_one) || (!digit_value && !trailing_zero))
group[2] = static_cast<AbstractCJKChar>(kDigit0 + digit_value);
trailing_zero &= !digit_value;
if (digit_value)
group[3] = kThirdDigitMarker;
}
if (number != 0 || group_value > 999) {
digit_value = group_value / 1000;
bool drop_one =
table[kLang] == kKorean && cjk_style == kInformal && digit_value == 1;
if ((digit_value && !drop_one) || (!digit_value && !trailing_zero))
group[0] = static_cast<AbstractCJKChar>(kDigit0 + digit_value);
if (digit_value)
group[1] = kFourthDigitMarker;
}
if (trailing_zero && i > 0) {
group[6] = group[7];
group[7] = group[8];
group[8] = kDigit0;
}
// Remove the tens digit, but leave the marker, for any group that has
// a value of less than 20.
if (table[kLang] == kChinese && cjk_style == kInformal &&
group_value < 20) {
DCHECK(group[4] == kNoChar || group[4] == kDigit0 || group[4] == kDigit1);
group[4] = kNoChar;
}
if (number == 0)
break;
}
// Convert into characters, omitting consecutive runs of Digit0 and
// any trailing Digit0.
int length = 0;
const int kMaxLengthForNegativeSymbols = 5;
UChar characters[kBufferLength + kMaxLengthForNegativeSymbols];
AbstractCJKChar last = kNoChar;
if (negative) {
while (UChar a = table[kNeg1 + length])
characters[length++] = a;
}
for (int i = 0; i < kBufferLength; ++i) {
AbstractCJKChar a = buffer[i];
if (a != kNoChar) {
if (a != kDigit0 || (table[kLang] == kChinese && last != kDigit0)) {
UChar new_char = table[a];
if (new_char != kNoChar) {
characters[length++] = table[a];
if (table[kLang] == kKorean &&
(a == kSecondGroupMarker || a == kThirdGroupMarker ||
a == kFourthGroupMarker))
characters[length++] = ' ';
}
}
last = a;
}
}
if ((table[kLang] == kChinese && last == kDigit0) ||
characters[length - 1] == ' ')
--length;
return String(characters, length);
}
static EListStyleType EffectiveListMarkerType(EListStyleType type, int count) {
// Note, the following switch statement has been explicitly grouped
// by list-style-type ordinal range.
switch (type) {
case EListStyleType::kArabicIndic:
case EListStyleType::kBengali:
case EListStyleType::kCambodian:
case EListStyleType::kCircle:
case EListStyleType::kDecimalLeadingZero:
case EListStyleType::kDecimal:
case EListStyleType::kDevanagari:
case EListStyleType::kDisc:
case EListStyleType::kGujarati:
case EListStyleType::kGurmukhi:
case EListStyleType::kKannada:
case EListStyleType::kKhmer:
case EListStyleType::kLao:
case EListStyleType::kMalayalam:
case EListStyleType::kMongolian:
case EListStyleType::kMyanmar:
case EListStyleType::kNone:
case EListStyleType::kOriya:
case EListStyleType::kPersian:
case EListStyleType::kSquare:
case EListStyleType::kTelugu:
case EListStyleType::kThai:
case EListStyleType::kTibetan:
case EListStyleType::kUrdu:
case EListStyleType::kKoreanHangulFormal:
case EListStyleType::kKoreanHanjaFormal:
case EListStyleType::kKoreanHanjaInformal:
case EListStyleType::kCjkIdeographic:
case EListStyleType::kSimpChineseFormal:
case EListStyleType::kSimpChineseInformal:
case EListStyleType::kTradChineseFormal:
case EListStyleType::kTradChineseInformal:
return type; // Can represent all ordinals.
case EListStyleType::kArmenian:
case EListStyleType::kLowerArmenian:
case EListStyleType::kUpperArmenian:
return (count < 1 || count > 99999999) ? EListStyleType::kDecimal : type;
case EListStyleType::kGeorgian:
return (count < 1 || count > 19999) ? EListStyleType::kDecimal : type;
case EListStyleType::kHebrew:
return (count < 0 || count > 999999) ? EListStyleType::kDecimal : type;
case EListStyleType::kLowerRoman:
case EListStyleType::kUpperRoman:
return (count < 1 || count > 3999) ? EListStyleType::kDecimal : type;
case EListStyleType::kCjkEarthlyBranch:
case EListStyleType::kCjkHeavenlyStem:
case EListStyleType::kEthiopicHalehameAm:
case EListStyleType::kEthiopicHalehame:
case EListStyleType::kEthiopicHalehameTiEr:
case EListStyleType::kEthiopicHalehameTiEt:
case EListStyleType::kHangul:
case EListStyleType::kHangulConsonant:
case EListStyleType::kHiragana:
case EListStyleType::kHiraganaIroha:
case EListStyleType::kKatakana:
case EListStyleType::kKatakanaIroha:
case EListStyleType::kLowerAlpha:
case EListStyleType::kLowerGreek:
case EListStyleType::kLowerLatin:
case EListStyleType::kUpperAlpha:
case EListStyleType::kUpperLatin:
return (count < 1) ? EListStyleType::kDecimal : type;
case EListStyleType::kString:
NOTREACHED();
break;
}
NOTREACHED();
return type;
}
UChar Suffix(EListStyleType type, int count) {
// If the list-style-type cannot represent |count| because it's outside its
// ordinal range then we fall back to some list style that can represent
// |count|.
EListStyleType effective_type = EffectiveListMarkerType(type, count);
// Note, the following switch statement has been explicitly
// grouped by list-style-type suffix.
switch (effective_type) {
case EListStyleType::kCircle:
case EListStyleType::kDisc:
case EListStyleType::kNone:
case EListStyleType::kSquare:
return ' ';
case EListStyleType::kEthiopicHalehame:
case EListStyleType::kEthiopicHalehameAm:
case EListStyleType::kEthiopicHalehameTiEr:
case EListStyleType::kEthiopicHalehameTiEt:
return kEthiopicPrefaceColonCharacter;
case EListStyleType::kArmenian:
case EListStyleType::kArabicIndic:
case EListStyleType::kBengali:
case EListStyleType::kCambodian:
case EListStyleType::kCjkIdeographic:
case EListStyleType::kCjkEarthlyBranch:
case EListStyleType::kCjkHeavenlyStem:
case EListStyleType::kDecimalLeadingZero:
case EListStyleType::kDecimal:
case EListStyleType::kDevanagari:
case EListStyleType::kGeorgian:
case EListStyleType::kGujarati:
case EListStyleType::kGurmukhi:
case EListStyleType::kHangul:
case EListStyleType::kHangulConsonant:
case EListStyleType::kHebrew:
case EListStyleType::kHiragana:
case EListStyleType::kHiraganaIroha:
case EListStyleType::kKannada:
case EListStyleType::kKatakana:
case EListStyleType::kKatakanaIroha:
case EListStyleType::kKhmer:
case EListStyleType::kLao:
case EListStyleType::kLowerAlpha:
case EListStyleType::kLowerArmenian:
case EListStyleType::kLowerGreek:
case EListStyleType::kLowerLatin:
case EListStyleType::kLowerRoman:
case EListStyleType::kMalayalam:
case EListStyleType::kMongolian:
case EListStyleType::kMyanmar:
case EListStyleType::kOriya:
case EListStyleType::kPersian:
case EListStyleType::kTelugu:
case EListStyleType::kThai:
case EListStyleType::kTibetan:
case EListStyleType::kUpperAlpha:
case EListStyleType::kUpperArmenian:
case EListStyleType::kUpperLatin:
case EListStyleType::kUpperRoman:
case EListStyleType::kUrdu:
return '.';
case EListStyleType::kSimpChineseFormal:
case EListStyleType::kSimpChineseInformal:
case EListStyleType::kTradChineseFormal:
case EListStyleType::kTradChineseInformal:
case EListStyleType::kKoreanHangulFormal:
case EListStyleType::kKoreanHanjaFormal:
case EListStyleType::kKoreanHanjaInformal:
return 0x3001;
case EListStyleType::kString:
NOTREACHED();
break;
}
NOTREACHED();
return '.';
}
String GetText(EListStyleType type, int count) {
// If the list-style-type, say hebrew, cannot represent |count| because it's
// outside its ordinal range then we fallback to some list style that can
// represent |count|.
switch (EffectiveListMarkerType(type, count)) {
case EListStyleType::kNone:
return "";
// We use the same characters for text security.
// See LayoutText::setInternalString.
case EListStyleType::kCircle:
return String(&kWhiteBulletCharacter, 1);
case EListStyleType::kDisc:
return String(&kBulletCharacter, 1);
case EListStyleType::kSquare:
// The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
// instead, but I think this looks better.
return String(&kBlackSquareCharacter, 1);
case EListStyleType::kDecimal:
return String::Number(count);
case EListStyleType::kDecimalLeadingZero:
if (count < -9 || count > 9)
return String::Number(count);
if (count < 0)
return "-0" + String::Number(-count); // -01 to -09
return "0" + String::Number(count); // 00 to 09
case EListStyleType::kArabicIndic: {
static const UChar kArabicIndicNumerals[10] = {
0x0660, 0x0661, 0x0662, 0x0663, 0x0664,
0x0665, 0x0666, 0x0667, 0x0668, 0x0669};
return ToNumeric(count, kArabicIndicNumerals);
}
case EListStyleType::kBengali: {
static const UChar kBengaliNumerals[10] = {0x09E6, 0x09E7, 0x09E8, 0x09E9,
0x09EA, 0x09EB, 0x09EC, 0x09ED,
0x09EE, 0x09EF};
return ToNumeric(count, kBengaliNumerals);
}
case EListStyleType::kCambodian:
case EListStyleType::kKhmer: {
static const UChar kKhmerNumerals[10] = {0x17E0, 0x17E1, 0x17E2, 0x17E3,
0x17E4, 0x17E5, 0x17E6, 0x17E7,
0x17E8, 0x17E9};
return ToNumeric(count, kKhmerNumerals);
}
case EListStyleType::kDevanagari: {
static const UChar kDevanagariNumerals[10] = {
0x0966, 0x0967, 0x0968, 0x0969, 0x096A,
0x096B, 0x096C, 0x096D, 0x096E, 0x096F};
return ToNumeric(count, kDevanagariNumerals);
}
case EListStyleType::kGujarati: {
static const UChar kGujaratiNumerals[10] = {
0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA,
0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF};
return ToNumeric(count, kGujaratiNumerals);
}
case EListStyleType::kGurmukhi: {
static const UChar kGurmukhiNumerals[10] = {
0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A,
0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F};
return ToNumeric(count, kGurmukhiNumerals);
}
case EListStyleType::kKannada: {
static const UChar kKannadaNumerals[10] = {0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9,
0x0CEA, 0x0CEB, 0x0CEC, 0x0CED,
0x0CEE, 0x0CEF};
return ToNumeric(count, kKannadaNumerals);
}
case EListStyleType::kLao: {
static const UChar kLaoNumerals[10] = {0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3,
0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7,
0x0ED8, 0x0ED9};
return ToNumeric(count, kLaoNumerals);
}
case EListStyleType::kMalayalam: {
static const UChar kMalayalamNumerals[10] = {
0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A,
0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F};
return ToNumeric(count, kMalayalamNumerals);
}
case EListStyleType::kMongolian: {
static const UChar kMongolianNumerals[10] = {
0x1810, 0x1811, 0x1812, 0x1813, 0x1814,
0x1815, 0x1816, 0x1817, 0x1818, 0x1819};
return ToNumeric(count, kMongolianNumerals);
}
case EListStyleType::kMyanmar: {
static const UChar kMyanmarNumerals[10] = {0x1040, 0x1041, 0x1042, 0x1043,
0x1044, 0x1045, 0x1046, 0x1047,
0x1048, 0x1049};
return ToNumeric(count, kMyanmarNumerals);
}
case EListStyleType::kOriya: {
static const UChar kOriyaNumerals[10] = {0x0B66, 0x0B67, 0x0B68, 0x0B69,
0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D,
0x0B6E, 0x0B6F};
return ToNumeric(count, kOriyaNumerals);
}
case EListStyleType::kPersian:
case EListStyleType::kUrdu: {
static const UChar kUrduNumerals[10] = {0x06F0, 0x06F1, 0x06F2, 0x06F3,
0x06F4, 0x06F5, 0x06F6, 0x06F7,
0x06F8, 0x06F9};
return ToNumeric(count, kUrduNumerals);
}
case EListStyleType::kTelugu: {
static const UChar kTeluguNumerals[10] = {0x0C66, 0x0C67, 0x0C68, 0x0C69,
0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D,
0x0C6E, 0x0C6F};
return ToNumeric(count, kTeluguNumerals);
}
case EListStyleType::kTibetan: {
static const UChar kTibetanNumerals[10] = {0x0F20, 0x0F21, 0x0F22, 0x0F23,
0x0F24, 0x0F25, 0x0F26, 0x0F27,
0x0F28, 0x0F29};
return ToNumeric(count, kTibetanNumerals);
}
case EListStyleType::kThai: {
static const UChar kThaiNumerals[10] = {0x0E50, 0x0E51, 0x0E52, 0x0E53,
0x0E54, 0x0E55, 0x0E56, 0x0E57,
0x0E58, 0x0E59};
return ToNumeric(count, kThaiNumerals);
}
case EListStyleType::kLowerAlpha:
case EListStyleType::kLowerLatin: {
static const LChar kLowerLatinAlphabet[26] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
return ToAlphabetic(count, kLowerLatinAlphabet);
}
case EListStyleType::kUpperAlpha:
case EListStyleType::kUpperLatin: {
static const LChar kUpperLatinAlphabet[26] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
return ToAlphabetic(count, kUpperLatinAlphabet);
}
case EListStyleType::kLowerGreek: {
static const UChar kLowerGreekAlphabet[24] = {
0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9};
return ToAlphabetic(count, kLowerGreekAlphabet);
}
case EListStyleType::kHiragana: {
// FIXME: This table comes from the CSS3 draft, and is probably
// incorrect, given the comments in that draft.
static const UChar kHiraganaAlphabet[48] = {
0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093};
return ToAlphabetic(count, kHiraganaAlphabet);
}
case EListStyleType::kHiraganaIroha: {
// FIXME: This table comes from the CSS3 draft, and is probably
// incorrect, given the comments in that draft.
static const UChar kHiraganaIrohaAlphabet[47] = {
0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059};
return ToAlphabetic(count, kHiraganaIrohaAlphabet);
}
case EListStyleType::kKatakana: {
// FIXME: This table comes from the CSS3 draft, and is probably
// incorrect, given the comments in that draft.
static const UChar kKatakanaAlphabet[48] = {
0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3};
return ToAlphabetic(count, kKatakanaAlphabet);
}
case EListStyleType::kKatakanaIroha: {
// FIXME: This table comes from the CSS3 draft, and is probably
// incorrect, given the comments in that draft.
static const UChar kKatakanaIrohaAlphabet[47] = {
0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9};
return ToAlphabetic(count, kKatakanaIrohaAlphabet);
}
case EListStyleType::kCjkEarthlyBranch: {
static const UChar kCjkEarthlyBranchAlphabet[12] = {
0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3,
0x5348, 0x672A, 0x7533, 0x9149, 0x620C, 0x4EA5};
return ToAlphabetic(count, kCjkEarthlyBranchAlphabet);
}
case EListStyleType::kCjkHeavenlyStem: {
static const UChar kCjkHeavenlyStemAlphabet[10] = {
0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A,
0x5DF1, 0x5E9A, 0x8F9B, 0x58EC, 0x7678};
return ToAlphabetic(count, kCjkHeavenlyStemAlphabet);
}
case EListStyleType::kHangulConsonant: {
static const UChar kHangulConsonantAlphabet[14] = {
0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145,
0x3147, 0x3148, 0x314A, 0x314B, 0x314C, 0x314D, 0x314E};
return ToAlphabetic(count, kHangulConsonantAlphabet);
}
case EListStyleType::kHangul: {
static const UChar kHangulAlphabet[14] = {
0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC,
0xC544, 0xC790, 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558};
return ToAlphabetic(count, kHangulAlphabet);
}
case EListStyleType::kEthiopicHalehame: {
static const UChar kEthiopicHalehameGezAlphabet[26] = {
0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230,
0x1240, 0x1260, 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8,
0x12C8, 0x12D0, 0x12D8, 0x12E8, 0x12F0, 0x1308, 0x1320,
0x1330, 0x1338, 0x1340, 0x1348, 0x1350};
return ToAlphabetic(count, kEthiopicHalehameGezAlphabet);
}
case EListStyleType::kEthiopicHalehameAm: {
static const UChar kEthiopicHalehameAmAlphabet[33] = {
0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230,
0x1238, 0x1240, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290,
0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12D8,
0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328,
0x1330, 0x1338, 0x1340, 0x1348, 0x1350};
return ToAlphabetic(count, kEthiopicHalehameAmAlphabet);
}
case EListStyleType::kEthiopicHalehameTiEr: {
static const UChar kEthiopicHalehameTiErAlphabet[31] = {
0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240,
0x1250, 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8,
0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300,
0x1308, 0x1320, 0x1328, 0x1330, 0x1338, 0x1348, 0x1350};
return ToAlphabetic(count, kEthiopicHalehameTiErAlphabet);
}
case EListStyleType::kEthiopicHalehameTiEt: {
static const UChar kEthiopicHalehameTiEtAlphabet[34] = {
0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230,
0x1238, 0x1240, 0x1250, 0x1260, 0x1270, 0x1278, 0x1280,
0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0,
0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320,
0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350};
return ToAlphabetic(count, kEthiopicHalehameTiEtAlphabet);
}
case EListStyleType::kKoreanHangulFormal: {
static const UChar kKoreanHangulFormalTable[26] = {
kKorean, 0xB9CC, 0x0000, 0xC5B5, 0x0000, 0xC870, 0x0000,
0xC2ED, 0xBC31, 0xCC9C, 0xC601, 0xC77C, 0xC774, 0xC0BC,
0xC0AC, 0xC624, 0xC721, 0xCE60, 0xD314, 0xAD6C, 0xB9C8,
0xC774, 0xB108, 0xC2A4, 0x0020, 0x0000};
return ToCJKIdeographic(count, kKoreanHangulFormalTable, kFormal);
}
case EListStyleType::kKoreanHanjaFormal: {
static const UChar kKoreanHanjaFormalTable[26] = {
kKorean, 0x842C, 0x0000, 0x5104, 0x0000, 0x5146, 0x0000,
0x62FE, 0x767E, 0x4EDF, 0x96F6, 0x58F9, 0x8CB3, 0x53C3,
0x56DB, 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D, 0xB9C8,
0xC774, 0xB108, 0xC2A4, 0x0020, 0x0000};
return ToCJKIdeographic(count, kKoreanHanjaFormalTable, kFormal);
}
case EListStyleType::kKoreanHanjaInformal: {
static const UChar kKoreanHanjaInformalTable[26] = {
kKorean, 0x842C, 0x0000, 0x5104, 0x0000, 0x5146, 0x0000,
0x5341, 0x767E, 0x5343, 0x96F6, 0x4E00, 0x4E8C, 0x4E09,
0x56DB, 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D, 0xB9C8,
0xC774, 0xB108, 0xC2A4, 0x0020, 0x0000};
return ToCJKIdeographic(count, kKoreanHanjaInformalTable, kInformal);
}
case EListStyleType::kCjkIdeographic:
case EListStyleType::kTradChineseInformal: {
static const UChar kTraditionalChineseInformalTable[22] = {
kChinese, 0x842C, 0x0000, 0x5104, 0x0000, 0x5146, 0x0000, 0x5341,
0x767E, 0x5343, 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB, 0x4E94,
0x516D, 0x4E03, 0x516B, 0x4E5D, 0x8CA0, 0x0000};
return ToCJKIdeographic(count, kTraditionalChineseInformalTable,
kInformal);
}
case EListStyleType::kSimpChineseInformal: {
static const UChar kSimpleChineseInformalTable[22] = {
kChinese, 0x4E07, 0x0000, 0x4EBF, 0x0000, 0x4E07, 0x4EBF, 0x5341,
0x767E, 0x5343, 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB, 0x4E94,
0x516D, 0x4E03, 0x516B, 0x4E5D, 0x8D1F, 0x0000};
return ToCJKIdeographic(count, kSimpleChineseInformalTable, kInformal);
}
case EListStyleType::kTradChineseFormal: {
static const UChar kTraditionalChineseFormalTable[22] = {
kChinese, 0x842C, 0x0000, 0x5104, 0x0000, 0x5146, 0x0000, 0x62FE,
0x4F70, 0x4EDF, 0x96F6, 0x58F9, 0x8CB3, 0x53C3, 0x8086, 0x4F0D,
0x9678, 0x67D2, 0x634C, 0x7396, 0x8CA0, 0x0000};
return ToCJKIdeographic(count, kTraditionalChineseFormalTable, kFormal);
}
case EListStyleType::kSimpChineseFormal: {
static const UChar kSimpleChineseFormalTable[22] = {
kChinese, 0x4E07, 0x0000, 0x4EBF, 0x0000, 0x4E07, 0x4EBF, 0x62FE,
0x4F70, 0x4EDF, 0x96F6, 0x58F9, 0x8D30, 0x53C1, 0x8086, 0x4F0D,
0x9646, 0x67D2, 0x634C, 0x7396, 0x8D1F, 0x0000};
return ToCJKIdeographic(count, kSimpleChineseFormalTable, kFormal);
}
case EListStyleType::kLowerRoman:
return ToRoman(count, false);
case EListStyleType::kUpperRoman:
return ToRoman(count, true);
case EListStyleType::kArmenian:
case EListStyleType::kUpperArmenian:
// CSS3 says "armenian" means "lower-armenian".
// But the CSS2.1 test suite contains uppercase test results for
// "armenian", so we'll match the test suite.
return ToArmenian(count, true);
case EListStyleType::kLowerArmenian:
return ToArmenian(count, false);
case EListStyleType::kGeorgian:
return ToGeorgian(count);
case EListStyleType::kHebrew:
return ToHebrew(count);
case EListStyleType::kString:
NOTREACHED();
break;
}
NOTREACHED();
return "";
}
} // namespace list_marker_text
} // namespace blink