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"
31
#include "GraphicsContext.h"
32
#include "RenderLayer.h"
33
#include "RenderListItem.h"
34
#include "RenderView.h"
35
#include <wtf/text/StringBuilder.h>
36
#include <wtf/unicode/CharacterNames.h>
40
using namespace Unicode;
44
const int cMarkerPadding = 7;
46
enum SequenceType { NumericSequence, AlphabeticSequence };
48
static String toRoman(int number, bool upper)
50
// FIXME: CSS3 describes how to make this work for much larger numbers,
51
// using overbars and special characters. It also specifies the characters
52
// in the range U+2160 to U+217F instead of standard ASCII ones.
53
ASSERT(number >= 1 && number <= 3999);
55
// Big enough to store largest roman number less than 3999 which
56
// is 3888 (MMMDCCCLXXXVIII)
57
const int lettersSize = 15;
58
LChar letters[lettersSize];
61
const LChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
62
const LChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
63
const LChar* digits = upper ? udigits : ldigits;
66
int num = number % 10;
68
for (int i = num % 5; i > 0; i--)
69
letters[lettersSize - ++length] = digits[d];
70
if (num >= 4 && num <= 8)
71
letters[lettersSize - ++length] = digits[d + 1];
73
letters[lettersSize - ++length] = digits[d + 2];
75
letters[lettersSize - ++length] = digits[d];
80
ASSERT(length <= lettersSize);
81
return String(&letters[lettersSize - length], length);
84
// The typedef is needed because taking sizeof(number) in the const expression below doesn't work with some compilers.
85
// This is likely the case because of the template.
86
typedef int numberType;
88
template <typename CharacterType>
89
static inline String toAlphabeticOrNumeric(numberType number, const CharacterType* sequence, unsigned sequenceSize, SequenceType type)
91
ASSERT(sequenceSize >= 2);
93
const int lettersSize = sizeof(numberType) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
95
CharacterType letters[lettersSize];
97
bool isNegativeNumber = false;
98
unsigned numberShadow = number;
99
if (type == AlphabeticSequence) {
102
} else if (number < 0) {
103
numberShadow = -number;
104
isNegativeNumber = true;
106
letters[lettersSize - 1] = sequence[numberShadow % sequenceSize];
109
if (type == AlphabeticSequence) {
110
while ((numberShadow /= sequenceSize) > 0) {
112
letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
115
while ((numberShadow /= sequenceSize) > 0)
116
letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
118
if (isNegativeNumber)
119
letters[lettersSize - ++length] = hyphenMinus;
121
ASSERT(length <= lettersSize);
122
return String(&letters[lettersSize - length], length);
125
template <typename CharacterType>
126
static String toSymbolic(int number, const CharacterType* symbols, unsigned symbolsSize)
129
ASSERT(symbolsSize >= 1);
130
unsigned numberShadow = number;
133
// The asterisks list-style-type is the worst case; we show |numberShadow| asterisks.
134
StringBuilder letters;
135
letters.append(symbols[numberShadow % symbolsSize]);
136
unsigned numSymbols = numberShadow / symbolsSize;
138
letters.append(symbols[numberShadow % symbolsSize]);
139
return letters.toString();
142
template <typename CharacterType>
143
static String toAlphabetic(int number, const CharacterType* alphabet, unsigned alphabetSize)
145
return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence);
148
template <typename CharacterType>
149
static String toNumeric(int number, const CharacterType* numerals, unsigned numeralsSize)
151
return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence);
154
template <typename CharacterType, size_t size>
155
static inline String toAlphabetic(int number, const CharacterType(&alphabet)[size])
157
return toAlphabetic(number, alphabet, size);
160
template <typename CharacterType, size_t size>
161
static inline String toNumeric(int number, const CharacterType(&alphabet)[size])
163
return toNumeric(number, alphabet, size);
166
template <typename CharacterType, size_t size>
167
static inline String toSymbolic(int number, const CharacterType(&alphabet)[size])
169
return toSymbolic(number, alphabet, size);
172
static int toHebrewUnder1000(int number, UChar letters[5])
174
// FIXME: CSS3 mentions various refinements not implemented here.
175
// FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
176
ASSERT(number >= 0 && number < 1000);
178
int fourHundreds = number / 400;
179
for (int i = 0; i < fourHundreds; i++)
180
letters[length++] = 1511 + 3;
183
letters[length++] = 1511 + (number / 100) - 1;
185
if (number == 15 || number == 16) {
186
letters[length++] = 1487 + 9;
187
letters[length++] = 1487 + number - 9;
189
if (int tens = number / 10) {
190
static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
191
letters[length++] = hebrewTens[tens - 1];
193
if (int ones = number % 10)
194
letters[length++] = 1487 + ones;
200
static String toHebrew(int number)
202
// FIXME: CSS3 mentions ways to make this work for much larger numbers.
203
ASSERT(number >= 0 && number <= 999999);
206
static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
207
return String(hebrewZero, 3);
210
const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
211
UChar letters[lettersSize];
217
length = toHebrewUnder1000(number / 1000, letters);
218
letters[length++] = '\'';
219
number = number % 1000;
221
length += toHebrewUnder1000(number, letters + length);
223
ASSERT(length <= lettersSize);
224
return String(letters, length);
227
static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
229
ASSERT(number >= 0 && number < 10000);
232
int lowerOffset = upper ? 0 : 0x0030;
234
if (int thousands = number / 1000) {
235
if (thousands == 7) {
236
letters[length++] = 0x0552 + lowerOffset;
238
letters[length++] = 0x0302;
240
letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
242
letters[length++] = 0x0302;
246
if (int hundreds = (number / 100) % 10) {
247
letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
249
letters[length++] = 0x0302;
252
if (int tens = (number / 10) % 10) {
253
letters[length++] = (0x053A - 1 + lowerOffset) + tens;
255
letters[length++] = 0x0302;
258
if (int ones = number % 10) {
259
letters[length++] = (0x531 - 1 + lowerOffset) + ones;
261
letters[length++] = 0x0302;
267
static String toArmenian(int number, bool upper)
269
ASSERT(number >= 1 && number <= 99999999);
271
const int lettersSize = 18; // twice what toArmenianUnder10000 needs
272
UChar letters[lettersSize];
274
int length = toArmenianUnder10000(number / 10000, upper, true, letters);
275
length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
277
ASSERT(length <= lettersSize);
278
return String(letters, length);
281
static String toGeorgian(int number)
283
ASSERT(number >= 1 && number <= 19999);
285
const int lettersSize = 5;
286
UChar letters[lettersSize];
291
letters[length++] = 0x10F5;
293
if (int thousands = (number / 1000) % 10) {
294
static const UChar georgianThousands[9] = {
295
0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
297
letters[length++] = georgianThousands[thousands - 1];
300
if (int hundreds = (number / 100) % 10) {
301
static const UChar georgianHundreds[9] = {
302
0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
304
letters[length++] = georgianHundreds[hundreds - 1];
307
if (int tens = (number / 10) % 10) {
308
static const UChar georgianTens[9] = {
309
0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
311
letters[length++] = georgianTens[tens - 1];
314
if (int ones = number % 10) {
315
static const UChar georgianOnes[9] = {
316
0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
318
letters[length++] = georgianOnes[ones - 1];
321
ASSERT(length <= lettersSize);
322
return String(letters, length);
325
// The table uses the order from the CSS3 specification:
326
// first 3 group markers, then 3 digit markers, then ten digits.
327
static String toCJKIdeographic(int number, const UChar table[16])
331
enum AbstractCJKChar {
333
secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
334
secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
335
digit0, digit1, digit2, digit3, digit4,
336
digit5, digit6, digit7, digit8, digit9
340
return String(&table[digit0 - 1], 1);
342
const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker
343
const int bufferLength = 4 * groupLength;
344
AbstractCJKChar buffer[bufferLength] = { noChar };
346
for (int i = 0; i < 4; ++i) {
347
int groupValue = number % 10000;
350
// Process least-significant group first, but put it in the buffer last.
351
AbstractCJKChar* group = &buffer[(3 - i) * groupLength];
354
group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i);
356
// Put in the four digits and digit markers for any non-zero digits.
357
group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10));
358
if (number != 0 || groupValue > 9) {
359
int digitValue = ((groupValue / 10) % 10);
360
group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue);
362
group[5] = secondDigitMarker;
364
if (number != 0 || groupValue > 99) {
365
int digitValue = ((groupValue / 100) % 10);
366
group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue);
368
group[3] = thirdDigitMarker;
370
if (number != 0 || groupValue > 999) {
371
int digitValue = groupValue / 1000;
372
group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue);
374
group[1] = fourthDigitMarker;
377
// Remove the tens digit, but leave the marker, for any group that has
378
// a value of less than 20.
379
if (groupValue < 20) {
380
ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1);
388
// Convert into characters, omitting consecutive runs of digit0 and
389
// any trailing digit0.
391
UChar characters[bufferLength];
392
AbstractCJKChar last = noChar;
393
for (int i = 0; i < bufferLength; ++i) {
394
AbstractCJKChar a = buffer[i];
396
if (a != digit0 || last != digit0)
397
characters[length++] = table[a - 1];
404
return String(characters, length);
407
static EListStyleType effectiveListMarkerType(EListStyleType type, int value)
409
// Note, the following switch statement has been explicitly grouped
410
// by list-style-type ordinal range.
414
case BinaryListStyle:
417
case DecimalLeadingZero:
418
case DecimalListStyle:
426
case LowerHexadecimal:
438
case UpperHexadecimal:
440
return type; // Can represent all ordinals.
442
return (value < 1 || value > 99999999) ? DecimalListStyle : type;
444
return (value < 0) ? DecimalListStyle : type;
446
return (value < 1 || value > 19999) ? DecimalListStyle : type;
448
return (value < 0 || value > 999999) ? DecimalListStyle : type;
451
return (value < 1 || value > 3999) ? DecimalListStyle : type;
456
case CjkEarthlyBranch:
457
case CjkHeavenlyStem:
459
case EthiopicAbegede:
460
case EthiopicAbegedeAmEt:
461
case EthiopicAbegedeGez:
462
case EthiopicAbegedeTiEr:
463
case EthiopicAbegedeTiEt:
464
case EthiopicHalehameAaEr:
465
case EthiopicHalehameAaEt:
466
case EthiopicHalehameAmEt:
467
case EthiopicHalehameGez:
468
case EthiopicHalehameOmEt:
469
case EthiopicHalehameSidEt:
470
case EthiopicHalehameSoEt:
471
case EthiopicHalehameTiEr:
472
case EthiopicHalehameTiEt:
473
case EthiopicHalehameTig:
476
case HangulConsonant:
491
case TigrinyaErAbegede:
493
case TigrinyaEtAbegede:
499
return (value < 1) ? DecimalListStyle : type;
502
ASSERT_NOT_REACHED();
506
static UChar listMarkerSuffix(EListStyleType type, int value)
508
// If the list-style-type cannot represent |value| because it's outside its
509
// ordinal range then we fall back to some list style that can represent |value|.
510
EListStyleType effectiveType = effectiveListMarkerType(type, value);
512
// Note, the following switch statement has been explicitly
513
// grouped by list-style-type suffix.
514
switch (effectiveType) {
526
case EthiopicAbegede:
527
case EthiopicAbegedeAmEt:
528
case EthiopicAbegedeGez:
529
case EthiopicAbegedeTiEr:
530
case EthiopicAbegedeTiEt:
531
case EthiopicHalehameAaEr:
532
case EthiopicHalehameAaEt:
533
case EthiopicHalehameAmEt:
534
case EthiopicHalehameGez:
535
case EthiopicHalehameOmEt:
536
case EthiopicHalehameSidEt:
537
case EthiopicHalehameSoEt:
538
case EthiopicHalehameTiEr:
539
case EthiopicHalehameTiEt:
540
case EthiopicHalehameTig:
546
case TigrinyaErAbegede:
548
case TigrinyaEtAbegede:
549
return ethiopicPrefaceColon;
553
case BinaryListStyle:
556
case CjkEarthlyBranch:
557
case CjkHeavenlyStem:
558
case DecimalLeadingZero:
559
case DecimalListStyle:
565
case HangulConsonant:
577
case LowerHexadecimal:
593
case UpperHexadecimal:
601
ASSERT_NOT_REACHED();
605
String listMarkerText(EListStyleType type, int value)
607
// If the list-style-type, say hebrew, cannot represent |value| because it's outside
608
// its ordinal range then we fallback to some list style that can represent |value|.
609
switch (effectiveListMarkerType(type, value)) {
614
static const LChar asterisksSymbols[1] = {
617
return toSymbolic(value, asterisksSymbols);
619
// We use the same characters for text security.
620
// See RenderText::setInternalString.
622
return String(&whiteBullet, 1);
624
return String(&bullet, 1);
626
static const UChar footnotesSymbols[4] = {
627
0x002A, 0x2051, 0x2020, 0x2021
629
return toSymbolic(value, footnotesSymbols);
632
// The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
633
// instead, but I think this looks better.
634
return String(&blackSquare, 1);
636
case DecimalListStyle:
637
return String::number(value);
638
case DecimalLeadingZero:
639
if (value < -9 || value > 9)
640
return String::number(value);
642
return "-0" + String::number(-value); // -01 to -09
643
return "0" + String::number(value); // 00 to 09
646
static const UChar arabicIndicNumerals[10] = {
647
0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
649
return toNumeric(value, arabicIndicNumerals);
651
case BinaryListStyle: {
652
static const LChar binaryNumerals[2] = {
655
return toNumeric(value, binaryNumerals);
658
static const UChar bengaliNumerals[10] = {
659
0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
661
return toNumeric(value, bengaliNumerals);
665
static const UChar khmerNumerals[10] = {
666
0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9
668
return toNumeric(value, khmerNumerals);
671
static const UChar devanagariNumerals[10] = {
672
0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F
674
return toNumeric(value, devanagariNumerals);
677
static const UChar gujaratiNumerals[10] = {
678
0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF
680
return toNumeric(value, gujaratiNumerals);
683
static const UChar gurmukhiNumerals[10] = {
684
0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F
686
return toNumeric(value, gurmukhiNumerals);
689
static const UChar kannadaNumerals[10] = {
690
0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF
692
return toNumeric(value, kannadaNumerals);
694
case LowerHexadecimal: {
695
static const LChar lowerHexadecimalNumerals[16] = {
696
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
698
return toNumeric(value, lowerHexadecimalNumerals);
701
static const UChar laoNumerals[10] = {
702
0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9
704
return toNumeric(value, laoNumerals);
707
static const UChar malayalamNumerals[10] = {
708
0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F
710
return toNumeric(value, malayalamNumerals);
713
static const UChar mongolianNumerals[10] = {
714
0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819
716
return toNumeric(value, mongolianNumerals);
719
static const UChar myanmarNumerals[10] = {
720
0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049
722
return toNumeric(value, myanmarNumerals);
725
static const LChar octalNumerals[8] = {
726
'0', '1', '2', '3', '4', '5', '6', '7'
728
return toNumeric(value, octalNumerals);
731
static const UChar oriyaNumerals[10] = {
732
0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F
734
return toNumeric(value, oriyaNumerals);
738
static const UChar urduNumerals[10] = {
739
0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9
741
return toNumeric(value, urduNumerals);
744
static const UChar teluguNumerals[10] = {
745
0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F
747
return toNumeric(value, teluguNumerals);
750
static const UChar tibetanNumerals[10] = {
751
0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29
753
return toNumeric(value, tibetanNumerals);
756
static const UChar thaiNumerals[10] = {
757
0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59
759
return toNumeric(value, thaiNumerals);
761
case UpperHexadecimal: {
762
static const LChar upperHexadecimalNumerals[16] = {
763
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
765
return toNumeric(value, upperHexadecimalNumerals);
770
static const LChar lowerLatinAlphabet[26] = {
771
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
772
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
774
return toAlphabetic(value, lowerLatinAlphabet);
778
static const LChar upperLatinAlphabet[26] = {
779
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
780
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
782
return toAlphabetic(value, upperLatinAlphabet);
785
static const UChar lowerGreekAlphabet[24] = {
786
0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
787
0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
788
0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
790
return toAlphabetic(value, lowerGreekAlphabet);
794
// FIXME: This table comes from the CSS3 draft, and is probably
795
// incorrect, given the comments in that draft.
796
static const UChar hiraganaAlphabet[48] = {
797
0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
798
0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
799
0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
800
0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
801
0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
802
0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
804
return toAlphabetic(value, hiraganaAlphabet);
806
case HiraganaIroha: {
807
// FIXME: This table comes from the CSS3 draft, and is probably
808
// incorrect, given the comments in that draft.
809
static const UChar hiraganaIrohaAlphabet[47] = {
810
0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
811
0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
812
0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
813
0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
814
0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
815
0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
817
return toAlphabetic(value, hiraganaIrohaAlphabet);
820
// FIXME: This table comes from the CSS3 draft, and is probably
821
// incorrect, given the comments in that draft.
822
static const UChar katakanaAlphabet[48] = {
823
0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
824
0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
825
0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
826
0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
827
0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
828
0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
830
return toAlphabetic(value, katakanaAlphabet);
832
case KatakanaIroha: {
833
// FIXME: This table comes from the CSS3 draft, and is probably
834
// incorrect, given the comments in that draft.
835
static const UChar katakanaIrohaAlphabet[47] = {
836
0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
837
0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
838
0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
839
0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
840
0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
841
0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
843
return toAlphabetic(value, katakanaIrohaAlphabet);
847
case EthiopicHalehameAaEt:
848
case EthiopicHalehameAaEr: {
849
static const UChar ethiopicHalehameAaErAlphabet[18] = {
850
0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290,
851
0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348
853
return toAlphabetic(value, ethiopicHalehameAaErAlphabet);
856
case EthiopicHalehameAmEt: {
857
static const UChar ethiopicHalehameAmEtAlphabet[33] = {
858
0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
859
0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8,
860
0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320,
861
0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
863
return toAlphabetic(value, ethiopicHalehameAmEtAlphabet);
866
case EthiopicAbegedeAmEt: {
867
static const UChar ethiopicAbegedeAmEtAlphabet[33] = {
868
0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
869
0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
870
0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238,
871
0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
873
return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet);
875
case CjkEarthlyBranch: {
876
static const UChar cjkEarthlyBranchAlphabet[12] = {
877
0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533,
878
0x9149, 0x620C, 0x4EA5
880
return toAlphabetic(value, cjkEarthlyBranchAlphabet);
882
case CjkHeavenlyStem: {
883
static const UChar cjkHeavenlyStemAlphabet[10] = {
884
0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC,
887
return toAlphabetic(value, cjkHeavenlyStemAlphabet);
890
case EthiopicHalehameGez: {
891
static const UChar ethiopicHalehameGezAlphabet[26] = {
892
0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260,
893
0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
894
0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
896
return toAlphabetic(value, ethiopicHalehameGezAlphabet);
898
case EthiopicAbegede:
899
case EthiopicAbegedeGez: {
900
static const UChar ethiopicAbegedeGezAlphabet[26] = {
901
0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320,
902
0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338,
903
0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350
905
return toAlphabetic(value, ethiopicAbegedeGezAlphabet);
907
case HangulConsonant: {
908
static const UChar hangulConsonantAlphabet[14] = {
909
0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148,
910
0x314A, 0x314B, 0x314C, 0x314D, 0x314E
912
return toAlphabetic(value, hangulConsonantAlphabet);
915
static const UChar hangulAlphabet[14] = {
916
0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790,
917
0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558
919
return toAlphabetic(value, hangulAlphabet);
922
case EthiopicHalehameOmEt: {
923
static const UChar ethiopicHalehameOmEtAlphabet[25] = {
924
0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270,
925
0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8,
926
0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
928
return toAlphabetic(value, ethiopicHalehameOmEtAlphabet);
931
case EthiopicHalehameSidEt: {
932
static const UChar ethiopicHalehameSidEtAlphabet[26] = {
933
0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
934
0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0,
935
0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
937
return toAlphabetic(value, ethiopicHalehameSidEtAlphabet);
940
case EthiopicHalehameSoEt: {
941
static const UChar ethiopicHalehameSoEtAlphabet[22] = {
942
0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
943
0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0,
944
0x1300, 0x1308, 0x1338, 0x1348
946
return toAlphabetic(value, ethiopicHalehameSoEtAlphabet);
949
case EthiopicHalehameTig: {
950
static const UChar ethiopicHalehameTigAlphabet[27] = {
951
0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
952
0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
953
0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350
955
return toAlphabetic(value, ethiopicHalehameTigAlphabet);
958
case EthiopicHalehameTiEr: {
959
static const UChar ethiopicHalehameTiErAlphabet[31] = {
960
0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250,
961
0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8,
962
0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328,
963
0x1330, 0x1338, 0x1348, 0x1350
965
return toAlphabetic(value, ethiopicHalehameTiErAlphabet);
967
case TigrinyaErAbegede:
968
case EthiopicAbegedeTiEr: {
969
static const UChar ethiopicAbegedeTiErAlphabet[31] = {
970
0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
971
0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
972
0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238,
973
0x1270, 0x1278, 0x1330, 0x1350
975
return toAlphabetic(value, ethiopicAbegedeTiErAlphabet);
978
case EthiopicHalehameTiEt: {
979
static const UChar ethiopicHalehameTiEtAlphabet[34] = {
980
0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
981
0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8,
982
0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308,
983
0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
985
return toAlphabetic(value, ethiopicHalehameTiEtAlphabet);
987
case TigrinyaEtAbegede:
988
case EthiopicAbegedeTiEt: {
989
static const UChar ethiopicAbegedeTiEtAlphabet[34] = {
990
0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
991
0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
992
0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230,
993
0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
995
return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet);
998
static const UChar upperGreekAlphabet[24] = {
999
0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399,
1000
0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3,
1001
0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9
1003
return toAlphabetic(value, upperGreekAlphabet);
1005
case LowerNorwegian: {
1006
static const LChar lowerNorwegianAlphabet[29] = {
1007
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
1008
0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
1009
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xE6,
1012
return toAlphabetic(value, lowerNorwegianAlphabet);
1014
case UpperNorwegian: {
1015
static const LChar upperNorwegianAlphabet[29] = {
1016
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
1017
0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
1018
0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xC6,
1021
return toAlphabetic(value, upperNorwegianAlphabet);
1023
case CJKIdeographic: {
1024
static const UChar traditionalChineseInformalTable[16] = {
1025
0x842C, 0x5104, 0x5146,
1026
0x5341, 0x767E, 0x5343,
1027
0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
1028
0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
1030
return toCJKIdeographic(value, traditionalChineseInformalTable);
1034
return toRoman(value, false);
1036
return toRoman(value, true);
1040
// CSS3 says "armenian" means "lower-armenian".
1041
// But the CSS2.1 test suite contains uppercase test results for "armenian",
1042
// so we'll match the test suite.
1043
return toArmenian(value, true);
1045
return toArmenian(value, false);
1047
return toGeorgian(value);
1049
return toHebrew(value);
1052
ASSERT_NOT_REACHED();
1056
RenderListMarker::RenderListMarker(RenderListItem* item)
1057
: RenderBox(item->document())
1060
// init RenderObject attributes
1061
setInline(true); // our object is Inline
1062
setReplaced(true); // pretend to be replaced
1065
RenderListMarker::~RenderListMarker()
1068
m_image->removeClient(this);
1071
void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
1073
if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType()))
1074
setNeedsLayoutAndPrefWidthsRecalc();
1076
RenderBox::styleWillChange(diff, newStyle);
1079
void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
1081
RenderBox::styleDidChange(diff, oldStyle);
1083
if (m_image != style()->listStyleImage()) {
1085
m_image->removeClient(this);
1086
m_image = style()->listStyleImage();
1088
m_image->addClient(this);
1092
InlineBox* RenderListMarker::createInlineBox()
1094
InlineBox* result = RenderBox::createInlineBox();
1095
result->setIsText(isText());
1099
bool RenderListMarker::isImage() const
1101
return m_image && !m_image->errorOccurred();
1104
LayoutRect RenderListMarker::localSelectionRect()
1106
InlineBox* box = inlineBoxWrapper();
1108
return LayoutRect(LayoutPoint(), size());
1109
RootInlineBox* root = m_inlineBoxWrapper->root();
1110
LayoutUnit newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - root->selectionBottom() : root->selectionTop() - m_inlineBoxWrapper->logicalTop();
1111
if (root->block()->style()->isHorizontalWritingMode())
1112
return LayoutRect(0, newLogicalTop, width(), root->selectionHeight());
1113
return LayoutRect(newLogicalTop, 0, root->selectionHeight(), height());
1116
void RenderListMarker::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1118
if (paintInfo.phase != PaintPhaseForeground)
1121
if (style()->visibility() != VISIBLE)
1124
LayoutPoint boxOrigin(paintOffset + location());
1125
LayoutRect overflowRect(visualOverflowRect());
1126
overflowRect.moveBy(boxOrigin);
1127
overflowRect.inflate(maximalOutlineSize(paintInfo.phase));
1129
if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
1132
LayoutRect box(boxOrigin, size());
1134
IntRect marker = getRelativeMarkerRect();
1135
marker.moveBy(roundedIntPoint(boxOrigin));
1137
GraphicsContext* context = paintInfo.context;
1141
if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
1142
paintCustomHighlight(paintOffset, style()->highlight(), true);
1144
context->drawImage(m_image->image(this, marker.size()).get(), style()->colorSpace(), marker);
1145
if (selectionState() != SelectionNone) {
1146
LayoutRect selRect = localSelectionRect();
1147
selRect.moveBy(boxOrigin);
1148
context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor(), style()->colorSpace());
1154
// FIXME: paint gap between marker and list item proper
1155
if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
1156
paintCustomHighlight(paintOffset, style()->highlight(), true);
1159
if (selectionState() != SelectionNone) {
1160
LayoutRect selRect = localSelectionRect();
1161
selRect.moveBy(boxOrigin);
1162
context->fillRect(pixelSnappedIntRect(selRect), selectionBackgroundColor(), style()->colorSpace());
1165
const Color color(style()->visitedDependentColor(CSSPropertyColor));
1166
context->setStrokeColor(color, style()->colorSpace());
1167
context->setStrokeStyle(SolidStroke);
1168
context->setStrokeThickness(1.0f);
1169
context->setFillColor(color, style()->colorSpace());
1171
EListStyleType type = style()->listStyleType();
1174
context->drawEllipse(marker);
1177
context->setFillColor(Color::transparent, ColorSpaceDeviceRGB);
1178
context->drawEllipse(marker);
1181
context->drawRect(marker);
1187
case AmharicAbegede:
1190
case BinaryListStyle:
1193
case CJKIdeographic:
1194
case CjkEarthlyBranch:
1195
case CjkHeavenlyStem:
1196
case DecimalLeadingZero:
1197
case DecimalListStyle:
1200
case EthiopicAbegede:
1201
case EthiopicAbegedeAmEt:
1202
case EthiopicAbegedeGez:
1203
case EthiopicAbegedeTiEr:
1204
case EthiopicAbegedeTiEt:
1205
case EthiopicHalehameAaEr:
1206
case EthiopicHalehameAaEt:
1207
case EthiopicHalehameAmEt:
1208
case EthiopicHalehameGez:
1209
case EthiopicHalehameOmEt:
1210
case EthiopicHalehameSidEt:
1211
case EthiopicHalehameSoEt:
1212
case EthiopicHalehameTiEr:
1213
case EthiopicHalehameTiEt:
1214
case EthiopicHalehameTig:
1219
case HangulConsonant:
1231
case LowerHexadecimal:
1233
case LowerNorwegian:
1249
case TigrinyaErAbegede:
1251
case TigrinyaEtAbegede:
1255
case UpperHexadecimal:
1257
case UpperNorwegian:
1264
if (m_text.isEmpty())
1267
const Font& font = style()->font();
1268
TextRun textRun = RenderBlock::constructTextRun(this, font, m_text, style());
1270
GraphicsContextStateSaver stateSaver(*context, false);
1271
if (!style()->isHorizontalWritingMode()) {
1272
marker.moveBy(roundedIntPoint(-boxOrigin));
1273
marker = marker.transposedRect();
1274
marker.moveBy(IntPoint(roundToInt(box.x()), roundToInt(box.y() - logicalHeight())));
1276
context->translate(marker.x(), marker.maxY());
1277
context->rotate(static_cast<float>(deg2rad(90.)));
1278
context->translate(-marker.x(), -marker.maxY());
1281
IntPoint textOrigin = IntPoint(marker.x(), marker.y() + style()->fontMetrics().ascent());
1283
if (type == Asterisks || type == Footnotes)
1284
context->drawText(font, textRun, textOrigin);
1286
// Text is not arbitrary. We can judge whether it's RTL from the first character,
1287
// and we only need to handle the direction RightToLeft for now.
1288
bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
1289
StringBuilder reversedText;
1290
if (textNeedsReversing) {
1291
int length = m_text.length();
1292
reversedText.reserveCapacity(length);
1293
for (int i = length - 1; i >= 0; --i)
1294
reversedText.append(m_text[i]);
1295
textRun.setText(reversedText.characters(), length);
1298
const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1299
if (style()->isLeftToRightDirection()) {
1300
int width = font.width(textRun);
1301
context->drawText(font, textRun, textOrigin);
1302
UChar suffixSpace[2] = { suffix, ' ' };
1303
context->drawText(font, RenderBlock::constructTextRun(this, font, suffixSpace, 2, style()), textOrigin + IntSize(width, 0));
1305
UChar spaceSuffix[2] = { ' ', suffix };
1306
TextRun spaceSuffixRun = RenderBlock::constructTextRun(this, font, spaceSuffix, 2, style());
1307
int width = font.width(spaceSuffixRun);
1308
context->drawText(font, spaceSuffixRun, textOrigin);
1309
context->drawText(font, textRun, textOrigin + IntSize(width, 0));
1314
void RenderListMarker::layout()
1316
StackStats::LayoutCheckPoint layoutCheckPoint;
1317
ASSERT(needsLayout());
1320
setWidth(m_image->imageSize(this, style()->effectiveZoom()).width());
1321
setHeight(m_image->imageSize(this, style()->effectiveZoom()).height());
1323
setLogicalWidth(minPreferredLogicalWidth());
1324
setLogicalHeight(style()->fontMetrics().height());
1330
Length startMargin = style()->marginStart();
1331
Length endMargin = style()->marginEnd();
1332
if (startMargin.isFixed())
1333
setMarginStart(startMargin.value());
1334
if (endMargin.isFixed())
1335
setMarginEnd(endMargin.value());
1337
setNeedsLayout(false);
1340
void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
1342
// A list marker can't have a background or border image, so no need to call the base class method.
1343
if (o != m_image->data())
1346
if (width() != m_image->imageSize(this, style()->effectiveZoom()).width() || height() != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred())
1347
setNeedsLayoutAndPrefWidthsRecalc();
1352
void RenderListMarker::computePreferredLogicalWidths()
1354
ASSERT(preferredLogicalWidthsDirty());
1358
const Font& font = style()->font();
1359
const FontMetrics& fontMetrics = font.fontMetrics();
1362
// FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful
1363
// until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box.
1364
int bulletWidth = fontMetrics.ascent() / 2;
1365
m_image->setContainerSizeForRenderer(this, IntSize(bulletWidth, bulletWidth), style()->effectiveZoom());
1366
LayoutSize imageSize = m_image->imageSize(this, style()->effectiveZoom());
1367
m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style()->isHorizontalWritingMode() ? imageSize.width() : imageSize.height();
1368
setPreferredLogicalWidthsDirty(false);
1373
LayoutUnit logicalWidth = 0;
1374
EListStyleType type = style()->listStyleType();
1380
m_text = listMarkerText(type, m_listItem->value());
1381
logicalWidth = font.width(m_text); // no suffix for these types
1386
m_text = listMarkerText(type, 0); // value is ignored for these types
1387
logicalWidth = (fontMetrics.ascent() * 2 / 3 + 1) / 2 + 2;
1391
case AmharicAbegede:
1394
case BinaryListStyle:
1397
case CJKIdeographic:
1398
case CjkEarthlyBranch:
1399
case CjkHeavenlyStem:
1400
case DecimalLeadingZero:
1401
case DecimalListStyle:
1404
case EthiopicAbegede:
1405
case EthiopicAbegedeAmEt:
1406
case EthiopicAbegedeGez:
1407
case EthiopicAbegedeTiEr:
1408
case EthiopicAbegedeTiEt:
1409
case EthiopicHalehameAaEr:
1410
case EthiopicHalehameAaEt:
1411
case EthiopicHalehameAmEt:
1412
case EthiopicHalehameGez:
1413
case EthiopicHalehameOmEt:
1414
case EthiopicHalehameSidEt:
1415
case EthiopicHalehameSoEt:
1416
case EthiopicHalehameTiEr:
1417
case EthiopicHalehameTiEt:
1418
case EthiopicHalehameTig:
1423
case HangulConsonant:
1435
case LowerHexadecimal:
1437
case LowerNorwegian:
1453
case TigrinyaErAbegede:
1455
case TigrinyaEtAbegede:
1459
case UpperHexadecimal:
1461
case UpperNorwegian:
1464
m_text = listMarkerText(type, m_listItem->value());
1465
if (m_text.isEmpty())
1468
LayoutUnit itemWidth = font.width(m_text);
1469
UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1470
LayoutUnit suffixSpaceWidth = font.width(RenderBlock::constructTextRun(this, font, suffixSpace, 2, style()));
1471
logicalWidth = itemWidth + suffixSpaceWidth;
1476
m_minPreferredLogicalWidth = logicalWidth;
1477
m_maxPreferredLogicalWidth = logicalWidth;
1479
setPreferredLogicalWidthsDirty(false);
1484
void RenderListMarker::updateMargins()
1486
const FontMetrics& fontMetrics = style()->fontMetrics();
1488
LayoutUnit marginStart = 0;
1489
LayoutUnit marginEnd = 0;
1493
marginEnd = cMarkerPadding;
1494
else switch (style()->listStyleType()) {
1499
marginEnd = fontMetrics.ascent() - minPreferredLogicalWidth() + 1;
1505
if (style()->isLeftToRightDirection()) {
1507
marginStart = -minPreferredLogicalWidth() - cMarkerPadding;
1509
int offset = fontMetrics.ascent() * 2 / 3;
1510
switch (style()->listStyleType()) {
1514
marginStart = -offset - cMarkerPadding - 1;
1519
marginStart = m_text.isEmpty() ? LayoutUnit() : -minPreferredLogicalWidth() - offset / 2;
1522
marginEnd = -marginStart - minPreferredLogicalWidth();
1525
marginEnd = cMarkerPadding;
1527
int offset = fontMetrics.ascent() * 2 / 3;
1528
switch (style()->listStyleType()) {
1532
marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth();
1537
marginEnd = m_text.isEmpty() ? 0 : offset / 2;
1540
marginStart = -marginEnd - minPreferredLogicalWidth();
1545
style()->setMarginStart(Length(marginStart, Fixed));
1546
style()->setMarginEnd(Length(marginEnd, Fixed));
1549
LayoutUnit RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1552
return m_listItem->lineHeight(firstLine, direction, PositionOfInteriorLineBoxes);
1553
return RenderBox::lineHeight(firstLine, direction, linePositionMode);
1556
int RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1559
return m_listItem->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
1560
return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1563
String RenderListMarker::suffix() const
1565
EListStyleType type = style()->listStyleType();
1566
const UChar suffix = listMarkerSuffix(type, m_listItem->value());
1571
// If the suffix is not ' ', an extra space is needed
1573
if (style()->isLeftToRightDirection()) {
1581
return String(data, 2);
1584
bool RenderListMarker::isInside() const
1586
return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
1589
IntRect RenderListMarker::getRelativeMarkerRect()
1592
return IntRect(0, 0, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
1594
IntRect relativeRect;
1595
EListStyleType type = style()->listStyleType();
1599
const Font& font = style()->font();
1600
relativeRect = IntRect(0, 0, font.width(m_text), font.fontMetrics().height());
1606
// FIXME: Are these particular rounding rules necessary?
1607
const FontMetrics& fontMetrics = style()->fontMetrics();
1608
int ascent = fontMetrics.ascent();
1609
int bulletWidth = (ascent * 2 / 3 + 1) / 2;
1610
relativeRect = IntRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
1617
case AmharicAbegede:
1620
case BinaryListStyle:
1623
case CJKIdeographic:
1624
case CjkEarthlyBranch:
1625
case CjkHeavenlyStem:
1626
case DecimalLeadingZero:
1627
case DecimalListStyle:
1630
case EthiopicAbegede:
1631
case EthiopicAbegedeAmEt:
1632
case EthiopicAbegedeGez:
1633
case EthiopicAbegedeTiEr:
1634
case EthiopicAbegedeTiEt:
1635
case EthiopicHalehameAaEr:
1636
case EthiopicHalehameAaEt:
1637
case EthiopicHalehameAmEt:
1638
case EthiopicHalehameGez:
1639
case EthiopicHalehameOmEt:
1640
case EthiopicHalehameSidEt:
1641
case EthiopicHalehameSoEt:
1642
case EthiopicHalehameTiEr:
1643
case EthiopicHalehameTiEt:
1644
case EthiopicHalehameTig:
1649
case HangulConsonant:
1661
case LowerHexadecimal:
1663
case LowerNorwegian:
1679
case TigrinyaErAbegede:
1681
case TigrinyaEtAbegede:
1685
case UpperHexadecimal:
1687
case UpperNorwegian:
1690
if (m_text.isEmpty())
1692
const Font& font = style()->font();
1693
int itemWidth = font.width(m_text);
1694
UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
1695
int suffixSpaceWidth = font.width(RenderBlock::constructTextRun(this, font, suffixSpace, 2, style()));
1696
relativeRect = IntRect(0, 0, itemWidth + suffixSpaceWidth, font.fontMetrics().height());
1699
if (!style()->isHorizontalWritingMode()) {
1700
relativeRect = relativeRect.transposedRect();
1701
relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
1704
return relativeRect;
1707
void RenderListMarker::setSelectionState(SelectionState state)
1709
// The selection state for our containing block hierarchy is updated by the base class call.
1710
RenderBox::setSelectionState(state);
1712
if (m_inlineBoxWrapper && canUpdateSelectionOnRootLineBoxes())
1713
if (RootInlineBox* root = m_inlineBoxWrapper->root())
1714
root->setHasSelectedChildren(state != SelectionNone);
1717
LayoutRect RenderListMarker::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent)
1719
ASSERT(!needsLayout());
1721
if (selectionState() == SelectionNone || !inlineBoxWrapper())
1722
return LayoutRect();
1724
RootInlineBox* root = inlineBoxWrapper()->root();
1725
LayoutRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight());
1727
if (clipToVisibleContent)
1728
computeRectForRepaint(repaintContainer, rect);
1730
rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1735
} // namespace WebCore