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/LayoutRectRecorder.h"
32 #include "core/rendering/RenderLayer.h"
33 #include "core/rendering/RenderListItem.h"
34 #include "core/rendering/RenderView.h"
35 #include "platform/fonts/Font.h"
36 #include "platform/graphics/GraphicsContextStateSaver.h"
37 #include "wtf/text/StringBuilder.h"
38 #include "wtf/unicode/CharacterNames.h"
42 using namespace Unicode;
46 const int cMarkerPadding = 7;
48 enum SequenceType { NumericSequence, AlphabeticSequence };
50 static String toRoman(int number, bool upper)
52 // FIXME: CSS3 describes how to make this work for much larger numbers,
53 // using overbars and special characters. It also specifies the characters
54 // in the range U+2160 to U+217F instead of standard ASCII ones.
55 ASSERT(number >= 1 && number <= 3999);
57 // Big enough to store largest roman number less than 3999 which
58 // is 3888 (MMMDCCCLXXXVIII)
59 const int lettersSize = 15;
60 LChar letters[lettersSize];
63 const LChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
64 const LChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
65 const LChar* digits = upper ? udigits : ldigits;
68 int num = number % 10;
70 for (int i = num % 5; i > 0; i--)
71 letters[lettersSize - ++length] = digits[d];
72 if (num >= 4 && num <= 8)
73 letters[lettersSize - ++length] = digits[d + 1];
75 letters[lettersSize - ++length] = digits[d + 2];
77 letters[lettersSize - ++length] = digits[d];
82 ASSERT(length <= lettersSize);
83 return String(&letters[lettersSize - length], length);
86 // The typedef is needed because taking sizeof(number) in the const expression below doesn't work with some compilers.
87 // This is likely the case because of the template.
88 typedef int numberType;
90 template <typename CharacterType>
91 static inline String toAlphabeticOrNumeric(numberType number, const CharacterType* sequence, unsigned sequenceSize, SequenceType type)
93 ASSERT(sequenceSize >= 2);
95 const int lettersSize = sizeof(numberType) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
97 CharacterType letters[lettersSize];
99 bool isNegativeNumber = false;
100 unsigned numberShadow = number;
101 if (type == AlphabeticSequence) {
104 } else if (number < 0) {
105 numberShadow = -number;
106 isNegativeNumber = true;
108 letters[lettersSize - 1] = sequence[numberShadow % sequenceSize];
111 if (type == AlphabeticSequence) {
112 while ((numberShadow /= sequenceSize) > 0) {
114 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
117 while ((numberShadow /= sequenceSize) > 0)
118 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
120 if (isNegativeNumber)
121 letters[lettersSize - ++length] = hyphenMinus;
123 ASSERT(length <= lettersSize);
124 return String(&letters[lettersSize - length], length);
127 template <typename CharacterType>
128 static String toSymbolic(int number, const CharacterType* symbols, unsigned symbolsSize)
131 ASSERT(symbolsSize >= 1);
132 unsigned numberShadow = number;
135 // The asterisks list-style-type is the worst case; we show |numberShadow| asterisks.
136 StringBuilder letters;
137 letters.append(symbols[numberShadow % symbolsSize]);
138 unsigned numSymbols = numberShadow / symbolsSize;
140 letters.append(symbols[numberShadow % symbolsSize]);
141 return letters.toString();
144 template <typename CharacterType>
145 static String toAlphabetic(int number, const CharacterType* alphabet, unsigned alphabetSize)
147 return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence);
150 template <typename CharacterType>
151 static String toNumeric(int number, const CharacterType* numerals, unsigned numeralsSize)
153 return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence);
156 template <typename CharacterType, size_t size>
157 static inline String toAlphabetic(int number, const CharacterType(&alphabet)[size])
159 return toAlphabetic(number, alphabet, size);
162 template <typename CharacterType, size_t size>
163 static inline String toNumeric(int number, const CharacterType(&alphabet)[size])
165 return toNumeric(number, alphabet, size);
168 template <typename CharacterType, size_t size>
169 static inline String toSymbolic(int number, const CharacterType(&alphabet)[size])
171 return toSymbolic(number, alphabet, size);
174 static int toHebrewUnder1000(int number, UChar letters[5])
176 // FIXME: CSS3 mentions various refinements not implemented here.
177 // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
178 ASSERT(number >= 0 && number < 1000);
180 int fourHundreds = number / 400;
181 for (int i = 0; i < fourHundreds; i++)
182 letters[length++] = 1511 + 3;
185 letters[length++] = 1511 + (number / 100) - 1;
187 if (number == 15 || number == 16) {
188 letters[length++] = 1487 + 9;
189 letters[length++] = 1487 + number - 9;
191 if (int tens = number / 10) {
192 static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
193 letters[length++] = hebrewTens[tens - 1];
195 if (int ones = number % 10)
196 letters[length++] = 1487 + ones;
202 static String toHebrew(int number)
204 // FIXME: CSS3 mentions ways to make this work for much larger numbers.
205 ASSERT(number >= 0 && number <= 999999);
208 static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
209 return String(hebrewZero, 3);
212 const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
213 UChar letters[lettersSize];
219 length = toHebrewUnder1000(number / 1000, letters);
220 letters[length++] = '\'';
221 number = number % 1000;
223 length += toHebrewUnder1000(number, letters + length);
225 ASSERT(length <= lettersSize);
226 return String(letters, length);
229 static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
231 ASSERT(number >= 0 && number < 10000);
234 int lowerOffset = upper ? 0 : 0x0030;
236 if (int thousands = number / 1000) {
237 if (thousands == 7) {
238 letters[length++] = 0x0552 + lowerOffset;
240 letters[length++] = 0x0302;
242 letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
244 letters[length++] = 0x0302;
248 if (int hundreds = (number / 100) % 10) {
249 letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
251 letters[length++] = 0x0302;
254 if (int tens = (number / 10) % 10) {
255 letters[length++] = (0x053A - 1 + lowerOffset) + tens;
257 letters[length++] = 0x0302;
260 if (int ones = number % 10) {
261 letters[length++] = (0x531 - 1 + lowerOffset) + ones;
263 letters[length++] = 0x0302;
269 static String toArmenian(int number, bool upper)
271 ASSERT(number >= 1 && number <= 99999999);
273 const int lettersSize = 18; // twice what toArmenianUnder10000 needs
274 UChar letters[lettersSize];
276 int length = toArmenianUnder10000(number / 10000, upper, true, letters);
277 length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
279 ASSERT(length <= lettersSize);
280 return String(letters, length);
283 static String toGeorgian(int number)
285 ASSERT(number >= 1 && number <= 19999);
287 const int lettersSize = 5;
288 UChar letters[lettersSize];
293 letters[length++] = 0x10F5;
295 if (int thousands = (number / 1000) % 10) {
296 static const UChar georgianThousands[9] = {
297 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
299 letters[length++] = georgianThousands[thousands - 1];
302 if (int hundreds = (number / 100) % 10) {
303 static const UChar georgianHundreds[9] = {
304 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
306 letters[length++] = georgianHundreds[hundreds - 1];
309 if (int tens = (number / 10) % 10) {
310 static const UChar georgianTens[9] = {
311 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
313 letters[length++] = georgianTens[tens - 1];
316 if (int ones = number % 10) {
317 static const UChar georgianOnes[9] = {
318 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
320 letters[length++] = georgianOnes[ones - 1];
323 ASSERT(length <= lettersSize);
324 return String(letters, length);
327 // The table uses the order from the CSS3 specification:
328 // first 3 group markers, then 3 digit markers, then ten digits.
329 static String toCJKIdeographic(int number, const UChar table[16])
333 enum AbstractCJKChar {
335 secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
336 secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
337 digit0, digit1, digit2, digit3, digit4,
338 digit5, digit6, digit7, digit8, digit9
342 return String(&table[digit0 - 1], 1);
344 const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker
345 const int bufferLength = 4 * groupLength;
346 AbstractCJKChar buffer[bufferLength] = { noChar };
348 for (int i = 0; i < 4; ++i) {
349 int groupValue = number % 10000;
352 // Process least-significant group first, but put it in the buffer last.
353 AbstractCJKChar* group = &buffer[(3 - i) * groupLength];
356 group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i);
358 // Put in the four digits and digit markers for any non-zero digits.
359 group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10));
360 if (number != 0 || groupValue > 9) {
361 int digitValue = ((groupValue / 10) % 10);
362 group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue);
364 group[5] = secondDigitMarker;
366 if (number != 0 || groupValue > 99) {
367 int digitValue = ((groupValue / 100) % 10);
368 group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue);
370 group[3] = thirdDigitMarker;
372 if (number != 0 || groupValue > 999) {
373 int digitValue = groupValue / 1000;
374 group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue);
376 group[1] = fourthDigitMarker;
379 // Remove the tens digit, but leave the marker, for any group that has
380 // a value of less than 20.
381 if (groupValue < 20) {
382 ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1);
390 // Convert into characters, omitting consecutive runs of digit0 and
391 // any trailing digit0.
393 UChar characters[bufferLength];
394 AbstractCJKChar last = noChar;
395 for (int i = 0; i < bufferLength; ++i) {
396 AbstractCJKChar a = buffer[i];
398 if (a != digit0 || last != digit0)
399 characters[length++] = table[a - 1];
406 return String(characters, length);
409 static EListStyleType effectiveListMarkerType(EListStyleType type, int value)
411 // Note, the following switch statement has been explicitly grouped
412 // by list-style-type ordinal range.
416 case BinaryListStyle:
419 case DecimalLeadingZero:
420 case DecimalListStyle:
428 case LowerHexadecimal:
440 case UpperHexadecimal:
442 return type; // Can represent all ordinals.
444 return (value < 1 || value > 99999999) ? DecimalListStyle : type;
446 return (value < 0) ? DecimalListStyle : type;
448 return (value < 1 || value > 19999) ? DecimalListStyle : type;
450 return (value < 0 || value > 999999) ? DecimalListStyle : type;
453 return (value < 1 || value > 3999) ? DecimalListStyle : type;
458 case CjkEarthlyBranch:
459 case CjkHeavenlyStem:
461 case EthiopicAbegede:
462 case EthiopicAbegedeAmEt:
463 case EthiopicAbegedeGez:
464 case EthiopicAbegedeTiEr:
465 case EthiopicAbegedeTiEt:
466 case EthiopicHalehameAaEr:
467 case EthiopicHalehameAaEt:
468 case EthiopicHalehameAmEt:
469 case EthiopicHalehameGez:
470 case EthiopicHalehameOmEt:
471 case EthiopicHalehameSidEt:
472 case EthiopicHalehameSoEt:
473 case EthiopicHalehameTiEr:
474 case EthiopicHalehameTiEt:
475 case EthiopicHalehameTig:
478 case HangulConsonant:
493 case TigrinyaErAbegede:
495 case TigrinyaEtAbegede:
501 return (value < 1) ? DecimalListStyle : type;
504 ASSERT_NOT_REACHED();
508 static UChar listMarkerSuffix(EListStyleType type, int value)
510 // If the list-style-type cannot represent |value| because it's outside its
511 // ordinal range then we fall back to some list style that can represent |value|.
512 EListStyleType effectiveType = effectiveListMarkerType(type, value);
514 // Note, the following switch statement has been explicitly
515 // grouped by list-style-type suffix.
516 switch (effectiveType) {
528 case EthiopicAbegede:
529 case EthiopicAbegedeAmEt:
530 case EthiopicAbegedeGez:
531 case EthiopicAbegedeTiEr:
532 case EthiopicAbegedeTiEt:
533 case EthiopicHalehameAaEr:
534 case EthiopicHalehameAaEt:
535 case EthiopicHalehameAmEt:
536 case EthiopicHalehameGez:
537 case EthiopicHalehameOmEt:
538 case EthiopicHalehameSidEt:
539 case EthiopicHalehameSoEt:
540 case EthiopicHalehameTiEr:
541 case EthiopicHalehameTiEt:
542 case EthiopicHalehameTig:
548 case TigrinyaErAbegede:
550 case TigrinyaEtAbegede:
551 return ethiopicPrefaceColon;
555 case BinaryListStyle:
558 case CjkEarthlyBranch:
559 case CjkHeavenlyStem:
560 case DecimalLeadingZero:
561 case DecimalListStyle:
567 case HangulConsonant:
579 case LowerHexadecimal:
595 case UpperHexadecimal:
603 ASSERT_NOT_REACHED();
607 String listMarkerText(EListStyleType type, int value)
609 // If the list-style-type, say hebrew, cannot represent |value| because it's outside
610 // its ordinal range then we fallback to some list style that can represent |value|.
611 switch (effectiveListMarkerType(type, value)) {
616 static const LChar asterisksSymbols[1] = {
619 return toSymbolic(value, asterisksSymbols);
621 // We use the same characters for text security.
622 // See RenderText::setInternalString.
624 return String(&whiteBullet, 1);
626 return String(&bullet, 1);
628 static const UChar footnotesSymbols[4] = {
629 0x002A, 0x2051, 0x2020, 0x2021
631 return toSymbolic(value, footnotesSymbols);
634 // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
635 // instead, but I think this looks better.
636 return String(&blackSquare, 1);
638 case DecimalListStyle:
639 return String::number(value);
640 case DecimalLeadingZero:
641 if (value < -9 || value > 9)
642 return String::number(value);
644 return "-0" + String::number(-value); // -01 to -09
645 return "0" + String::number(value); // 00 to 09
648 static const UChar arabicIndicNumerals[10] = {
649 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
651 return toNumeric(value, arabicIndicNumerals);
653 case BinaryListStyle: {
654 static const LChar binaryNumerals[2] = {
657 return toNumeric(value, binaryNumerals);
660 static const UChar bengaliNumerals[10] = {
661 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
663 return toNumeric(value, bengaliNumerals);
667 static const UChar khmerNumerals[10] = {
668 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9
670 return toNumeric(value, khmerNumerals);
673 static const UChar devanagariNumerals[10] = {
674 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F
676 return toNumeric(value, devanagariNumerals);
679 static const UChar gujaratiNumerals[10] = {
680 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF
682 return toNumeric(value, gujaratiNumerals);
685 static const UChar gurmukhiNumerals[10] = {
686 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F
688 return toNumeric(value, gurmukhiNumerals);
691 static const UChar kannadaNumerals[10] = {
692 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF
694 return toNumeric(value, kannadaNumerals);
696 case LowerHexadecimal: {
697 static const LChar lowerHexadecimalNumerals[16] = {
698 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
700 return toNumeric(value, lowerHexadecimalNumerals);
703 static const UChar laoNumerals[10] = {
704 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9
706 return toNumeric(value, laoNumerals);
709 static const UChar malayalamNumerals[10] = {
710 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F
712 return toNumeric(value, malayalamNumerals);
715 static const UChar mongolianNumerals[10] = {
716 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819
718 return toNumeric(value, mongolianNumerals);
721 static const UChar myanmarNumerals[10] = {
722 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049
724 return toNumeric(value, myanmarNumerals);
727 static const LChar octalNumerals[8] = {
728 '0', '1', '2', '3', '4', '5', '6', '7'
730 return toNumeric(value, octalNumerals);
733 static const UChar oriyaNumerals[10] = {
734 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F
736 return toNumeric(value, oriyaNumerals);
740 static const UChar urduNumerals[10] = {
741 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9
743 return toNumeric(value, urduNumerals);
746 static const UChar teluguNumerals[10] = {
747 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F
749 return toNumeric(value, teluguNumerals);
752 static const UChar tibetanNumerals[10] = {
753 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29
755 return toNumeric(value, tibetanNumerals);
758 static const UChar thaiNumerals[10] = {
759 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59
761 return toNumeric(value, thaiNumerals);
763 case UpperHexadecimal: {
764 static const LChar upperHexadecimalNumerals[16] = {
765 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
767 return toNumeric(value, upperHexadecimalNumerals);
772 static const LChar lowerLatinAlphabet[26] = {
773 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
774 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
776 return toAlphabetic(value, lowerLatinAlphabet);
780 static const LChar upperLatinAlphabet[26] = {
781 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
782 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
784 return toAlphabetic(value, upperLatinAlphabet);
787 static const UChar lowerGreekAlphabet[24] = {
788 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
789 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
790 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
792 return toAlphabetic(value, lowerGreekAlphabet);
796 // FIXME: This table comes from the CSS3 draft, and is probably
797 // incorrect, given the comments in that draft.
798 static const UChar hiraganaAlphabet[48] = {
799 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
800 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
801 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
802 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
803 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
804 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
806 return toAlphabetic(value, hiraganaAlphabet);
808 case HiraganaIroha: {
809 // FIXME: This table comes from the CSS3 draft, and is probably
810 // incorrect, given the comments in that draft.
811 static const UChar hiraganaIrohaAlphabet[47] = {
812 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
813 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
814 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
815 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
816 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
817 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
819 return toAlphabetic(value, hiraganaIrohaAlphabet);
822 // FIXME: This table comes from the CSS3 draft, and is probably
823 // incorrect, given the comments in that draft.
824 static const UChar katakanaAlphabet[48] = {
825 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
826 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
827 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
828 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
829 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
830 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
832 return toAlphabetic(value, katakanaAlphabet);
834 case KatakanaIroha: {
835 // FIXME: This table comes from the CSS3 draft, and is probably
836 // incorrect, given the comments in that draft.
837 static const UChar katakanaIrohaAlphabet[47] = {
838 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
839 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
840 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
841 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
842 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
843 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
845 return toAlphabetic(value, katakanaIrohaAlphabet);
849 case EthiopicHalehameAaEt:
850 case EthiopicHalehameAaEr: {
851 static const UChar ethiopicHalehameAaErAlphabet[18] = {
852 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290,
853 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348
855 return toAlphabetic(value, ethiopicHalehameAaErAlphabet);
858 case EthiopicHalehameAmEt: {
859 static const UChar ethiopicHalehameAmEtAlphabet[33] = {
860 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
861 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8,
862 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320,
863 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
865 return toAlphabetic(value, ethiopicHalehameAmEtAlphabet);
868 case EthiopicAbegedeAmEt: {
869 static const UChar ethiopicAbegedeAmEtAlphabet[33] = {
870 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
871 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
872 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238,
873 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
875 return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet);
877 case CjkEarthlyBranch: {
878 static const UChar cjkEarthlyBranchAlphabet[12] = {
879 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533,
880 0x9149, 0x620C, 0x4EA5
882 return toAlphabetic(value, cjkEarthlyBranchAlphabet);
884 case CjkHeavenlyStem: {
885 static const UChar cjkHeavenlyStemAlphabet[10] = {
886 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC,
889 return toAlphabetic(value, cjkHeavenlyStemAlphabet);
892 case EthiopicHalehameGez: {
893 static const UChar ethiopicHalehameGezAlphabet[26] = {
894 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260,
895 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
896 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
898 return toAlphabetic(value, ethiopicHalehameGezAlphabet);
900 case EthiopicAbegede:
901 case EthiopicAbegedeGez: {
902 static const UChar ethiopicAbegedeGezAlphabet[26] = {
903 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320,
904 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338,
905 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350
907 return toAlphabetic(value, ethiopicAbegedeGezAlphabet);
909 case HangulConsonant: {
910 static const UChar hangulConsonantAlphabet[14] = {
911 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148,
912 0x314A, 0x314B, 0x314C, 0x314D, 0x314E
914 return toAlphabetic(value, hangulConsonantAlphabet);
917 static const UChar hangulAlphabet[14] = {
918 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790,
919 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558
921 return toAlphabetic(value, hangulAlphabet);
924 case EthiopicHalehameOmEt: {
925 static const UChar ethiopicHalehameOmEtAlphabet[25] = {
926 0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270,
927 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8,
928 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
930 return toAlphabetic(value, ethiopicHalehameOmEtAlphabet);
933 case EthiopicHalehameSidEt: {
934 static const UChar ethiopicHalehameSidEtAlphabet[26] = {
935 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
936 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0,
937 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
939 return toAlphabetic(value, ethiopicHalehameSidEtAlphabet);
942 case EthiopicHalehameSoEt: {
943 static const UChar ethiopicHalehameSoEtAlphabet[22] = {
944 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
945 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0,
946 0x1300, 0x1308, 0x1338, 0x1348
948 return toAlphabetic(value, ethiopicHalehameSoEtAlphabet);
951 case EthiopicHalehameTig: {
952 static const UChar ethiopicHalehameTigAlphabet[27] = {
953 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
954 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
955 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350
957 return toAlphabetic(value, ethiopicHalehameTigAlphabet);
960 case EthiopicHalehameTiEr: {
961 static const UChar ethiopicHalehameTiErAlphabet[31] = {
962 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250,
963 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8,
964 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328,
965 0x1330, 0x1338, 0x1348, 0x1350
967 return toAlphabetic(value, ethiopicHalehameTiErAlphabet);
969 case TigrinyaErAbegede:
970 case EthiopicAbegedeTiEr: {
971 static const UChar ethiopicAbegedeTiErAlphabet[31] = {
972 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
973 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
974 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238,
975 0x1270, 0x1278, 0x1330, 0x1350
977 return toAlphabetic(value, ethiopicAbegedeTiErAlphabet);
980 case EthiopicHalehameTiEt: {
981 static const UChar ethiopicHalehameTiEtAlphabet[34] = {
982 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
983 0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8,
984 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308,
985 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
987 return toAlphabetic(value, ethiopicHalehameTiEtAlphabet);
989 case TigrinyaEtAbegede:
990 case EthiopicAbegedeTiEt: {
991 static const UChar ethiopicAbegedeTiEtAlphabet[34] = {
992 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
993 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
994 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230,
995 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
997 return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet);
1000 static const UChar upperGreekAlphabet[24] = {
1001 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399,
1002 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3,
1003 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9
1005 return toAlphabetic(value, upperGreekAlphabet);
1007 case LowerNorwegian: {
1008 static const LChar lowerNorwegianAlphabet[29] = {
1009 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
1010 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
1011 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xE6,
1014 return toAlphabetic(value, lowerNorwegianAlphabet);
1016 case UpperNorwegian: {
1017 static const LChar upperNorwegianAlphabet[29] = {
1018 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
1019 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
1020 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xC6,
1023 return toAlphabetic(value, upperNorwegianAlphabet);
1025 case CJKIdeographic: {
1026 static const UChar traditionalChineseInformalTable[16] = {
1027 0x842C, 0x5104, 0x5146,
1028 0x5341, 0x767E, 0x5343,
1029 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
1030 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
1032 return toCJKIdeographic(value, traditionalChineseInformalTable);
1036 return toRoman(value, false);
1038 return toRoman(value, true);
1042 // CSS3 says "armenian" means "lower-armenian".
1043 // But the CSS2.1 test suite contains uppercase test results for "armenian",
1044 // so we'll match the test suite.
1045 return toArmenian(value, true);
1047 return toArmenian(value, false);
1049 return toGeorgian(value);
1051 return toHebrew(value);
1054 ASSERT_NOT_REACHED();
1058 RenderListMarker::RenderListMarker(RenderListItem* item)
1062 // init RenderObject attributes
1063 setInline(true); // our object is Inline
1064 setReplaced(true); // pretend to be replaced
1067 RenderListMarker::~RenderListMarker()
1070 m_image->removeClient(this);
1073 RenderListMarker* RenderListMarker::createAnonymous(RenderListItem* item)
1075 Document& document = item->document();
1076 RenderListMarker* renderer = new RenderListMarker(item);
1077 renderer->setDocumentForAnonymous(&document);
1081 void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
1083 if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType()))
1084 setNeedsLayoutAndPrefWidthsRecalc();
1086 RenderBox::styleWillChange(diff, newStyle);
1089 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
1091 RenderBox::styleDidChange(diff, oldStyle);
1093 if (m_image != style()->listStyleImage()) {
1095 m_image->removeClient(this);
1096 m_image = style()->listStyleImage();
1098 m_image->addClient(this);
1102 InlineBox* RenderListMarker::createInlineBox()
1104 InlineBox* result = RenderBox::createInlineBox();
1105 result->setIsText(isText());
1109 bool RenderListMarker::isImage() const
1111 return m_image && !m_image->errorOccurred();
1114 LayoutRect RenderListMarker::localSelectionRect()
1116 InlineBox* box = inlineBoxWrapper();
1118 return LayoutRect(LayoutPoint(), size());
1119 RootInlineBox* root = inlineBoxWrapper()->root();
1120 LayoutUnit newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? inlineBoxWrapper()->logicalBottom() - root->selectionBottom() : root->selectionTop() - inlineBoxWrapper()->logicalTop();
1121 if (root->block()->style()->isHorizontalWritingMode())
1122 return LayoutRect(0, newLogicalTop, width(), root->selectionHeight());
1123 return LayoutRect(newLogicalTop, 0, root->selectionHeight(), height());
1126 void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1128 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
1130 if (paintInfo.phase != PaintPhaseForeground)
1133 if (style()->visibility() != VISIBLE)
1136 LayoutPoint boxOrigin(paintOffset + location());
1137 LayoutRect overflowRect(visualOverflowRect());
1138 overflowRect.moveBy(boxOrigin);
1139 overflowRect.inflate(maximalOutlineSize(paintInfo.phase));
1141 if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
1144 LayoutRect box(boxOrigin, size());
1146 IntRect marker = getRelativeMarkerRect();
1147 marker.moveBy(roundedIntPoint(boxOrigin));
1149 GraphicsContext* context = paintInfo.context;
1152 context->drawImage(m_image->image(this, marker.size()).get(), marker);
1153 if (selectionState() != SelectionNone) {
1154 LayoutRect selRect = localSelectionRect();
1155 selRect.moveBy(boxOrigin);
1156 context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor());
1161 if (selectionState() != SelectionNone) {
1162 LayoutRect selRect = localSelectionRect();
1163 selRect.moveBy(boxOrigin);
1164 context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor());
1167 const Color color(resolveColor(CSSPropertyColor));
1168 context->setStrokeColor(color);
1169 context->setStrokeStyle(SolidStroke);
1170 context->setStrokeThickness(1.0f);
1171 context->setFillColor(color);
1173 EListStyleType type = style()->listStyleType();
1176 context->fillEllipse(marker);
1179 context->strokeEllipse(marker);
1182 context->fillRect(marker);
1188 case AmharicAbegede:
1191 case BinaryListStyle:
1194 case CJKIdeographic:
1195 case CjkEarthlyBranch:
1196 case CjkHeavenlyStem:
1197 case DecimalLeadingZero:
1198 case DecimalListStyle:
1201 case EthiopicAbegede:
1202 case EthiopicAbegedeAmEt:
1203 case EthiopicAbegedeGez:
1204 case EthiopicAbegedeTiEr:
1205 case EthiopicAbegedeTiEt:
1206 case EthiopicHalehameAaEr:
1207 case EthiopicHalehameAaEt:
1208 case EthiopicHalehameAmEt:
1209 case EthiopicHalehameGez:
1210 case EthiopicHalehameOmEt:
1211 case EthiopicHalehameSidEt:
1212 case EthiopicHalehameSoEt:
1213 case EthiopicHalehameTiEr:
1214 case EthiopicHalehameTiEt:
1215 case EthiopicHalehameTig:
1220 case HangulConsonant:
1232 case LowerHexadecimal:
1234 case LowerNorwegian:
1250 case TigrinyaErAbegede:
1252 case TigrinyaEtAbegede:
1256 case UpperHexadecimal:
1258 case UpperNorwegian:
1265 if (m_text.isEmpty())
1268 const Font& font = style()->font();
1269 TextRun textRun = RenderBlockFlow::constructTextRun(this, font, m_text, style());
1271 GraphicsContextStateSaver stateSaver(*context, false);
1272 if (!style()->isHorizontalWritingMode()) {
1273 marker.moveBy(roundedIntPoint(-boxOrigin));
1274 marker = marker.transposedRect();
1275 marker.moveBy(IntPoint(roundToInt(box.x()), roundToInt(box.y() - logicalHeight())));
1277 context->translate(marker.x(), marker.maxY());
1278 context->rotate(static_cast<float>(deg2rad(90.)));
1279 context->translate(-marker.x(), -marker.maxY());
1282 TextRunPaintInfo textRunPaintInfo(textRun);
1283 textRunPaintInfo.bounds = marker;
1284 IntPoint textOrigin = IntPoint(marker.x(), marker.y() + style()->fontMetrics().ascent());
1286 if (type == Asterisks || type == Footnotes) {
1287 context->drawText(font, textRunPaintInfo, textOrigin);
1290 // Text is not arbitrary. We can judge whether it's RTL from the first character,
1291 // and we only need to handle the direction RightToLeft for now.
1292 bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
1293 StringBuilder reversedText;
1294 if (textNeedsReversing) {
1295 int length = m_text.length();
1296 reversedText.reserveCapacity(length);
1297 for (int i = length - 1; i >= 0; --i)
1298 reversedText.append(m_text[i]);
1299 ASSERT(reversedText.length() == reversedText.capacity());
1300 textRun.setText(reversedText.toString());
1303 const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1304 UChar suffixStr[2] = {
1305 style()->isLeftToRightDirection() ? suffix : ' ',
1306 style()->isLeftToRightDirection() ? ' ' : suffix
1308 TextRun suffixRun = RenderBlockFlow::constructTextRun(this, font, suffixStr, 2, style(), style()->direction());
1309 TextRunPaintInfo suffixRunInfo(suffixRun);
1310 suffixRunInfo.bounds = marker;
1312 if (style()->isLeftToRightDirection()) {
1313 context->drawText(font, textRunPaintInfo, textOrigin);
1314 context->drawText(font, suffixRunInfo, textOrigin + IntSize(font.width(textRun), 0));
1316 context->drawText(font, suffixRunInfo, textOrigin);
1317 context->drawText(font, textRunPaintInfo, textOrigin + IntSize(font.width(suffixRun), 0));
1322 void RenderListMarker::layout()
1324 ASSERT(needsLayout());
1326 LayoutRectRecorder recorder(*this);
1328 updateMarginsAndContent();
1329 setWidth(m_image->imageSize(this, style()->effectiveZoom()).width());
1330 setHeight(m_image->imageSize(this, style()->effectiveZoom()).height());
1332 setLogicalWidth(minPreferredLogicalWidth());
1333 setLogicalHeight(style()->fontMetrics().height());
1339 Length startMargin = style()->marginStart();
1340 Length endMargin = style()->marginEnd();
1341 if (startMargin.isFixed())
1342 setMarginStart(startMargin.value());
1343 if (endMargin.isFixed())
1344 setMarginEnd(endMargin.value());
1349 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
1351 // A list marker can't have a background or border image, so no need to call the base class method.
1352 if (o != m_image->data())
1355 if (width() != m_image->imageSize(this, style()->effectiveZoom()).width() || height() != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred())
1356 setNeedsLayoutAndPrefWidthsRecalc();
1361 void RenderListMarker::updateMarginsAndContent()
1367 void RenderListMarker::updateContent()
1369 // FIXME: This if-statement is just a performance optimization, but it's messy to use the preferredLogicalWidths dirty bit for this.
1370 // It's unclear if this is a premature optimization.
1371 if (!preferredLogicalWidthsDirty())
1377 // FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful
1378 // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box.
1379 int bulletWidth = style()->fontMetrics().ascent() / 2;
1380 m_image->setContainerSizeForRenderer(this, IntSize(bulletWidth, bulletWidth), style()->effectiveZoom());
1384 EListStyleType type = style()->listStyleType();
1391 m_text = listMarkerText(type, 0); // value is ignored for these types
1397 case AmharicAbegede:
1400 case BinaryListStyle:
1403 case CJKIdeographic:
1404 case CjkEarthlyBranch:
1405 case CjkHeavenlyStem:
1406 case DecimalLeadingZero:
1407 case DecimalListStyle:
1410 case EthiopicAbegede:
1411 case EthiopicAbegedeAmEt:
1412 case EthiopicAbegedeGez:
1413 case EthiopicAbegedeTiEr:
1414 case EthiopicAbegedeTiEt:
1415 case EthiopicHalehameAaEr:
1416 case EthiopicHalehameAaEt:
1417 case EthiopicHalehameAmEt:
1418 case EthiopicHalehameGez:
1419 case EthiopicHalehameOmEt:
1420 case EthiopicHalehameSidEt:
1421 case EthiopicHalehameSoEt:
1422 case EthiopicHalehameTiEr:
1423 case EthiopicHalehameTiEt:
1424 case EthiopicHalehameTig:
1429 case HangulConsonant:
1441 case LowerHexadecimal:
1443 case LowerNorwegian:
1459 case TigrinyaErAbegede:
1461 case TigrinyaEtAbegede:
1465 case UpperHexadecimal:
1467 case UpperNorwegian:
1470 m_text = listMarkerText(type, m_listItem->value());
1475 void RenderListMarker::computePreferredLogicalWidths()
1477 ASSERT(preferredLogicalWidthsDirty());
1481 LayoutSize imageSize = m_image->imageSize(this, style()->effectiveZoom());
1482 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style()->isHorizontalWritingMode() ? imageSize.width() : imageSize.height();
1483 clearPreferredLogicalWidthsDirty();
1488 const Font& font = style()->font();
1490 LayoutUnit logicalWidth = 0;
1491 EListStyleType type = style()->listStyleType();
1497 logicalWidth = font.width(m_text); // no suffix for these types
1502 logicalWidth = (font.fontMetrics().ascent() * 2 / 3 + 1) / 2 + 2;
1506 case AmharicAbegede:
1509 case BinaryListStyle:
1512 case CJKIdeographic:
1513 case CjkEarthlyBranch:
1514 case CjkHeavenlyStem:
1515 case DecimalLeadingZero:
1516 case DecimalListStyle:
1519 case EthiopicAbegede:
1520 case EthiopicAbegedeAmEt:
1521 case EthiopicAbegedeGez:
1522 case EthiopicAbegedeTiEr:
1523 case EthiopicAbegedeTiEt:
1524 case EthiopicHalehameAaEr:
1525 case EthiopicHalehameAaEt:
1526 case EthiopicHalehameAmEt:
1527 case EthiopicHalehameGez:
1528 case EthiopicHalehameOmEt:
1529 case EthiopicHalehameSidEt:
1530 case EthiopicHalehameSoEt:
1531 case EthiopicHalehameTiEr:
1532 case EthiopicHalehameTiEt:
1533 case EthiopicHalehameTig:
1538 case HangulConsonant:
1550 case LowerHexadecimal:
1552 case LowerNorwegian:
1568 case TigrinyaErAbegede:
1570 case TigrinyaEtAbegede:
1574 case UpperHexadecimal:
1576 case UpperNorwegian:
1579 if (m_text.isEmpty())
1582 LayoutUnit itemWidth = font.width(m_text);
1583 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1584 LayoutUnit suffixSpaceWidth = font.width(RenderBlockFlow::constructTextRun(this, font, suffixSpace, 2, style(), style()->direction()));
1585 logicalWidth = itemWidth + suffixSpaceWidth;
1590 m_minPreferredLogicalWidth = logicalWidth;
1591 m_maxPreferredLogicalWidth = logicalWidth;
1593 clearPreferredLogicalWidthsDirty();
1598 void RenderListMarker::updateMargins()
1600 const FontMetrics& fontMetrics = style()->fontMetrics();
1602 LayoutUnit marginStart = 0;
1603 LayoutUnit marginEnd = 0;
1607 marginEnd = cMarkerPadding;
1608 else switch (style()->listStyleType()) {
1613 marginEnd = fontMetrics.ascent() - minPreferredLogicalWidth() + 1;
1619 if (style()->isLeftToRightDirection()) {
1621 marginStart = -minPreferredLogicalWidth() - cMarkerPadding;
1623 int offset = fontMetrics.ascent() * 2 / 3;
1624 switch (style()->listStyleType()) {
1628 marginStart = -offset - cMarkerPadding - 1;
1633 marginStart = m_text.isEmpty() ? LayoutUnit() : -minPreferredLogicalWidth() - offset / 2;
1636 marginEnd = -marginStart - minPreferredLogicalWidth();
1639 marginEnd = cMarkerPadding;
1641 int offset = fontMetrics.ascent() * 2 / 3;
1642 switch (style()->listStyleType()) {
1646 marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth();
1651 marginEnd = m_text.isEmpty() ? 0 : offset / 2;
1654 marginStart = -marginEnd - minPreferredLogicalWidth();
1659 style()->setMarginStart(Length(marginStart, Fixed));
1660 style()->setMarginEnd(Length(marginEnd, Fixed));
1663 LayoutUnit RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1666 return m_listItem->lineHeight(firstLine, direction, PositionOfInteriorLineBoxes);
1667 return RenderBox::lineHeight(firstLine, direction, linePositionMode);
1670 int RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1672 ASSERT(linePositionMode == PositionOnContainingLine);
1674 return m_listItem->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
1675 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1678 String RenderListMarker::suffix() const
1680 EListStyleType type = style()->listStyleType();
1681 const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1686 // If the suffix is not ' ', an extra space is needed
1688 if (style()->isLeftToRightDirection()) {
1696 return String(data, 2);
1699 bool RenderListMarker::isInside() const
1701 return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
1704 IntRect RenderListMarker::getRelativeMarkerRect()
1707 return IntRect(0, 0, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
1709 IntRect relativeRect;
1710 EListStyleType type = style()->listStyleType();
1714 const Font& font = style()->font();
1715 relativeRect = IntRect(0, 0, font.width(m_text), font.fontMetrics().height());
1721 // FIXME: Are these particular rounding rules necessary?
1722 const FontMetrics& fontMetrics = style()->fontMetrics();
1723 int ascent = fontMetrics.ascent();
1724 int bulletWidth = (ascent * 2 / 3 + 1) / 2;
1725 relativeRect = IntRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
1732 case AmharicAbegede:
1735 case BinaryListStyle:
1738 case CJKIdeographic:
1739 case CjkEarthlyBranch:
1740 case CjkHeavenlyStem:
1741 case DecimalLeadingZero:
1742 case DecimalListStyle:
1745 case EthiopicAbegede:
1746 case EthiopicAbegedeAmEt:
1747 case EthiopicAbegedeGez:
1748 case EthiopicAbegedeTiEr:
1749 case EthiopicAbegedeTiEt:
1750 case EthiopicHalehameAaEr:
1751 case EthiopicHalehameAaEt:
1752 case EthiopicHalehameAmEt:
1753 case EthiopicHalehameGez:
1754 case EthiopicHalehameOmEt:
1755 case EthiopicHalehameSidEt:
1756 case EthiopicHalehameSoEt:
1757 case EthiopicHalehameTiEr:
1758 case EthiopicHalehameTiEt:
1759 case EthiopicHalehameTig:
1764 case HangulConsonant:
1776 case LowerHexadecimal:
1778 case LowerNorwegian:
1794 case TigrinyaErAbegede:
1796 case TigrinyaEtAbegede:
1800 case UpperHexadecimal:
1802 case UpperNorwegian:
1805 if (m_text.isEmpty())
1807 const Font& font = style()->font();
1808 int itemWidth = font.width(m_text);
1809 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1810 int suffixSpaceWidth = font.width(RenderBlockFlow::constructTextRun(this, font, suffixSpace, 2, style(), style()->direction()));
1811 relativeRect = IntRect(0, 0, itemWidth + suffixSpaceWidth, font.fontMetrics().height());
1814 if (!style()->isHorizontalWritingMode()) {
1815 relativeRect = relativeRect.transposedRect();
1816 relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
1819 return relativeRect;
1822 void RenderListMarker::setSelectionState(SelectionState state)
1824 // The selection state for our containing block hierarchy is updated by the base class call.
1825 RenderBox::setSelectionState(state);
1827 if (inlineBoxWrapper() && canUpdateSelectionOnRootLineBoxes())
1828 if (RootInlineBox* root = inlineBoxWrapper()->root())
1829 root->setHasSelectedChildren(state != SelectionNone);
1832 LayoutRect RenderListMarker::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent)
1834 ASSERT(!needsLayout());
1836 if (selectionState() == SelectionNone || !inlineBoxWrapper())
1837 return LayoutRect();
1839 RootInlineBox* root = inlineBoxWrapper()->root();
1840 LayoutRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight());
1842 if (clipToVisibleContent)
1843 computeRectForRepaint(repaintContainer, rect);
1845 rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1850 } // namespace WebCore