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 "RenderListMarker.h"
28 #include "CachedImage.h"
30 #include "GraphicsContext.h"
31 #include "RenderLayer.h"
32 #include "RenderListItem.h"
33 #include "RenderView.h"
34 #include <wtf/text/StringBuilder.h>
35 #include <wtf/unicode/CharacterNames.h>
39 using namespace Unicode;
43 const int cMarkerPadding = 7;
45 enum SequenceType { NumericSequence, AlphabeticSequence };
47 static String toRoman(int number, bool upper)
49 // FIXME: CSS3 describes how to make this work for much larger numbers,
50 // using overbars and special characters. It also specifies the characters
51 // in the range U+2160 to U+217F instead of standard ASCII ones.
52 ASSERT(number >= 1 && number <= 3999);
54 // Big enough to store largest roman number less than 3999 which
55 // is 3888 (MMMDCCCLXXXVIII)
56 const int lettersSize = 15;
57 UChar letters[lettersSize];
60 const UChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
61 const UChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
62 const UChar* digits = upper ? udigits : ldigits;
65 int num = number % 10;
67 for (int i = num % 5; i > 0; i--)
68 letters[lettersSize - ++length] = digits[d];
69 if (num >= 4 && num <= 8)
70 letters[lettersSize - ++length] = digits[d + 1];
72 letters[lettersSize - ++length] = digits[d + 2];
74 letters[lettersSize - ++length] = digits[d];
79 ASSERT(length <= lettersSize);
80 return String(&letters[lettersSize - length], length);
83 static inline String toAlphabeticOrNumeric(int number, const UChar* sequence, unsigned sequenceSize, SequenceType type)
85 ASSERT(sequenceSize >= 2);
87 const int lettersSize = sizeof(number) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
89 UChar letters[lettersSize];
91 bool isNegativeNumber = false;
92 unsigned numberShadow = number;
93 if (type == AlphabeticSequence) {
96 } else if (number < 0) {
97 numberShadow = -number;
98 isNegativeNumber = true;
100 letters[lettersSize - 1] = sequence[numberShadow % sequenceSize];
103 if (type == AlphabeticSequence) {
104 while ((numberShadow /= sequenceSize) > 0) {
106 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
109 while ((numberShadow /= sequenceSize) > 0)
110 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
112 if (isNegativeNumber)
113 letters[lettersSize - ++length] = hyphenMinus;
115 ASSERT(length <= lettersSize);
116 return String(&letters[lettersSize - length], length);
119 static String toSymbolic(int number, const UChar* symbols, unsigned symbolsSize)
122 ASSERT(symbolsSize >= 1);
123 unsigned numberShadow = number;
126 // The asterisks list-style-type is the worst case; we show |numberShadow| asterisks.
127 StringBuilder letters;
128 letters.append(symbols[numberShadow % symbolsSize]);
129 unsigned numSymbols = numberShadow / symbolsSize;
131 letters.append(symbols[numberShadow % symbolsSize]);
132 return letters.toString();
135 static String toAlphabetic(int number, const UChar* alphabet, unsigned alphabetSize)
137 return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence);
140 static String toNumeric(int number, const UChar* numerals, unsigned numeralsSize)
142 return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence);
145 template <size_t size> static inline String toAlphabetic(int number, const UChar(&alphabet)[size])
147 return toAlphabetic(number, alphabet, size);
150 template <size_t size> static inline String toNumeric(int number, const UChar(&alphabet)[size])
152 return toNumeric(number, alphabet, size);
155 template <size_t size> static inline String toSymbolic(int number, const UChar(&alphabet)[size])
157 return toSymbolic(number, alphabet, size);
160 static int toHebrewUnder1000(int number, UChar letters[5])
162 // FIXME: CSS3 mentions various refinements not implemented here.
163 // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
164 ASSERT(number >= 0 && number < 1000);
166 int fourHundreds = number / 400;
167 for (int i = 0; i < fourHundreds; i++)
168 letters[length++] = 1511 + 3;
171 letters[length++] = 1511 + (number / 100) - 1;
173 if (number == 15 || number == 16) {
174 letters[length++] = 1487 + 9;
175 letters[length++] = 1487 + number - 9;
177 if (int tens = number / 10) {
178 static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
179 letters[length++] = hebrewTens[tens - 1];
181 if (int ones = number % 10)
182 letters[length++] = 1487 + ones;
188 static String toHebrew(int number)
190 // FIXME: CSS3 mentions ways to make this work for much larger numbers.
191 ASSERT(number >= 0 && number <= 999999);
194 static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
195 return String(hebrewZero, 3);
198 const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
199 UChar letters[lettersSize];
205 length = toHebrewUnder1000(number / 1000, letters);
206 letters[length++] = '\'';
207 number = number % 1000;
209 length += toHebrewUnder1000(number, letters + length);
211 ASSERT(length <= lettersSize);
212 return String(letters, length);
215 static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
217 ASSERT(number >= 0 && number < 10000);
220 int lowerOffset = upper ? 0 : 0x0030;
222 if (int thousands = number / 1000) {
223 if (thousands == 7) {
224 letters[length++] = 0x0548 + lowerOffset;
225 letters[length++] = 0x0552 + lowerOffset;
227 letters[length++] = 0x0302;
229 letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
231 letters[length++] = 0x0302;
235 if (int hundreds = (number / 100) % 10) {
236 letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
238 letters[length++] = 0x0302;
241 if (int tens = (number / 10) % 10) {
242 letters[length++] = (0x053A - 1 + lowerOffset) + tens;
244 letters[length++] = 0x0302;
247 if (int ones = number % 10) {
248 letters[length++] = (0x531 - 1 + lowerOffset) + ones;
250 letters[length++] = 0x0302;
256 static String toArmenian(int number, bool upper)
258 ASSERT(number >= 1 && number <= 99999999);
260 const int lettersSize = 18; // twice what toArmenianUnder10000 needs
261 UChar letters[lettersSize];
263 int length = toArmenianUnder10000(number / 10000, upper, true, letters);
264 length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
266 ASSERT(length <= lettersSize);
267 return String(letters, length);
270 static String toGeorgian(int number)
272 ASSERT(number >= 1 && number <= 19999);
274 const int lettersSize = 5;
275 UChar letters[lettersSize];
280 letters[length++] = 0x10F5;
282 if (int thousands = (number / 1000) % 10) {
283 static const UChar georgianThousands[9] = {
284 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
286 letters[length++] = georgianThousands[thousands - 1];
289 if (int hundreds = (number / 100) % 10) {
290 static const UChar georgianHundreds[9] = {
291 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
293 letters[length++] = georgianHundreds[hundreds - 1];
296 if (int tens = (number / 10) % 10) {
297 static const UChar georgianTens[9] = {
298 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
300 letters[length++] = georgianTens[tens - 1];
303 if (int ones = number % 10) {
304 static const UChar georgianOnes[9] = {
305 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
307 letters[length++] = georgianOnes[ones - 1];
310 ASSERT(length <= lettersSize);
311 return String(letters, length);
314 // The table uses the order from the CSS3 specification:
315 // first 3 group markers, then 3 digit markers, then ten digits.
316 static String toCJKIdeographic(int number, const UChar table[16])
320 enum AbstractCJKChar {
322 secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
323 secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
324 digit0, digit1, digit2, digit3, digit4,
325 digit5, digit6, digit7, digit8, digit9
329 return String(&table[digit0 - 1], 1);
331 const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker
332 const int bufferLength = 4 * groupLength;
333 AbstractCJKChar buffer[bufferLength] = { noChar };
335 for (int i = 0; i < 4; ++i) {
336 int groupValue = number % 10000;
339 // Process least-significant group first, but put it in the buffer last.
340 AbstractCJKChar* group = &buffer[(3 - i) * groupLength];
343 group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i);
345 // Put in the four digits and digit markers for any non-zero digits.
346 group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10));
347 if (number != 0 || groupValue > 9) {
348 int digitValue = ((groupValue / 10) % 10);
349 group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue);
351 group[5] = secondDigitMarker;
353 if (number != 0 || groupValue > 99) {
354 int digitValue = ((groupValue / 100) % 10);
355 group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue);
357 group[3] = thirdDigitMarker;
359 if (number != 0 || groupValue > 999) {
360 int digitValue = groupValue / 1000;
361 group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue);
363 group[1] = fourthDigitMarker;
366 // Remove the tens digit, but leave the marker, for any group that has
367 // a value of less than 20.
368 if (groupValue < 20) {
369 ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1);
377 // Convert into characters, omitting consecutive runs of digit0 and
378 // any trailing digit0.
380 UChar characters[bufferLength];
381 AbstractCJKChar last = noChar;
382 for (int i = 0; i < bufferLength; ++i) {
383 AbstractCJKChar a = buffer[i];
385 if (a != digit0 || last != digit0)
386 characters[length++] = table[a - 1];
393 return String(characters, length);
396 static EListStyleType effectiveListMarkerType(EListStyleType type, int value)
398 // Note, the following switch statement has been explicitly grouped
399 // by list-style-type ordinal range.
403 case BinaryListStyle:
406 case DecimalLeadingZero:
407 case DecimalListStyle:
415 case LowerHexadecimal:
427 case UpperHexadecimal:
429 return type; // Can represent all ordinals.
431 return (value < 1 || value > 99999999) ? DecimalListStyle : type;
433 return (value < 0) ? DecimalListStyle : type;
435 return (value < 1 || value > 19999) ? DecimalListStyle : type;
437 return (value < 0 || value > 999999) ? DecimalListStyle : type;
440 return (value < 1 || value > 3999) ? DecimalListStyle : type;
445 case CjkEarthlyBranch:
446 case CjkHeavenlyStem:
448 case EthiopicAbegede:
449 case EthiopicAbegedeAmEt:
450 case EthiopicAbegedeGez:
451 case EthiopicAbegedeTiEr:
452 case EthiopicAbegedeTiEt:
453 case EthiopicHalehameAaEr:
454 case EthiopicHalehameAaEt:
455 case EthiopicHalehameAmEt:
456 case EthiopicHalehameGez:
457 case EthiopicHalehameOmEt:
458 case EthiopicHalehameSidEt:
459 case EthiopicHalehameSoEt:
460 case EthiopicHalehameTiEr:
461 case EthiopicHalehameTiEt:
462 case EthiopicHalehameTig:
465 case HangulConsonant:
480 case TigrinyaErAbegede:
482 case TigrinyaEtAbegede:
488 return (value < 1) ? DecimalListStyle : type;
491 ASSERT_NOT_REACHED();
495 static UChar listMarkerSuffix(EListStyleType type, int value)
497 // If the list-style-type cannot represent |value| because it's outside its
498 // ordinal range then we fall back to some list style that can represent |value|.
499 EListStyleType effectiveType = effectiveListMarkerType(type, value);
501 // Note, the following switch statement has been explicitly
502 // grouped by list-style-type suffix.
503 switch (effectiveType) {
515 case EthiopicAbegede:
516 case EthiopicAbegedeAmEt:
517 case EthiopicAbegedeGez:
518 case EthiopicAbegedeTiEr:
519 case EthiopicAbegedeTiEt:
520 case EthiopicHalehameAaEr:
521 case EthiopicHalehameAaEt:
522 case EthiopicHalehameAmEt:
523 case EthiopicHalehameGez:
524 case EthiopicHalehameOmEt:
525 case EthiopicHalehameSidEt:
526 case EthiopicHalehameSoEt:
527 case EthiopicHalehameTiEr:
528 case EthiopicHalehameTiEt:
529 case EthiopicHalehameTig:
535 case TigrinyaErAbegede:
537 case TigrinyaEtAbegede:
538 return ethiopicPrefaceColon;
542 case BinaryListStyle:
545 case CjkEarthlyBranch:
546 case CjkHeavenlyStem:
547 case DecimalLeadingZero:
548 case DecimalListStyle:
554 case HangulConsonant:
566 case LowerHexadecimal:
582 case UpperHexadecimal:
590 ASSERT_NOT_REACHED();
594 String listMarkerText(EListStyleType type, int value)
596 // If the list-style-type, say hebrew, cannot represent |value| because it's outside
597 // its ordinal range then we fallback to some list style that can represent |value|.
598 switch (effectiveListMarkerType(type, value)) {
603 static const UChar asterisksSymbols[1] = {
606 return toSymbolic(value, asterisksSymbols);
608 // We use the same characters for text security.
609 // See RenderText::setInternalString.
611 return String(&whiteBullet, 1);
613 return String(&bullet, 1);
615 static const UChar footnotesSymbols[4] = {
616 0x002A, 0x2051, 0x2020, 0x2021
618 return toSymbolic(value, footnotesSymbols);
621 // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
622 // instead, but I think this looks better.
623 return String(&blackSquare, 1);
625 case DecimalListStyle:
626 return String::number(value);
627 case DecimalLeadingZero:
628 if (value < -9 || value > 9)
629 return String::number(value);
631 return "-0" + String::number(-value); // -01 to -09
632 return "0" + String::number(value); // 00 to 09
635 static const UChar arabicIndicNumerals[10] = {
636 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
638 return toNumeric(value, arabicIndicNumerals);
640 case BinaryListStyle: {
641 static const UChar binaryNumerals[2] = {
644 return toNumeric(value, binaryNumerals);
647 static const UChar bengaliNumerals[10] = {
648 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
650 return toNumeric(value, bengaliNumerals);
654 static const UChar khmerNumerals[10] = {
655 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9
657 return toNumeric(value, khmerNumerals);
660 static const UChar devanagariNumerals[10] = {
661 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F
663 return toNumeric(value, devanagariNumerals);
666 static const UChar gujaratiNumerals[10] = {
667 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF
669 return toNumeric(value, gujaratiNumerals);
672 static const UChar gurmukhiNumerals[10] = {
673 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F
675 return toNumeric(value, gurmukhiNumerals);
678 static const UChar kannadaNumerals[10] = {
679 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF
681 return toNumeric(value, kannadaNumerals);
683 case LowerHexadecimal: {
684 static const UChar lowerHexadecimalNumerals[16] = {
685 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
687 return toNumeric(value, lowerHexadecimalNumerals);
690 static const UChar laoNumerals[10] = {
691 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9
693 return toNumeric(value, laoNumerals);
696 static const UChar malayalamNumerals[10] = {
697 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F
699 return toNumeric(value, malayalamNumerals);
702 static const UChar mongolianNumerals[10] = {
703 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819
705 return toNumeric(value, mongolianNumerals);
708 static const UChar myanmarNumerals[10] = {
709 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049
711 return toNumeric(value, myanmarNumerals);
714 static const UChar octalNumerals[8] = {
715 '0', '1', '2', '3', '4', '5', '6', '7'
717 return toNumeric(value, octalNumerals);
720 static const UChar oriyaNumerals[10] = {
721 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F
723 return toNumeric(value, oriyaNumerals);
727 static const UChar urduNumerals[10] = {
728 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9
730 return toNumeric(value, urduNumerals);
733 static const UChar teluguNumerals[10] = {
734 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F
736 return toNumeric(value, teluguNumerals);
739 static const UChar tibetanNumerals[10] = {
740 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29
742 return toNumeric(value, tibetanNumerals);
745 static const UChar thaiNumerals[10] = {
746 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59
748 return toNumeric(value, thaiNumerals);
750 case UpperHexadecimal: {
751 static const UChar upperHexadecimalNumerals[16] = {
752 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
754 return toNumeric(value, upperHexadecimalNumerals);
759 static const UChar lowerLatinAlphabet[26] = {
760 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
761 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
763 return toAlphabetic(value, lowerLatinAlphabet);
767 static const UChar upperLatinAlphabet[26] = {
768 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
769 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
771 return toAlphabetic(value, upperLatinAlphabet);
774 static const UChar lowerGreekAlphabet[24] = {
775 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
776 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
777 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
779 return toAlphabetic(value, lowerGreekAlphabet);
783 // FIXME: This table comes from the CSS3 draft, and is probably
784 // incorrect, given the comments in that draft.
785 static const UChar hiraganaAlphabet[48] = {
786 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
787 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
788 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
789 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
790 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
791 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
793 return toAlphabetic(value, hiraganaAlphabet);
795 case HiraganaIroha: {
796 // FIXME: This table comes from the CSS3 draft, and is probably
797 // incorrect, given the comments in that draft.
798 static const UChar hiraganaIrohaAlphabet[47] = {
799 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
800 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
801 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
802 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
803 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
804 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
806 return toAlphabetic(value, hiraganaIrohaAlphabet);
809 // FIXME: This table comes from the CSS3 draft, and is probably
810 // incorrect, given the comments in that draft.
811 static const UChar katakanaAlphabet[48] = {
812 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
813 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
814 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
815 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
816 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
817 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
819 return toAlphabetic(value, katakanaAlphabet);
821 case KatakanaIroha: {
822 // FIXME: This table comes from the CSS3 draft, and is probably
823 // incorrect, given the comments in that draft.
824 static const UChar katakanaIrohaAlphabet[47] = {
825 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
826 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
827 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
828 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
829 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
830 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
832 return toAlphabetic(value, katakanaIrohaAlphabet);
836 case EthiopicHalehameAaEt:
837 case EthiopicHalehameAaEr: {
838 static const UChar ethiopicHalehameAaErAlphabet[18] = {
839 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290,
840 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348
842 return toAlphabetic(value, ethiopicHalehameAaErAlphabet);
845 case EthiopicHalehameAmEt: {
846 static const UChar ethiopicHalehameAmEtAlphabet[33] = {
847 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
848 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8,
849 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320,
850 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
852 return toAlphabetic(value, ethiopicHalehameAmEtAlphabet);
855 case EthiopicAbegedeAmEt: {
856 static const UChar ethiopicAbegedeAmEtAlphabet[33] = {
857 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
858 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
859 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238,
860 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
862 return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet);
864 case CjkEarthlyBranch: {
865 static const UChar cjkEarthlyBranchAlphabet[12] = {
866 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533,
867 0x9149, 0x620C, 0x4EA5
869 return toAlphabetic(value, cjkEarthlyBranchAlphabet);
871 case CjkHeavenlyStem: {
872 static const UChar cjkHeavenlyStemAlphabet[10] = {
873 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC,
876 return toAlphabetic(value, cjkHeavenlyStemAlphabet);
879 case EthiopicHalehameGez: {
880 static const UChar ethiopicHalehameGezAlphabet[26] = {
881 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260,
882 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
883 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
885 return toAlphabetic(value, ethiopicHalehameGezAlphabet);
887 case EthiopicAbegede:
888 case EthiopicAbegedeGez: {
889 static const UChar ethiopicAbegedeGezAlphabet[26] = {
890 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320,
891 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338,
892 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350
894 return toAlphabetic(value, ethiopicAbegedeGezAlphabet);
896 case HangulConsonant: {
897 static const UChar hangulConsonantAlphabet[14] = {
898 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148,
899 0x314A, 0x314B, 0x314C, 0x314D, 0x314E
901 return toAlphabetic(value, hangulConsonantAlphabet);
904 static const UChar hangulAlphabet[14] = {
905 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790,
906 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558
908 return toAlphabetic(value, hangulAlphabet);
911 case EthiopicHalehameOmEt: {
912 static const UChar ethiopicHalehameOmEtAlphabet[25] = {
913 0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270,
914 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8,
915 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
917 return toAlphabetic(value, ethiopicHalehameOmEtAlphabet);
920 case EthiopicHalehameSidEt: {
921 static const UChar ethiopicHalehameSidEtAlphabet[26] = {
922 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
923 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0,
924 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
926 return toAlphabetic(value, ethiopicHalehameSidEtAlphabet);
929 case EthiopicHalehameSoEt: {
930 static const UChar ethiopicHalehameSoEtAlphabet[22] = {
931 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
932 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0,
933 0x1300, 0x1308, 0x1338, 0x1348
935 return toAlphabetic(value, ethiopicHalehameSoEtAlphabet);
938 case EthiopicHalehameTig: {
939 static const UChar ethiopicHalehameTigAlphabet[27] = {
940 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
941 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
942 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350
944 return toAlphabetic(value, ethiopicHalehameTigAlphabet);
947 case EthiopicHalehameTiEr: {
948 static const UChar ethiopicHalehameTiErAlphabet[31] = {
949 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250,
950 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8,
951 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328,
952 0x1330, 0x1338, 0x1348, 0x1350
954 return toAlphabetic(value, ethiopicHalehameTiErAlphabet);
956 case TigrinyaErAbegede:
957 case EthiopicAbegedeTiEr: {
958 static const UChar ethiopicAbegedeTiErAlphabet[31] = {
959 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
960 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
961 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238,
962 0x1270, 0x1278, 0x1330, 0x1350
964 return toAlphabetic(value, ethiopicAbegedeTiErAlphabet);
967 case EthiopicHalehameTiEt: {
968 static const UChar ethiopicHalehameTiEtAlphabet[34] = {
969 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
970 0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8,
971 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308,
972 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
974 return toAlphabetic(value, ethiopicHalehameTiEtAlphabet);
976 case TigrinyaEtAbegede:
977 case EthiopicAbegedeTiEt: {
978 static const UChar ethiopicAbegedeTiEtAlphabet[34] = {
979 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
980 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
981 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230,
982 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
984 return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet);
987 static const UChar upperGreekAlphabet[24] = {
988 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399,
989 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3,
990 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9
992 return toAlphabetic(value, upperGreekAlphabet);
994 case LowerNorwegian: {
995 static const UChar lowerNorwegianAlphabet[29] = {
996 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069,
997 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072,
998 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E6,
1001 return toAlphabetic(value, lowerNorwegianAlphabet);
1003 case UpperNorwegian: {
1004 static const UChar upperNorwegianAlphabet[29] = {
1005 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049,
1006 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052,
1007 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00C6,
1010 return toAlphabetic(value, upperNorwegianAlphabet);
1012 case CJKIdeographic: {
1013 static const UChar traditionalChineseInformalTable[16] = {
1014 0x842C, 0x5104, 0x5146,
1015 0x5341, 0x767E, 0x5343,
1016 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
1017 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
1019 return toCJKIdeographic(value, traditionalChineseInformalTable);
1023 return toRoman(value, false);
1025 return toRoman(value, true);
1029 // CSS3 says "armenian" means "lower-armenian".
1030 // But the CSS2.1 test suite contains uppercase test results for "armenian",
1031 // so we'll match the test suite.
1032 return toArmenian(value, true);
1034 return toArmenian(value, false);
1036 return toGeorgian(value);
1038 return toHebrew(value);
1041 ASSERT_NOT_REACHED();
1045 RenderListMarker::RenderListMarker(RenderListItem* item)
1046 : RenderBox(item->document())
1049 // init RenderObject attributes
1050 setInline(true); // our object is Inline
1051 setReplaced(true); // pretend to be replaced
1054 RenderListMarker::~RenderListMarker()
1057 m_image->removeClient(this);
1060 void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
1062 if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType()))
1063 setNeedsLayoutAndPrefWidthsRecalc();
1065 RenderBox::styleWillChange(diff, newStyle);
1068 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
1070 RenderBox::styleDidChange(diff, oldStyle);
1072 if (m_image != style()->listStyleImage()) {
1074 m_image->removeClient(this);
1075 m_image = style()->listStyleImage();
1077 m_image->addClient(this);
1081 InlineBox* RenderListMarker::createInlineBox()
1083 InlineBox* result = RenderBox::createInlineBox();
1084 result->setIsText(isText());
1088 bool RenderListMarker::isImage() const
1090 return m_image && !m_image->errorOccurred();
1093 LayoutRect RenderListMarker::localSelectionRect()
1095 InlineBox* box = inlineBoxWrapper();
1097 return LayoutRect(LayoutPoint(), size());
1098 RootInlineBox* root = m_inlineBoxWrapper->root();
1099 LayoutUnit newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - root->selectionBottom() : root->selectionTop() - m_inlineBoxWrapper->logicalTop();
1100 if (root->block()->style()->isHorizontalWritingMode())
1101 return LayoutRect(0, newLogicalTop, width(), root->selectionHeight());
1102 return LayoutRect(newLogicalTop, 0, root->selectionHeight(), height());
1105 void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1107 if (paintInfo.phase != PaintPhaseForeground)
1110 if (style()->visibility() != VISIBLE)
1113 LayoutPoint boxOrigin(paintOffset + location());
1114 LayoutRect overflowRect(visualOverflowRect());
1115 overflowRect.moveBy(boxOrigin);
1116 overflowRect.inflate(maximalOutlineSize(paintInfo.phase));
1118 if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
1121 LayoutRect box(boxOrigin, size());
1123 IntRect marker = getRelativeMarkerRect();
1124 marker.moveBy(roundedIntPoint(boxOrigin));
1126 GraphicsContext* context = paintInfo.context;
1130 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
1131 paintCustomHighlight(paintOffset, style()->highlight(), true);
1133 context->drawImage(m_image->image(this, marker.size()).get(), style()->colorSpace(), marker);
1134 if (selectionState() != SelectionNone) {
1135 LayoutRect selRect = localSelectionRect();
1136 selRect.moveBy(boxOrigin);
1137 context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor(), style()->colorSpace());
1143 // FIXME: paint gap between marker and list item proper
1144 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
1145 paintCustomHighlight(paintOffset, style()->highlight(), true);
1148 if (selectionState() != SelectionNone) {
1149 LayoutRect selRect = localSelectionRect();
1150 selRect.moveBy(boxOrigin);
1151 context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor(), style()->colorSpace());
1154 const Color color(style()->visitedDependentColor(CSSPropertyColor));
1155 context->setStrokeColor(color, style()->colorSpace());
1156 context->setStrokeStyle(SolidStroke);
1157 context->setStrokeThickness(1.0f);
1158 context->setFillColor(color, style()->colorSpace());
1160 EListStyleType type = style()->listStyleType();
1163 context->drawEllipse(marker);
1166 context->setFillColor(Color::transparent, ColorSpaceDeviceRGB);
1167 context->drawEllipse(marker);
1170 context->drawRect(marker);
1176 case AmharicAbegede:
1179 case BinaryListStyle:
1182 case CJKIdeographic:
1183 case CjkEarthlyBranch:
1184 case CjkHeavenlyStem:
1185 case DecimalLeadingZero:
1186 case DecimalListStyle:
1189 case EthiopicAbegede:
1190 case EthiopicAbegedeAmEt:
1191 case EthiopicAbegedeGez:
1192 case EthiopicAbegedeTiEr:
1193 case EthiopicAbegedeTiEt:
1194 case EthiopicHalehameAaEr:
1195 case EthiopicHalehameAaEt:
1196 case EthiopicHalehameAmEt:
1197 case EthiopicHalehameGez:
1198 case EthiopicHalehameOmEt:
1199 case EthiopicHalehameSidEt:
1200 case EthiopicHalehameSoEt:
1201 case EthiopicHalehameTiEr:
1202 case EthiopicHalehameTiEt:
1203 case EthiopicHalehameTig:
1208 case HangulConsonant:
1220 case LowerHexadecimal:
1222 case LowerNorwegian:
1238 case TigrinyaErAbegede:
1240 case TigrinyaEtAbegede:
1244 case UpperHexadecimal:
1246 case UpperNorwegian:
1253 if (m_text.isEmpty())
1256 const Font& font = style()->font();
1257 TextRun textRun = RenderBlock::constructTextRun(this, font, m_text, style());
1259 GraphicsContextStateSaver stateSaver(*context, false);
1260 if (!style()->isHorizontalWritingMode()) {
1261 marker.moveBy(roundedIntPoint(-boxOrigin));
1262 marker = marker.transposedRect();
1263 marker.moveBy(IntPoint(roundToInt(box.x()), roundToInt(box.y() - logicalHeight())));
1265 context->translate(marker.x(), marker.maxY());
1266 context->rotate(static_cast<float>(deg2rad(90.)));
1267 context->translate(-marker.x(), -marker.maxY());
1270 IntPoint textOrigin = IntPoint(marker.x(), marker.y() + style()->fontMetrics().ascent());
1272 if (type == Asterisks || type == Footnotes)
1273 context->drawText(font, textRun, textOrigin);
1275 // Text is not arbitrary. We can judge whether it's RTL from the first character,
1276 // and we only need to handle the direction RightToLeft for now.
1277 bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
1278 StringBuilder reversedText;
1279 if (textNeedsReversing) {
1280 int length = m_text.length();
1281 reversedText.reserveCapacity(length);
1282 for (int i = length - 1; i >= 0; --i)
1283 reversedText.append(m_text[i]);
1284 textRun.setText(reversedText.characters(), length);
1287 const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1288 if (style()->isLeftToRightDirection()) {
1289 int width = font.width(textRun);
1290 context->drawText(font, textRun, textOrigin);
1291 UChar suffixSpace[2] = { suffix, ' ' };
1292 context->drawText(font, RenderBlock::constructTextRun(this, font, suffixSpace, 2, style()), textOrigin + IntSize(width, 0));
1294 UChar spaceSuffix[2] = { ' ', suffix };
1295 TextRun spaceSuffixRun = RenderBlock::constructTextRun(this, font, spaceSuffix, 2, style());
1296 int width = font.width(spaceSuffixRun);
1297 context->drawText(font, spaceSuffixRun, textOrigin);
1298 context->drawText(font, textRun, textOrigin + IntSize(width, 0));
1303 void RenderListMarker::layout()
1305 ASSERT(needsLayout());
1308 setWidth(m_image->imageSize(this, style()->effectiveZoom()).width());
1309 setHeight(m_image->imageSize(this, style()->effectiveZoom()).height());
1311 setLogicalWidth(minPreferredLogicalWidth());
1312 setLogicalHeight(style()->fontMetrics().height());
1318 Length startMargin = style()->marginStart();
1319 Length endMargin = style()->marginEnd();
1320 if (startMargin.isFixed())
1321 setMarginStart(startMargin.value());
1322 if (endMargin.isFixed())
1323 setMarginEnd(endMargin.value());
1325 setNeedsLayout(false);
1328 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
1330 // A list marker can't have a background or border image, so no need to call the base class method.
1331 if (o != m_image->data())
1334 if (width() != m_image->imageSize(this, style()->effectiveZoom()).width() || height() != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred())
1335 setNeedsLayoutAndPrefWidthsRecalc();
1340 void RenderListMarker::computePreferredLogicalWidths()
1342 ASSERT(preferredLogicalWidthsDirty());
1346 const Font& font = style()->font();
1347 const FontMetrics& fontMetrics = font.fontMetrics();
1350 // FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful
1351 // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box.
1352 int bulletWidth = fontMetrics.ascent() / 2;
1353 m_image->setContainerSizeForRenderer(this, IntSize(bulletWidth, bulletWidth), style()->effectiveZoom());
1354 LayoutSize imageSize = m_image->imageSize(this, style()->effectiveZoom());
1355 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style()->isHorizontalWritingMode() ? imageSize.width() : imageSize.height();
1356 setPreferredLogicalWidthsDirty(false);
1361 LayoutUnit logicalWidth = 0;
1362 EListStyleType type = style()->listStyleType();
1368 m_text = listMarkerText(type, m_listItem->value());
1369 logicalWidth = font.width(m_text); // no suffix for these types
1374 m_text = listMarkerText(type, 0); // value is ignored for these types
1375 logicalWidth = (fontMetrics.ascent() * 2 / 3 + 1) / 2 + 2;
1379 case AmharicAbegede:
1382 case BinaryListStyle:
1385 case CJKIdeographic:
1386 case CjkEarthlyBranch:
1387 case CjkHeavenlyStem:
1388 case DecimalLeadingZero:
1389 case DecimalListStyle:
1392 case EthiopicAbegede:
1393 case EthiopicAbegedeAmEt:
1394 case EthiopicAbegedeGez:
1395 case EthiopicAbegedeTiEr:
1396 case EthiopicAbegedeTiEt:
1397 case EthiopicHalehameAaEr:
1398 case EthiopicHalehameAaEt:
1399 case EthiopicHalehameAmEt:
1400 case EthiopicHalehameGez:
1401 case EthiopicHalehameOmEt:
1402 case EthiopicHalehameSidEt:
1403 case EthiopicHalehameSoEt:
1404 case EthiopicHalehameTiEr:
1405 case EthiopicHalehameTiEt:
1406 case EthiopicHalehameTig:
1411 case HangulConsonant:
1423 case LowerHexadecimal:
1425 case LowerNorwegian:
1441 case TigrinyaErAbegede:
1443 case TigrinyaEtAbegede:
1447 case UpperHexadecimal:
1449 case UpperNorwegian:
1452 m_text = listMarkerText(type, m_listItem->value());
1453 if (m_text.isEmpty())
1456 LayoutUnit itemWidth = font.width(m_text);
1457 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1458 LayoutUnit suffixSpaceWidth = font.width(RenderBlock::constructTextRun(this, font, suffixSpace, 2, style()));
1459 logicalWidth = itemWidth + suffixSpaceWidth;
1464 m_minPreferredLogicalWidth = logicalWidth;
1465 m_maxPreferredLogicalWidth = logicalWidth;
1467 setPreferredLogicalWidthsDirty(false);
1472 void RenderListMarker::updateMargins()
1474 const FontMetrics& fontMetrics = style()->fontMetrics();
1476 LayoutUnit marginStart = 0;
1477 LayoutUnit marginEnd = 0;
1481 marginEnd = cMarkerPadding;
1482 else switch (style()->listStyleType()) {
1487 marginEnd = fontMetrics.ascent() - minPreferredLogicalWidth() + 1;
1493 if (style()->isLeftToRightDirection()) {
1495 marginStart = -minPreferredLogicalWidth() - cMarkerPadding;
1497 int offset = fontMetrics.ascent() * 2 / 3;
1498 switch (style()->listStyleType()) {
1502 marginStart = -offset - cMarkerPadding - 1;
1507 marginStart = m_text.isEmpty() ? ZERO_LAYOUT_UNIT : -minPreferredLogicalWidth() - offset / 2;
1510 marginEnd = -marginStart - minPreferredLogicalWidth();
1513 marginEnd = cMarkerPadding;
1515 int offset = fontMetrics.ascent() * 2 / 3;
1516 switch (style()->listStyleType()) {
1520 marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth();
1525 marginEnd = m_text.isEmpty() ? 0 : offset / 2;
1528 marginStart = -marginEnd - minPreferredLogicalWidth();
1533 style()->setMarginStart(Length(marginStart, Fixed));
1534 style()->setMarginEnd(Length(marginEnd, Fixed));
1537 LayoutUnit RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1540 return m_listItem->lineHeight(firstLine, direction, PositionOfInteriorLineBoxes);
1541 return RenderBox::lineHeight(firstLine, direction, linePositionMode);
1544 LayoutUnit RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1547 return m_listItem->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
1548 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1551 String RenderListMarker::suffix() const
1553 EListStyleType type = style()->listStyleType();
1554 const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1559 // If the suffix is not ' ', an extra space is needed
1561 if (style()->isLeftToRightDirection()) {
1569 return String(data, 2);
1572 bool RenderListMarker::isInside() const
1574 return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
1577 IntRect RenderListMarker::getRelativeMarkerRect()
1580 return IntRect(0, 0, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
1582 IntRect relativeRect;
1583 EListStyleType type = style()->listStyleType();
1587 const Font& font = style()->font();
1588 relativeRect = IntRect(0, 0, font.width(m_text), font.fontMetrics().height());
1594 // FIXME: Are these particular rounding rules necessary?
1595 const FontMetrics& fontMetrics = style()->fontMetrics();
1596 int ascent = fontMetrics.ascent();
1597 int bulletWidth = (ascent * 2 / 3 + 1) / 2;
1598 relativeRect = IntRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
1605 case AmharicAbegede:
1608 case BinaryListStyle:
1611 case CJKIdeographic:
1612 case CjkEarthlyBranch:
1613 case CjkHeavenlyStem:
1614 case DecimalLeadingZero:
1615 case DecimalListStyle:
1618 case EthiopicAbegede:
1619 case EthiopicAbegedeAmEt:
1620 case EthiopicAbegedeGez:
1621 case EthiopicAbegedeTiEr:
1622 case EthiopicAbegedeTiEt:
1623 case EthiopicHalehameAaEr:
1624 case EthiopicHalehameAaEt:
1625 case EthiopicHalehameAmEt:
1626 case EthiopicHalehameGez:
1627 case EthiopicHalehameOmEt:
1628 case EthiopicHalehameSidEt:
1629 case EthiopicHalehameSoEt:
1630 case EthiopicHalehameTiEr:
1631 case EthiopicHalehameTiEt:
1632 case EthiopicHalehameTig:
1637 case HangulConsonant:
1649 case LowerHexadecimal:
1651 case LowerNorwegian:
1667 case TigrinyaErAbegede:
1669 case TigrinyaEtAbegede:
1673 case UpperHexadecimal:
1675 case UpperNorwegian:
1678 if (m_text.isEmpty())
1680 const Font& font = style()->font();
1681 int itemWidth = font.width(m_text);
1682 UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1683 int suffixSpaceWidth = font.width(RenderBlock::constructTextRun(this, font, suffixSpace, 2, style()));
1684 relativeRect = IntRect(0, 0, itemWidth + suffixSpaceWidth, font.fontMetrics().height());
1687 if (!style()->isHorizontalWritingMode()) {
1688 relativeRect = relativeRect.transposedRect();
1689 relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
1692 return relativeRect;
1695 void RenderListMarker::setSelectionState(SelectionState state)
1697 // The selection state for our containing block hierarchy is updated by the base class call.
1698 RenderBox::setSelectionState(state);
1700 if (m_inlineBoxWrapper && canUpdateSelectionOnRootLineBoxes())
1701 if (RootInlineBox* root = m_inlineBoxWrapper->root())
1702 root->setHasSelectedChildren(state != SelectionNone);
1705 LayoutRect RenderListMarker::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
1707 ASSERT(!needsLayout());
1709 if (selectionState() == SelectionNone || !inlineBoxWrapper())
1710 return LayoutRect();
1712 RootInlineBox* root = inlineBoxWrapper()->root();
1713 LayoutRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight());
1715 if (clipToVisibleContent)
1716 computeRectForRepaint(repaintContainer, rect);
1718 rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1723 } // namespace WebCore