2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
5 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
6 * Copyright (C) 2010 Daniel Bates (dbates@intudata.com)
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
26 #include "core/rendering/RenderListMarker.h"
28 #include "core/dom/Document.h"
29 #include "core/fetch/ImageResource.h"
30 #include "core/rendering/GraphicsContextAnnotator.h"
31 #include "core/rendering/RenderLayer.h"
32 #include "core/rendering/RenderListItem.h"
33 #include "core/rendering/RenderView.h"
34 #include "platform/fonts/Font.h"
35 #include "platform/graphics/GraphicsContextStateSaver.h"
36 #include "wtf/text/StringBuilder.h"
37 #include "wtf/unicode/CharacterNames.h"
41 using namespace Unicode;
45 const int cMarkerPadding = 7;
47 enum SequenceType { NumericSequence, AlphabeticSequence };
49 static String toRoman(int number, bool upper)
51 // FIXME: CSS3 describes how to make this work for much larger numbers,
52 // using overbars and special characters. It also specifies the characters
53 // in the range U+2160 to U+217F instead of standard ASCII ones.
54 ASSERT(number >= 1 && number <= 3999);
56 // Big enough to store largest roman number less than 3999 which
57 // is 3888 (MMMDCCCLXXXVIII)
58 const int lettersSize = 15;
59 LChar letters[lettersSize];
62 const LChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
63 const LChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
64 const LChar* digits = upper ? udigits : ldigits;
67 int num = number % 10;
69 for (int i = num % 5; i > 0; i--)
70 letters[lettersSize - ++length] = digits[d];
71 if (num >= 4 && num <= 8)
72 letters[lettersSize - ++length] = digits[d + 1];
74 letters[lettersSize - ++length] = digits[d + 2];
76 letters[lettersSize - ++length] = digits[d];
81 ASSERT(length <= lettersSize);
82 return String(&letters[lettersSize - length], length);
85 // The typedef is needed because taking sizeof(number) in the const expression below doesn't work with some compilers.
86 // This is likely the case because of the template.
87 typedef int numberType;
89 template <typename CharacterType>
90 static inline String toAlphabeticOrNumeric(numberType number, const CharacterType* sequence, unsigned sequenceSize, SequenceType type)
92 ASSERT(sequenceSize >= 2);
94 const int lettersSize = sizeof(numberType) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
96 CharacterType letters[lettersSize];
98 bool isNegativeNumber = false;
99 unsigned numberShadow = number;
100 if (type == AlphabeticSequence) {
103 } else if (number < 0) {
104 numberShadow = -number;
105 isNegativeNumber = true;
107 letters[lettersSize - 1] = sequence[numberShadow % sequenceSize];
110 if (type == AlphabeticSequence) {
111 while ((numberShadow /= sequenceSize) > 0) {
113 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
116 while ((numberShadow /= sequenceSize) > 0)
117 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
119 if (isNegativeNumber)
120 letters[lettersSize - ++length] = hyphenMinus;
122 ASSERT(length <= lettersSize);
123 return String(&letters[lettersSize - length], length);
126 template <typename CharacterType>
127 static String toSymbolic(int number, const CharacterType* symbols, unsigned symbolsSize)
130 ASSERT(symbolsSize >= 1);
131 unsigned numberShadow = number;
134 // The asterisks list-style-type is the worst case; we show |numberShadow| asterisks.
135 StringBuilder letters;
136 letters.append(symbols[numberShadow % symbolsSize]);
137 unsigned numSymbols = numberShadow / symbolsSize;
139 letters.append(symbols[numberShadow % symbolsSize]);
140 return letters.toString();
143 template <typename CharacterType>
144 static String toAlphabetic(int number, const CharacterType* alphabet, unsigned alphabetSize)
146 return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence);
149 template <typename CharacterType>
150 static String toNumeric(int number, const CharacterType* numerals, unsigned numeralsSize)
152 return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence);
155 template <typename CharacterType, size_t size>
156 static inline String toAlphabetic(int number, const CharacterType(&alphabet)[size])
158 return toAlphabetic(number, alphabet, size);
161 template <typename CharacterType, size_t size>
162 static inline String toNumeric(int number, const CharacterType(&alphabet)[size])
164 return toNumeric(number, alphabet, size);
167 template <typename CharacterType, size_t size>
168 static inline String toSymbolic(int number, const CharacterType(&alphabet)[size])
170 return toSymbolic(number, alphabet, size);
173 static int toHebrewUnder1000(int number, UChar letters[5])
175 // FIXME: CSS3 mentions various refinements not implemented here.
176 // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
177 ASSERT(number >= 0 && number < 1000);
179 int fourHundreds = number / 400;
180 for (int i = 0; i < fourHundreds; i++)
181 letters[length++] = 1511 + 3;
184 letters[length++] = 1511 + (number / 100) - 1;
186 if (number == 15 || number == 16) {
187 letters[length++] = 1487 + 9;
188 letters[length++] = 1487 + number - 9;
190 if (int tens = number / 10) {
191 static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
192 letters[length++] = hebrewTens[tens - 1];
194 if (int ones = number % 10)
195 letters[length++] = 1487 + ones;
201 static String toHebrew(int number)
203 // FIXME: CSS3 mentions ways to make this work for much larger numbers.
204 ASSERT(number >= 0 && number <= 999999);
207 static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
208 return String(hebrewZero, 3);
211 const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
212 UChar letters[lettersSize];
218 length = toHebrewUnder1000(number / 1000, letters);
219 letters[length++] = '\'';
220 number = number % 1000;
222 length += toHebrewUnder1000(number, letters + length);
224 ASSERT(length <= lettersSize);
225 return String(letters, length);
228 static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
230 ASSERT(number >= 0 && number < 10000);
233 int lowerOffset = upper ? 0 : 0x0030;
235 if (int thousands = number / 1000) {
236 if (thousands == 7) {
237 letters[length++] = 0x0552 + lowerOffset;
239 letters[length++] = 0x0302;
241 letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
243 letters[length++] = 0x0302;
247 if (int hundreds = (number / 100) % 10) {
248 letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
250 letters[length++] = 0x0302;
253 if (int tens = (number / 10) % 10) {
254 letters[length++] = (0x053A - 1 + lowerOffset) + tens;
256 letters[length++] = 0x0302;
259 if (int ones = number % 10) {
260 letters[length++] = (0x531 - 1 + lowerOffset) + ones;
262 letters[length++] = 0x0302;
268 static String toArmenian(int number, bool upper)
270 ASSERT(number >= 1 && number <= 99999999);
272 const int lettersSize = 18; // twice what toArmenianUnder10000 needs
273 UChar letters[lettersSize];
275 int length = toArmenianUnder10000(number / 10000, upper, true, letters);
276 length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
278 ASSERT(length <= lettersSize);
279 return String(letters, length);
282 static String toGeorgian(int number)
284 ASSERT(number >= 1 && number <= 19999);
286 const int lettersSize = 5;
287 UChar letters[lettersSize];
292 letters[length++] = 0x10F5;
294 if (int thousands = (number / 1000) % 10) {
295 static const UChar georgianThousands[9] = {
296 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
298 letters[length++] = georgianThousands[thousands - 1];
301 if (int hundreds = (number / 100) % 10) {
302 static const UChar georgianHundreds[9] = {
303 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
305 letters[length++] = georgianHundreds[hundreds - 1];
308 if (int tens = (number / 10) % 10) {
309 static const UChar georgianTens[9] = {
310 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
312 letters[length++] = georgianTens[tens - 1];
315 if (int ones = number % 10) {
316 static const UChar georgianOnes[9] = {
317 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
319 letters[length++] = georgianOnes[ones - 1];
322 ASSERT(length <= lettersSize);
323 return String(letters, length);
326 // The table uses the order from the CSS3 specification:
327 // first 3 group markers, then 3 digit markers, then ten digits.
328 static String toCJKIdeographic(int number, const UChar table[16])
332 enum AbstractCJKChar {
334 secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
335 secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
336 digit0, digit1, digit2, digit3, digit4,
337 digit5, digit6, digit7, digit8, digit9
341 return String(&table[digit0 - 1], 1);
343 const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker
344 const int bufferLength = 4 * groupLength;
345 AbstractCJKChar buffer[bufferLength] = { noChar };
347 for (int i = 0; i < 4; ++i) {
348 int groupValue = number % 10000;
351 // Process least-significant group first, but put it in the buffer last.
352 AbstractCJKChar* group = &buffer[(3 - i) * groupLength];
355 group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i);
357 // Put in the four digits and digit markers for any non-zero digits.
358 group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10));
359 if (number != 0 || groupValue > 9) {
360 int digitValue = ((groupValue / 10) % 10);
361 group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue);
363 group[5] = secondDigitMarker;
365 if (number != 0 || groupValue > 99) {
366 int digitValue = ((groupValue / 100) % 10);
367 group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue);
369 group[3] = thirdDigitMarker;
371 if (number != 0 || groupValue > 999) {
372 int digitValue = groupValue / 1000;
373 group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue);
375 group[1] = fourthDigitMarker;
378 // Remove the tens digit, but leave the marker, for any group that has
379 // a value of less than 20.
380 if (groupValue < 20) {
381 ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1);
389 // Convert into characters, omitting consecutive runs of digit0 and
390 // any trailing digit0.
392 UChar characters[bufferLength];
393 AbstractCJKChar last = noChar;
394 for (int i = 0; i < bufferLength; ++i) {
395 AbstractCJKChar a = buffer[i];
397 if (a != digit0 || last != digit0)
398 characters[length++] = table[a - 1];
405 return String(characters, length);
408 static EListStyleType effectiveListMarkerType(EListStyleType type, int value)
410 // Note, the following switch statement has been explicitly grouped
411 // by list-style-type ordinal range.
415 case BinaryListStyle:
418 case DecimalLeadingZero:
419 case DecimalListStyle:
427 case LowerHexadecimal:
439 case UpperHexadecimal:
441 return type; // Can represent all ordinals.
443 return (value < 1 || value > 99999999) ? DecimalListStyle : type;
445 return (value < 0) ? DecimalListStyle : type;
447 return (value < 1 || value > 19999) ? DecimalListStyle : type;
449 return (value < 0 || value > 999999) ? DecimalListStyle : type;
452 return (value < 1 || value > 3999) ? DecimalListStyle : type;
457 case CjkEarthlyBranch:
458 case CjkHeavenlyStem:
460 case EthiopicAbegede:
461 case EthiopicAbegedeAmEt:
462 case EthiopicAbegedeGez:
463 case EthiopicAbegedeTiEr:
464 case EthiopicAbegedeTiEt:
465 case EthiopicHalehameAaEr:
466 case EthiopicHalehameAaEt:
467 case EthiopicHalehameAmEt:
468 case EthiopicHalehameGez:
469 case EthiopicHalehameOmEt:
470 case EthiopicHalehameSidEt:
471 case EthiopicHalehameSoEt:
472 case EthiopicHalehameTiEr:
473 case EthiopicHalehameTiEt:
474 case EthiopicHalehameTig:
477 case HangulConsonant:
492 case TigrinyaErAbegede:
494 case TigrinyaEtAbegede:
500 return (value < 1) ? DecimalListStyle : type;
503 ASSERT_NOT_REACHED();
507 static UChar listMarkerSuffix(EListStyleType type, int value)
509 // If the list-style-type cannot represent |value| because it's outside its
510 // ordinal range then we fall back to some list style that can represent |value|.
511 EListStyleType effectiveType = effectiveListMarkerType(type, value);
513 // Note, the following switch statement has been explicitly
514 // grouped by list-style-type suffix.
515 switch (effectiveType) {
527 case EthiopicAbegede:
528 case EthiopicAbegedeAmEt:
529 case EthiopicAbegedeGez:
530 case EthiopicAbegedeTiEr:
531 case EthiopicAbegedeTiEt:
532 case EthiopicHalehameAaEr:
533 case EthiopicHalehameAaEt:
534 case EthiopicHalehameAmEt:
535 case EthiopicHalehameGez:
536 case EthiopicHalehameOmEt:
537 case EthiopicHalehameSidEt:
538 case EthiopicHalehameSoEt:
539 case EthiopicHalehameTiEr:
540 case EthiopicHalehameTiEt:
541 case EthiopicHalehameTig:
547 case TigrinyaErAbegede:
549 case TigrinyaEtAbegede:
550 return ethiopicPrefaceColon;
554 case BinaryListStyle:
557 case CjkEarthlyBranch:
558 case CjkHeavenlyStem:
559 case DecimalLeadingZero:
560 case DecimalListStyle:
566 case HangulConsonant:
578 case LowerHexadecimal:
594 case UpperHexadecimal:
602 ASSERT_NOT_REACHED();
606 String listMarkerText(EListStyleType type, int value)
608 // If the list-style-type, say hebrew, cannot represent |value| because it's outside
609 // its ordinal range then we fallback to some list style that can represent |value|.
610 switch (effectiveListMarkerType(type, value)) {
615 static const LChar asterisksSymbols[1] = {
618 return toSymbolic(value, asterisksSymbols);
620 // We use the same characters for text security.
621 // See RenderText::setInternalString.
623 return String(&whiteBullet, 1);
625 return String(&bullet, 1);
627 static const UChar footnotesSymbols[4] = {
628 0x002A, 0x2051, 0x2020, 0x2021
630 return toSymbolic(value, footnotesSymbols);
633 // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
634 // instead, but I think this looks better.
635 return String(&blackSquare, 1);
637 case DecimalListStyle:
638 return String::number(value);
639 case DecimalLeadingZero:
640 if (value < -9 || value > 9)
641 return String::number(value);
643 return "-0" + String::number(-value); // -01 to -09
644 return "0" + String::number(value); // 00 to 09
647 static const UChar arabicIndicNumerals[10] = {
648 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
650 return toNumeric(value, arabicIndicNumerals);
652 case BinaryListStyle: {
653 static const LChar binaryNumerals[2] = {
656 return toNumeric(value, binaryNumerals);
659 static const UChar bengaliNumerals[10] = {
660 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
662 return toNumeric(value, bengaliNumerals);
666 static const UChar khmerNumerals[10] = {
667 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9
669 return toNumeric(value, khmerNumerals);
672 static const UChar devanagariNumerals[10] = {
673 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F
675 return toNumeric(value, devanagariNumerals);
678 static const UChar gujaratiNumerals[10] = {
679 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF
681 return toNumeric(value, gujaratiNumerals);
684 static const UChar gurmukhiNumerals[10] = {
685 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F
687 return toNumeric(value, gurmukhiNumerals);
690 static const UChar kannadaNumerals[10] = {
691 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF
693 return toNumeric(value, kannadaNumerals);
695 case LowerHexadecimal: {
696 static const LChar lowerHexadecimalNumerals[16] = {
697 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
699 return toNumeric(value, lowerHexadecimalNumerals);
702 static const UChar laoNumerals[10] = {
703 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9
705 return toNumeric(value, laoNumerals);
708 static const UChar malayalamNumerals[10] = {
709 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F
711 return toNumeric(value, malayalamNumerals);
714 static const UChar mongolianNumerals[10] = {
715 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819
717 return toNumeric(value, mongolianNumerals);
720 static const UChar myanmarNumerals[10] = {
721 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049
723 return toNumeric(value, myanmarNumerals);
726 static const LChar octalNumerals[8] = {
727 '0', '1', '2', '3', '4', '5', '6', '7'
729 return toNumeric(value, octalNumerals);
732 static const UChar oriyaNumerals[10] = {
733 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F
735 return toNumeric(value, oriyaNumerals);
739 static const UChar urduNumerals[10] = {
740 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9
742 return toNumeric(value, urduNumerals);
745 static const UChar teluguNumerals[10] = {
746 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F
748 return toNumeric(value, teluguNumerals);
751 static const UChar tibetanNumerals[10] = {
752 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29
754 return toNumeric(value, tibetanNumerals);
757 static const UChar thaiNumerals[10] = {
758 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59
760 return toNumeric(value, thaiNumerals);
762 case UpperHexadecimal: {
763 static const LChar upperHexadecimalNumerals[16] = {
764 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
766 return toNumeric(value, upperHexadecimalNumerals);
771 static const LChar lowerLatinAlphabet[26] = {
772 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
773 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
775 return toAlphabetic(value, lowerLatinAlphabet);
779 static const LChar upperLatinAlphabet[26] = {
780 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
781 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
783 return toAlphabetic(value, upperLatinAlphabet);
786 static const UChar lowerGreekAlphabet[24] = {
787 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
788 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
789 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
791 return toAlphabetic(value, lowerGreekAlphabet);
795 // FIXME: This table comes from the CSS3 draft, and is probably
796 // incorrect, given the comments in that draft.
797 static const UChar hiraganaAlphabet[48] = {
798 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
799 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
800 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
801 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
802 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
803 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
805 return toAlphabetic(value, hiraganaAlphabet);
807 case HiraganaIroha: {
808 // FIXME: This table comes from the CSS3 draft, and is probably
809 // incorrect, given the comments in that draft.
810 static const UChar hiraganaIrohaAlphabet[47] = {
811 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
812 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
813 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
814 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
815 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
816 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
818 return toAlphabetic(value, hiraganaIrohaAlphabet);
821 // FIXME: This table comes from the CSS3 draft, and is probably
822 // incorrect, given the comments in that draft.
823 static const UChar katakanaAlphabet[48] = {
824 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
825 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
826 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
827 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
828 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
829 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
831 return toAlphabetic(value, katakanaAlphabet);
833 case KatakanaIroha: {
834 // FIXME: This table comes from the CSS3 draft, and is probably
835 // incorrect, given the comments in that draft.
836 static const UChar katakanaIrohaAlphabet[47] = {
837 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
838 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
839 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
840 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
841 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
842 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
844 return toAlphabetic(value, katakanaIrohaAlphabet);
848 case EthiopicHalehameAaEt:
849 case EthiopicHalehameAaEr: {
850 static const UChar ethiopicHalehameAaErAlphabet[18] = {
851 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290,
852 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348
854 return toAlphabetic(value, ethiopicHalehameAaErAlphabet);
857 case EthiopicHalehameAmEt: {
858 static const UChar ethiopicHalehameAmEtAlphabet[33] = {
859 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
860 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8,
861 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320,
862 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
864 return toAlphabetic(value, ethiopicHalehameAmEtAlphabet);
867 case EthiopicAbegedeAmEt: {
868 static const UChar ethiopicAbegedeAmEtAlphabet[33] = {
869 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
870 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
871 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238,
872 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
874 return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet);
876 case CjkEarthlyBranch: {
877 static const UChar cjkEarthlyBranchAlphabet[12] = {
878 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533,
879 0x9149, 0x620C, 0x4EA5
881 return toAlphabetic(value, cjkEarthlyBranchAlphabet);
883 case CjkHeavenlyStem: {
884 static const UChar cjkHeavenlyStemAlphabet[10] = {
885 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC,
888 return toAlphabetic(value, cjkHeavenlyStemAlphabet);
891 case EthiopicHalehameGez: {
892 static const UChar ethiopicHalehameGezAlphabet[26] = {
893 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260,
894 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
895 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
897 return toAlphabetic(value, ethiopicHalehameGezAlphabet);
899 case EthiopicAbegede:
900 case EthiopicAbegedeGez: {
901 static const UChar ethiopicAbegedeGezAlphabet[26] = {
902 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320,
903 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338,
904 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350
906 return toAlphabetic(value, ethiopicAbegedeGezAlphabet);
908 case HangulConsonant: {
909 static const UChar hangulConsonantAlphabet[14] = {
910 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148,
911 0x314A, 0x314B, 0x314C, 0x314D, 0x314E
913 return toAlphabetic(value, hangulConsonantAlphabet);
916 static const UChar hangulAlphabet[14] = {
917 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790,
918 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558
920 return toAlphabetic(value, hangulAlphabet);
923 case EthiopicHalehameOmEt: {
924 static const UChar ethiopicHalehameOmEtAlphabet[25] = {
925 0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270,
926 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8,
927 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
929 return toAlphabetic(value, ethiopicHalehameOmEtAlphabet);
932 case EthiopicHalehameSidEt: {
933 static const UChar ethiopicHalehameSidEtAlphabet[26] = {
934 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
935 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0,
936 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
938 return toAlphabetic(value, ethiopicHalehameSidEtAlphabet);
941 case EthiopicHalehameSoEt: {
942 static const UChar ethiopicHalehameSoEtAlphabet[22] = {
943 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
944 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0,
945 0x1300, 0x1308, 0x1338, 0x1348
947 return toAlphabetic(value, ethiopicHalehameSoEtAlphabet);
950 case EthiopicHalehameTig: {
951 static const UChar ethiopicHalehameTigAlphabet[27] = {
952 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
953 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
954 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350
956 return toAlphabetic(value, ethiopicHalehameTigAlphabet);
959 case EthiopicHalehameTiEr: {
960 static const UChar ethiopicHalehameTiErAlphabet[31] = {
961 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250,
962 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8,
963 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328,
964 0x1330, 0x1338, 0x1348, 0x1350
966 return toAlphabetic(value, ethiopicHalehameTiErAlphabet);
968 case TigrinyaErAbegede:
969 case EthiopicAbegedeTiEr: {
970 static const UChar ethiopicAbegedeTiErAlphabet[31] = {
971 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
972 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
973 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238,
974 0x1270, 0x1278, 0x1330, 0x1350
976 return toAlphabetic(value, ethiopicAbegedeTiErAlphabet);
979 case EthiopicHalehameTiEt: {
980 static const UChar ethiopicHalehameTiEtAlphabet[34] = {
981 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
982 0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8,
983 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308,
984 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
986 return toAlphabetic(value, ethiopicHalehameTiEtAlphabet);
988 case TigrinyaEtAbegede:
989 case EthiopicAbegedeTiEt: {
990 static const UChar ethiopicAbegedeTiEtAlphabet[34] = {
991 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
992 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
993 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230,
994 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
996 return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet);
999 static const UChar upperGreekAlphabet[24] = {
1000 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399,
1001 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3,
1002 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9
1004 return toAlphabetic(value, upperGreekAlphabet);
1006 case LowerNorwegian: {
1007 static const LChar lowerNorwegianAlphabet[29] = {
1008 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
1009 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
1010 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xE6,
1013 return toAlphabetic(value, lowerNorwegianAlphabet);
1015 case UpperNorwegian: {
1016 static const LChar upperNorwegianAlphabet[29] = {
1017 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
1018 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
1019 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xC6,
1022 return toAlphabetic(value, upperNorwegianAlphabet);
1024 case CJKIdeographic: {
1025 static const UChar traditionalChineseInformalTable[16] = {
1026 0x842C, 0x5104, 0x5146,
1027 0x5341, 0x767E, 0x5343,
1028 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
1029 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
1031 return toCJKIdeographic(value, traditionalChineseInformalTable);
1035 return toRoman(value, false);
1037 return toRoman(value, true);
1041 // CSS3 says "armenian" means "lower-armenian".
1042 // But the CSS2.1 test suite contains uppercase test results for "armenian",
1043 // so we'll match the test suite.
1044 return toArmenian(value, true);
1046 return toArmenian(value, false);
1048 return toGeorgian(value);
1050 return toHebrew(value);
1053 ASSERT_NOT_REACHED();
1057 RenderListMarker::RenderListMarker(RenderListItem* item)
1061 // init RenderObject attributes
1062 setInline(true); // our object is Inline
1063 setReplaced(true); // pretend to be replaced
1066 RenderListMarker::~RenderListMarker()
1069 m_image->removeClient(this);
1072 RenderListMarker* RenderListMarker::createAnonymous(RenderListItem* item)
1074 Document& document = item->document();
1075 RenderListMarker* renderer = new RenderListMarker(item);
1076 renderer->setDocumentForAnonymous(&document);
1080 void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
1082 if (style() && (newStyle.listStylePosition() != style()->listStylePosition() || newStyle.listStyleType() != style()->listStyleType()))
1083 setNeedsLayoutAndPrefWidthsRecalc();
1085 RenderBox::styleWillChange(diff, newStyle);
1088 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
1090 RenderBox::styleDidChange(diff, oldStyle);
1092 if (m_image != style()->listStyleImage()) {
1094 m_image->removeClient(this);
1095 m_image = style()->listStyleImage();
1097 m_image->addClient(this);
1101 InlineBox* RenderListMarker::createInlineBox()
1103 InlineBox* result = RenderBox::createInlineBox();
1104 result->setIsText(isText());
1108 bool RenderListMarker::isImage() const
1110 return m_image && !m_image->errorOccurred();
1113 LayoutRect RenderListMarker::localSelectionRect()
1115 InlineBox* box = inlineBoxWrapper();
1117 return LayoutRect(LayoutPoint(), size());
1118 RootInlineBox& root = inlineBoxWrapper()->root();
1119 LayoutUnit newLogicalTop = root.block().style()->isFlippedBlocksWritingMode() ? inlineBoxWrapper()->logicalBottom() - root.selectionBottom() : root.selectionTop() - inlineBoxWrapper()->logicalTop();
1120 if (root.block().style()->isHorizontalWritingMode())
1121 return LayoutRect(0, newLogicalTop, width(), root.selectionHeight());
1122 return LayoutRect(newLogicalTop, 0, root.selectionHeight(), height());
1125 void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1127 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
1129 if (paintInfo.phase != PaintPhaseForeground)
1132 if (style()->visibility() != VISIBLE)
1135 LayoutPoint boxOrigin(paintOffset + location());
1136 LayoutRect overflowRect(visualOverflowRect());
1137 overflowRect.moveBy(boxOrigin);
1139 if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
1142 LayoutRect box(boxOrigin, size());
1144 IntRect marker = getRelativeMarkerRect();
1145 marker.moveBy(roundedIntPoint(boxOrigin));
1147 GraphicsContext* context = paintInfo.context;
1150 context->drawImage(m_image->image(this, marker.size()).get(), marker);
1151 if (selectionState() != SelectionNone) {
1152 LayoutRect selRect = localSelectionRect();
1153 selRect.moveBy(boxOrigin);
1154 context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor());
1159 if (selectionState() != SelectionNone) {
1160 LayoutRect selRect = localSelectionRect();
1161 selRect.moveBy(boxOrigin);
1162 context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor());
1165 const Color color(resolveColor(CSSPropertyColor));
1166 context->setStrokeColor(color);
1167 context->setStrokeStyle(SolidStroke);
1168 context->setStrokeThickness(1.0f);
1169 context->setFillColor(color);
1171 EListStyleType type = style()->listStyleType();
1174 context->fillEllipse(marker);
1177 context->strokeEllipse(marker);
1180 context->fillRect(marker);
1186 case AmharicAbegede:
1189 case BinaryListStyle:
1192 case CJKIdeographic:
1193 case CjkEarthlyBranch:
1194 case CjkHeavenlyStem:
1195 case DecimalLeadingZero:
1196 case DecimalListStyle:
1199 case EthiopicAbegede:
1200 case EthiopicAbegedeAmEt:
1201 case EthiopicAbegedeGez:
1202 case EthiopicAbegedeTiEr:
1203 case EthiopicAbegedeTiEt:
1204 case EthiopicHalehameAaEr:
1205 case EthiopicHalehameAaEt:
1206 case EthiopicHalehameAmEt:
1207 case EthiopicHalehameGez:
1208 case EthiopicHalehameOmEt:
1209 case EthiopicHalehameSidEt:
1210 case EthiopicHalehameSoEt:
1211 case EthiopicHalehameTiEr:
1212 case EthiopicHalehameTiEt:
1213 case EthiopicHalehameTig:
1218 case HangulConsonant:
1230 case LowerHexadecimal:
1232 case LowerNorwegian:
1248 case TigrinyaErAbegede:
1250 case TigrinyaEtAbegede:
1254 case UpperHexadecimal:
1256 case UpperNorwegian:
1263 if (m_text.isEmpty())
1266 const Font& font = style()->font();
1267 TextRun textRun = RenderBlockFlow::constructTextRun(this, font, m_text, style());
1269 GraphicsContextStateSaver stateSaver(*context, false);
1270 if (!style()->isHorizontalWritingMode()) {
1271 marker.moveBy(roundedIntPoint(-boxOrigin));
1272 marker = marker.transposedRect();
1273 marker.moveBy(IntPoint(roundToInt(box.x()), roundToInt(box.y() - logicalHeight())));
1275 context->translate(marker.x(), marker.maxY());
1276 context->rotate(static_cast<float>(deg2rad(90.)));
1277 context->translate(-marker.x(), -marker.maxY());
1280 TextRunPaintInfo textRunPaintInfo(textRun);
1281 textRunPaintInfo.bounds = marker;
1282 IntPoint textOrigin = IntPoint(marker.x(), marker.y() + style()->fontMetrics().ascent());
1284 if (type == Asterisks || type == Footnotes) {
1285 context->drawText(font, textRunPaintInfo, textOrigin);
1288 // Text is not arbitrary. We can judge whether it's RTL from the first character,
1289 // and we only need to handle the direction RightToLeft for now.
1290 bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
1291 StringBuilder reversedText;
1292 if (textNeedsReversing) {
1293 int length = m_text.length();
1294 reversedText.reserveCapacity(length);
1295 for (int i = length - 1; i >= 0; --i)
1296 reversedText.append(m_text[i]);
1297 ASSERT(reversedText.length() == reversedText.capacity());
1298 textRun.setText(reversedText.toString());
1301 const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1302 UChar suffixStr[2] = {
1303 style()->isLeftToRightDirection() ? suffix : ' ',
1304 style()->isLeftToRightDirection() ? ' ' : suffix
1306 TextRun suffixRun = RenderBlockFlow::constructTextRun(this, font, suffixStr, 2, style(), style()->direction());
1307 TextRunPaintInfo suffixRunInfo(suffixRun);
1308 suffixRunInfo.bounds = marker;
1310 if (style()->isLeftToRightDirection()) {
1311 context->drawText(font, textRunPaintInfo, textOrigin);
1312 context->drawText(font, suffixRunInfo, textOrigin + IntSize(font.width(textRun), 0));
1314 context->drawText(font, suffixRunInfo, textOrigin);
1315 context->drawText(font, textRunPaintInfo, textOrigin + IntSize(font.width(suffixRun), 0));
1320 void RenderListMarker::layout()
1322 ASSERT(needsLayout());
1325 updateMarginsAndContent();
1326 setWidth(m_image->imageSize(this, style()->effectiveZoom()).width());
1327 setHeight(m_image->imageSize(this, style()->effectiveZoom()).height());
1329 setLogicalWidth(minPreferredLogicalWidth());
1330 setLogicalHeight(style()->fontMetrics().height());
1336 Length startMargin = style()->marginStart();
1337 Length endMargin = style()->marginEnd();
1338 if (startMargin.isFixed())
1339 setMarginStart(startMargin.value());
1340 if (endMargin.isFixed())
1341 setMarginEnd(endMargin.value());
1346 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
1348 // A list marker can't have a background or border image, so no need to call the base class method.
1349 if (o != m_image->data())
1352 if (width() != m_image->imageSize(this, style()->effectiveZoom()).width() || height() != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred())
1353 setNeedsLayoutAndPrefWidthsRecalc();
1358 void RenderListMarker::updateMarginsAndContent()
1364 void RenderListMarker::updateContent()
1366 // FIXME: This if-statement is just a performance optimization, but it's messy to use the preferredLogicalWidths dirty bit for this.
1367 // It's unclear if this is a premature optimization.
1368 if (!preferredLogicalWidthsDirty())
1374 // FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful
1375 // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box.
1376 int bulletWidth = style()->fontMetrics().ascent() / 2;
1377 IntSize defaultBulletSize(bulletWidth, bulletWidth);
1378 IntSize imageSize = calculateImageIntrinsicDimensions(m_image.get(), defaultBulletSize, DoNotScaleByEffectiveZoom);
1379 m_image->setContainerSizeForRenderer(this, imageSize, style()->effectiveZoom());
1383 EListStyleType type = style()->listStyleType();
1390 m_text = listMarkerText(type, 0); // value is ignored for these types
1396 case AmharicAbegede:
1399 case BinaryListStyle:
1402 case CJKIdeographic:
1403 case CjkEarthlyBranch:
1404 case CjkHeavenlyStem:
1405 case DecimalLeadingZero:
1406 case DecimalListStyle:
1409 case EthiopicAbegede:
1410 case EthiopicAbegedeAmEt:
1411 case EthiopicAbegedeGez:
1412 case EthiopicAbegedeTiEr:
1413 case EthiopicAbegedeTiEt:
1414 case EthiopicHalehameAaEr:
1415 case EthiopicHalehameAaEt:
1416 case EthiopicHalehameAmEt:
1417 case EthiopicHalehameGez:
1418 case EthiopicHalehameOmEt:
1419 case EthiopicHalehameSidEt:
1420 case EthiopicHalehameSoEt:
1421 case EthiopicHalehameTiEr:
1422 case EthiopicHalehameTiEt:
1423 case EthiopicHalehameTig:
1428 case HangulConsonant:
1440 case LowerHexadecimal:
1442 case LowerNorwegian:
1458 case TigrinyaErAbegede:
1460 case TigrinyaEtAbegede:
1464 case UpperHexadecimal:
1466 case UpperNorwegian:
1469 m_text = listMarkerText(type, m_listItem->value());
1474 void RenderListMarker::computePreferredLogicalWidths()
1476 ASSERT(preferredLogicalWidthsDirty());
1480 LayoutSize imageSize = m_image->imageSize(this, style()->effectiveZoom());
1481 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style()->isHorizontalWritingMode() ? imageSize.width() : imageSize.height();
1482 clearPreferredLogicalWidthsDirty();
1487 const Font& font = style()->font();
1489 LayoutUnit logicalWidth = 0;
1490 EListStyleType type = style()->listStyleType();
1496 logicalWidth = font.width(m_text); // no suffix for these types
1501 logicalWidth = (font.fontMetrics().ascent() * 2 / 3 + 1) / 2 + 2;
1505 case AmharicAbegede:
1508 case BinaryListStyle:
1511 case CJKIdeographic:
1512 case CjkEarthlyBranch:
1513 case CjkHeavenlyStem:
1514 case DecimalLeadingZero:
1515 case DecimalListStyle:
1518 case EthiopicAbegede:
1519 case EthiopicAbegedeAmEt:
1520 case EthiopicAbegedeGez:
1521 case EthiopicAbegedeTiEr:
1522 case EthiopicAbegedeTiEt:
1523 case EthiopicHalehameAaEr:
1524 case EthiopicHalehameAaEt:
1525 case EthiopicHalehameAmEt:
1526 case EthiopicHalehameGez:
1527 case EthiopicHalehameOmEt:
1528 case EthiopicHalehameSidEt:
1529 case EthiopicHalehameSoEt:
1530 case EthiopicHalehameTiEr:
1531 case EthiopicHalehameTiEt:
1532 case EthiopicHalehameTig:
1537 case HangulConsonant:
1549 case LowerHexadecimal:
1551 case LowerNorwegian:
1567 case TigrinyaErAbegede:
1569 case TigrinyaEtAbegede:
1573 case UpperHexadecimal:
1575 case UpperNorwegian:
1578 if (m_text.isEmpty())
1581 LayoutUnit itemWidth = font.width(m_text);
1582 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1583 LayoutUnit suffixSpaceWidth = font.width(RenderBlockFlow::constructTextRun(this, font, suffixSpace, 2, style(), style()->direction()));
1584 logicalWidth = itemWidth + suffixSpaceWidth;
1589 m_minPreferredLogicalWidth = logicalWidth;
1590 m_maxPreferredLogicalWidth = logicalWidth;
1592 clearPreferredLogicalWidthsDirty();
1597 void RenderListMarker::updateMargins()
1599 const FontMetrics& fontMetrics = style()->fontMetrics();
1601 LayoutUnit marginStart = 0;
1602 LayoutUnit marginEnd = 0;
1606 marginEnd = cMarkerPadding;
1607 else switch (style()->listStyleType()) {
1612 marginEnd = fontMetrics.ascent() - minPreferredLogicalWidth() + 1;
1618 if (style()->isLeftToRightDirection()) {
1620 marginStart = -minPreferredLogicalWidth() - cMarkerPadding;
1622 int offset = fontMetrics.ascent() * 2 / 3;
1623 switch (style()->listStyleType()) {
1627 marginStart = -offset - cMarkerPadding - 1;
1632 marginStart = m_text.isEmpty() ? LayoutUnit() : -minPreferredLogicalWidth() - offset / 2;
1635 marginEnd = -marginStart - minPreferredLogicalWidth();
1638 marginEnd = cMarkerPadding;
1640 int offset = fontMetrics.ascent() * 2 / 3;
1641 switch (style()->listStyleType()) {
1645 marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth();
1650 marginEnd = m_text.isEmpty() ? 0 : offset / 2;
1653 marginStart = -marginEnd - minPreferredLogicalWidth();
1658 style()->setMarginStart(Length(marginStart, Fixed));
1659 style()->setMarginEnd(Length(marginEnd, Fixed));
1662 LayoutUnit RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1665 return m_listItem->lineHeight(firstLine, direction, PositionOfInteriorLineBoxes);
1666 return RenderBox::lineHeight(firstLine, direction, linePositionMode);
1669 int RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1671 ASSERT(linePositionMode == PositionOnContainingLine);
1673 return m_listItem->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
1674 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1677 String RenderListMarker::suffix() const
1679 EListStyleType type = style()->listStyleType();
1680 const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1685 // If the suffix is not ' ', an extra space is needed
1687 if (style()->isLeftToRightDirection()) {
1695 return String(data, 2);
1698 bool RenderListMarker::isInside() const
1700 return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
1703 IntRect RenderListMarker::getRelativeMarkerRect()
1706 return IntRect(0, 0, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
1708 IntRect relativeRect;
1709 EListStyleType type = style()->listStyleType();
1713 const Font& font = style()->font();
1714 relativeRect = IntRect(0, 0, font.width(m_text), font.fontMetrics().height());
1720 // FIXME: Are these particular rounding rules necessary?
1721 const FontMetrics& fontMetrics = style()->fontMetrics();
1722 int ascent = fontMetrics.ascent();
1723 int bulletWidth = (ascent * 2 / 3 + 1) / 2;
1724 relativeRect = IntRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
1731 case AmharicAbegede:
1734 case BinaryListStyle:
1737 case CJKIdeographic:
1738 case CjkEarthlyBranch:
1739 case CjkHeavenlyStem:
1740 case DecimalLeadingZero:
1741 case DecimalListStyle:
1744 case EthiopicAbegede:
1745 case EthiopicAbegedeAmEt:
1746 case EthiopicAbegedeGez:
1747 case EthiopicAbegedeTiEr:
1748 case EthiopicAbegedeTiEt:
1749 case EthiopicHalehameAaEr:
1750 case EthiopicHalehameAaEt:
1751 case EthiopicHalehameAmEt:
1752 case EthiopicHalehameGez:
1753 case EthiopicHalehameOmEt:
1754 case EthiopicHalehameSidEt:
1755 case EthiopicHalehameSoEt:
1756 case EthiopicHalehameTiEr:
1757 case EthiopicHalehameTiEt:
1758 case EthiopicHalehameTig:
1763 case HangulConsonant:
1775 case LowerHexadecimal:
1777 case LowerNorwegian:
1793 case TigrinyaErAbegede:
1795 case TigrinyaEtAbegede:
1799 case UpperHexadecimal:
1801 case UpperNorwegian:
1804 if (m_text.isEmpty())
1806 const Font& font = style()->font();
1807 int itemWidth = font.width(m_text);
1808 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1809 int suffixSpaceWidth = font.width(RenderBlockFlow::constructTextRun(this, font, suffixSpace, 2, style(), style()->direction()));
1810 relativeRect = IntRect(0, 0, itemWidth + suffixSpaceWidth, font.fontMetrics().height());
1813 if (!style()->isHorizontalWritingMode()) {
1814 relativeRect = relativeRect.transposedRect();
1815 relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
1818 return relativeRect;
1821 void RenderListMarker::setSelectionState(SelectionState state)
1823 // The selection state for our containing block hierarchy is updated by the base class call.
1824 RenderBox::setSelectionState(state);
1826 if (inlineBoxWrapper() && canUpdateSelectionOnRootLineBoxes())
1827 inlineBoxWrapper()->root().setHasSelectedChildren(state != SelectionNone);
1830 LayoutRect RenderListMarker::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent)
1832 ASSERT(!needsLayout());
1834 if (selectionState() == SelectionNone || !inlineBoxWrapper())
1835 return LayoutRect();
1837 RootInlineBox& root = inlineBoxWrapper()->root();
1838 LayoutRect rect(0, root.selectionTop() - y(), width(), root.selectionHeight());
1840 if (clipToVisibleContent)
1841 computeRectForRepaint(repaintContainer, rect);
1843 rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1848 } // namespace WebCore