1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. 5 * Copyright (C) 2006 Andrew Wellington (proton (at) wiretapped.net) 6 * Copyright (C) 2010 Daniel Bates (dbates (at) intudata.com) 7 * 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. 12 * 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. 17 * 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. 22 * 23 */ 24 25 #include "config.h" 26 #include "RenderListMarker.h" 27 28 #include "CachedImage.h" 29 #include "CharacterNames.h" 30 #include "Document.h" 31 #include "GraphicsContext.h" 32 #include "RenderLayer.h" 33 #include "RenderListItem.h" 34 #include "RenderView.h" 35 36 using namespace std; 37 using namespace WTF; 38 using namespace Unicode; 39 40 namespace WebCore { 41 42 const int cMarkerPadding = 7; 43 44 enum SequenceType { NumericSequence, AlphabeticSequence }; 45 46 static String toRoman(int number, bool upper) 47 { 48 // FIXME: CSS3 describes how to make this work for much larger numbers, 49 // using overbars and special characters. It also specifies the characters 50 // in the range U+2160 to U+217F instead of standard ASCII ones. 51 if (number < 1 || number > 3999) 52 return String::number(number); 53 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]; 58 59 int length = 0; 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; 63 int d = 0; 64 do { 65 int num = number % 10; 66 if (num % 5 < 4) 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]; 71 if (num == 9) 72 letters[lettersSize - ++length] = digits[d + 2]; 73 if (num % 5 == 4) 74 letters[lettersSize - ++length] = digits[d]; 75 number /= 10; 76 d += 2; 77 } while (number); 78 79 ASSERT(length <= lettersSize); 80 return String(&letters[lettersSize - length], length); 81 } 82 83 static inline String toAlphabeticOrNumeric(int number, const UChar* sequence, int sequenceSize, SequenceType type) 84 { 85 ASSERT(sequenceSize >= 2); 86 87 const int lettersSize = sizeof(number) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign. 88 89 UChar letters[lettersSize]; 90 91 bool isNegativeNumber = false; 92 unsigned numberShadow = number; 93 if (type == AlphabeticSequence) { 94 ASSERT(number > 0); 95 --numberShadow; 96 } else if (number < 0) { 97 numberShadow = -number; 98 isNegativeNumber = true; 99 } 100 letters[lettersSize - 1] = sequence[numberShadow % sequenceSize]; 101 int length = 1; 102 103 if (type == AlphabeticSequence) { 104 while ((numberShadow /= sequenceSize) > 0) 105 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize - 1]; 106 } else { 107 while ((numberShadow /= sequenceSize) > 0) 108 letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize]; 109 } 110 if (isNegativeNumber) 111 letters[lettersSize - ++length] = hyphenMinus; 112 113 ASSERT(length <= lettersSize); 114 return String(&letters[lettersSize - length], length); 115 } 116 117 static String toAlphabetic(int number, const UChar* alphabet, int alphabetSize) 118 { 119 if (number < 1) 120 return String::number(number); 121 122 return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence); 123 } 124 125 static String toNumeric(int number, const UChar* numerals, int numeralsSize) 126 { 127 return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence); 128 } 129 130 template <size_t size> static inline String toAlphabetic(int number, const UChar(&alphabet)[size]) 131 { 132 return toAlphabetic(number, alphabet, size); 133 } 134 135 template <size_t size> static inline String toNumeric(int number, const UChar(&alphabet)[size]) 136 { 137 return toNumeric(number, alphabet, size); 138 } 139 140 static int toHebrewUnder1000(int number, UChar letters[5]) 141 { 142 // FIXME: CSS3 mentions various refinements not implemented here. 143 // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame). 144 ASSERT(number >= 0 && number < 1000); 145 int length = 0; 146 int fourHundreds = number / 400; 147 for (int i = 0; i < fourHundreds; i++) 148 letters[length++] = 1511 + 3; 149 number %= 400; 150 if (number / 100) 151 letters[length++] = 1511 + (number / 100) - 1; 152 number %= 100; 153 if (number == 15 || number == 16) { 154 letters[length++] = 1487 + 9; 155 letters[length++] = 1487 + number - 9; 156 } else { 157 if (int tens = number / 10) { 158 static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 }; 159 letters[length++] = hebrewTens[tens - 1]; 160 } 161 if (int ones = number % 10) 162 letters[length++] = 1487 + ones; 163 } 164 ASSERT(length <= 5); 165 return length; 166 } 167 168 static String toHebrew(int number) 169 { 170 // FIXME: CSS3 mentions ways to make this work for much larger numbers. 171 if (number < 0 || number > 999999) 172 return String::number(number); 173 174 if (number == 0) { 175 static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 }; 176 return String(hebrewZero, 3); 177 } 178 179 const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between 180 UChar letters[lettersSize]; 181 182 int length; 183 if (number < 1000) 184 length = 0; 185 else { 186 length = toHebrewUnder1000(number / 1000, letters); 187 letters[length++] = '\''; 188 number = number % 1000; 189 } 190 length += toHebrewUnder1000(number, letters + length); 191 192 ASSERT(length <= lettersSize); 193 return String(letters, length); 194 } 195 196 static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9]) 197 { 198 ASSERT(number >= 0 && number < 10000); 199 int length = 0; 200 201 int lowerOffset = upper ? 0 : 0x0030; 202 203 if (int thousands = number / 1000) { 204 if (thousands == 7) { 205 letters[length++] = 0x0548 + lowerOffset; 206 letters[length++] = 0x0552 + lowerOffset; 207 if (addCircumflex) 208 letters[length++] = 0x0302; 209 } else { 210 letters[length++] = (0x054C - 1 + lowerOffset) + thousands; 211 if (addCircumflex) 212 letters[length++] = 0x0302; 213 } 214 } 215 216 if (int hundreds = (number / 100) % 10) { 217 letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds; 218 if (addCircumflex) 219 letters[length++] = 0x0302; 220 } 221 222 if (int tens = (number / 10) % 10) { 223 letters[length++] = (0x053A - 1 + lowerOffset) + tens; 224 if (addCircumflex) 225 letters[length++] = 0x0302; 226 } 227 228 if (int ones = number % 10) { 229 letters[length++] = (0x531 - 1 + lowerOffset) + ones; 230 if (addCircumflex) 231 letters[length++] = 0x0302; 232 } 233 234 return length; 235 } 236 237 static String toArmenian(int number, bool upper) 238 { 239 if (number < 1 || number > 99999999) 240 return String::number(number); 241 242 const int lettersSize = 18; // twice what toArmenianUnder10000 needs 243 UChar letters[lettersSize]; 244 245 int length = toArmenianUnder10000(number / 10000, upper, true, letters); 246 length += toArmenianUnder10000(number % 10000, upper, false, letters + length); 247 248 ASSERT(length <= lettersSize); 249 return String(letters, length); 250 } 251 252 static String toGeorgian(int number) 253 { 254 if (number < 1 || number > 19999) 255 return String::number(number); 256 257 const int lettersSize = 5; 258 UChar letters[lettersSize]; 259 260 int length = 0; 261 262 if (number > 9999) 263 letters[length++] = 0x10F5; 264 265 if (int thousands = (number / 1000) % 10) { 266 static const UChar georgianThousands[9] = { 267 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0 268 }; 269 letters[length++] = georgianThousands[thousands - 1]; 270 } 271 272 if (int hundreds = (number / 100) % 10) { 273 static const UChar georgianHundreds[9] = { 274 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8 275 }; 276 letters[length++] = georgianHundreds[hundreds - 1]; 277 } 278 279 if (int tens = (number / 10) % 10) { 280 static const UChar georgianTens[9] = { 281 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF 282 }; 283 letters[length++] = georgianTens[tens - 1]; 284 } 285 286 if (int ones = number % 10) { 287 static const UChar georgianOnes[9] = { 288 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7 289 }; 290 letters[length++] = georgianOnes[ones - 1]; 291 } 292 293 ASSERT(length <= lettersSize); 294 return String(letters, length); 295 } 296 297 // The table uses the order from the CSS3 specification: 298 // first 3 group markers, then 3 digit markers, then ten digits. 299 static String toCJKIdeographic(int number, const UChar table[16]) 300 { 301 if (number < 0) 302 return String::number(number); 303 304 enum AbstractCJKChar { 305 noChar, 306 secondGroupMarker, thirdGroupMarker, fourthGroupMarker, 307 secondDigitMarker, thirdDigitMarker, fourthDigitMarker, 308 digit0, digit1, digit2, digit3, digit4, 309 digit5, digit6, digit7, digit8, digit9 310 }; 311 312 if (number == 0) 313 return String(&table[digit0 - 1], 1); 314 315 const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker 316 const int bufferLength = 4 * groupLength; 317 AbstractCJKChar buffer[bufferLength] = { noChar }; 318 319 for (int i = 0; i < 4; ++i) { 320 int groupValue = number % 10000; 321 number /= 10000; 322 323 // Process least-significant group first, but put it in the buffer last. 324 AbstractCJKChar* group = &buffer[(3 - i) * groupLength]; 325 326 if (groupValue && i) 327 group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i); 328 329 // Put in the four digits and digit markers for any non-zero digits. 330 group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10)); 331 if (number != 0 || groupValue > 9) { 332 int digitValue = ((groupValue / 10) % 10); 333 group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue); 334 if (digitValue) 335 group[5] = secondDigitMarker; 336 } 337 if (number != 0 || groupValue > 99) { 338 int digitValue = ((groupValue / 100) % 10); 339 group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue); 340 if (digitValue) 341 group[3] = thirdDigitMarker; 342 } 343 if (number != 0 || groupValue > 999) { 344 int digitValue = groupValue / 1000; 345 group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue); 346 if (digitValue) 347 group[1] = fourthDigitMarker; 348 } 349 350 // Remove the tens digit, but leave the marker, for any group that has 351 // a value of less than 20. 352 if (groupValue < 20) { 353 ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1); 354 group[4] = noChar; 355 } 356 357 if (number == 0) 358 break; 359 } 360 361 // Convert into characters, omitting consecutive runs of digit0 and 362 // any trailing digit0. 363 int length = 0; 364 UChar characters[bufferLength]; 365 AbstractCJKChar last = noChar; 366 for (int i = 0; i < bufferLength; ++i) { 367 AbstractCJKChar a = buffer[i]; 368 if (a != noChar) { 369 if (a != digit0 || last != digit0) 370 characters[length++] = table[a - 1]; 371 last = a; 372 } 373 } 374 if (last == digit0) 375 --length; 376 377 return String(characters, length); 378 } 379 380 static UChar listMarkerSuffix(EListStyleType type) 381 { 382 // Note, the following switch statement has been explicitly 383 // grouped by list-style-type suffix. 384 switch (type) { 385 case NoneListStyle: 386 case Disc: 387 case Circle: 388 case Square: 389 ASSERT_NOT_REACHED(); 390 return ' '; 391 case Afar: 392 case Amharic: 393 case AmharicAbegede: 394 case Ethiopic: 395 case EthiopicAbegede: 396 case EthiopicAbegedeAmEt: 397 case EthiopicAbegedeGez: 398 case EthiopicAbegedeTiEr: 399 case EthiopicAbegedeTiEt: 400 case EthiopicHalehameAaEr: 401 case EthiopicHalehameAaEt: 402 case EthiopicHalehameAmEt: 403 case EthiopicHalehameGez: 404 case EthiopicHalehameOmEt: 405 case EthiopicHalehameSidEt: 406 case EthiopicHalehameSoEt: 407 case EthiopicHalehameTiEr: 408 case EthiopicHalehameTiEt: 409 case EthiopicHalehameTig: 410 case Oromo: 411 case Sidama: 412 case Somali: 413 case Tigre: 414 case TigrinyaEr: 415 case TigrinyaErAbegede: 416 case TigrinyaEt: 417 case TigrinyaEtAbegede: 418 return ethiopicPrefaceColon; 419 case Armenian: 420 case ArabicIndic: 421 case Bengali: 422 case BinaryListStyle: 423 case Cambodian: 424 case CJKIdeographic: 425 case CjkEarthlyBranch: 426 case CjkHeavenlyStem: 427 case DecimalLeadingZero: 428 case DecimalListStyle: 429 case Devanagari: 430 case Georgian: 431 case Gujarati: 432 case Gurmukhi: 433 case Hangul: 434 case HangulConsonant: 435 case Hebrew: 436 case Hiragana: 437 case HiraganaIroha: 438 case Kannada: 439 case Katakana: 440 case KatakanaIroha: 441 case Khmer: 442 case Lao: 443 case LowerAlpha: 444 case LowerGreek: 445 case LowerHexadecimal: 446 case LowerLatin: 447 case LowerNorwegian: 448 case LowerRoman: 449 case Malayalam: 450 case Mongolian: 451 case Myanmar: 452 case Octal: 453 case Oriya: 454 case Persian: 455 case Telugu: 456 case Thai: 457 case Tibetan: 458 case UpperAlpha: 459 case UpperGreek: 460 case UpperHexadecimal: 461 case UpperLatin: 462 case UpperNorwegian: 463 case UpperRoman: 464 case Urdu: 465 return '.'; 466 } 467 468 ASSERT_NOT_REACHED(); 469 return '.'; 470 } 471 472 String listMarkerText(EListStyleType type, int value) 473 { 474 switch (type) { 475 case NoneListStyle: 476 return ""; 477 478 // We use the same characters for text security. 479 // See RenderText::setInternalString. 480 case Circle: 481 return String(&whiteBullet, 1); 482 case Disc: 483 return String(&bullet, 1); 484 case Square: 485 // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE 486 // instead, but I think this looks better. 487 return String(&blackSquare, 1); 488 489 case DecimalListStyle: 490 return String::number(value); 491 case DecimalLeadingZero: 492 if (value < -9 || value > 9) 493 return String::number(value); 494 if (value < 0) 495 return "-0" + String::number(-value); // -01 to -09 496 return "0" + String::number(value); // 00 to 09 497 498 case ArabicIndic: { 499 static const UChar arabicIndicNumerals[10] = { 500 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669 501 }; 502 return toNumeric(value, arabicIndicNumerals); 503 } 504 case BinaryListStyle: { 505 static const UChar binaryNumerals[2] = { 506 '0', '1' 507 }; 508 return toNumeric(value, binaryNumerals); 509 } 510 case Bengali: { 511 static const UChar bengaliNumerals[10] = { 512 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF 513 }; 514 return toNumeric(value, bengaliNumerals); 515 } 516 case Cambodian: 517 case Khmer: { 518 static const UChar khmerNumerals[10] = { 519 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9 520 }; 521 return toNumeric(value, khmerNumerals); 522 } 523 case Devanagari: { 524 static const UChar devanagariNumerals[10] = { 525 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F 526 }; 527 return toNumeric(value, devanagariNumerals); 528 } 529 case Gujarati: { 530 static const UChar gujaratiNumerals[10] = { 531 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF 532 }; 533 return toNumeric(value, gujaratiNumerals); 534 } 535 case Gurmukhi: { 536 static const UChar gurmukhiNumerals[10] = { 537 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F 538 }; 539 return toNumeric(value, gurmukhiNumerals); 540 } 541 case Kannada: { 542 static const UChar kannadaNumerals[10] = { 543 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF 544 }; 545 return toNumeric(value, kannadaNumerals); 546 } 547 case LowerHexadecimal: { 548 static const UChar lowerHexadecimalNumerals[16] = { 549 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 550 }; 551 return toNumeric(value, lowerHexadecimalNumerals); 552 } 553 case Lao: { 554 static const UChar laoNumerals[10] = { 555 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9 556 }; 557 return toNumeric(value, laoNumerals); 558 } 559 case Malayalam: { 560 static const UChar malayalamNumerals[10] = { 561 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F 562 }; 563 return toNumeric(value, malayalamNumerals); 564 } 565 case Mongolian: { 566 static const UChar mongolianNumerals[10] = { 567 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819 568 }; 569 return toNumeric(value, mongolianNumerals); 570 } 571 case Myanmar: { 572 static const UChar myanmarNumerals[10] = { 573 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049 574 }; 575 return toNumeric(value, myanmarNumerals); 576 } 577 case Octal: { 578 static const UChar octalNumerals[8] = { 579 '0', '1', '2', '3', '4', '5', '6', '7' 580 }; 581 return toNumeric(value, octalNumerals); 582 } 583 case Oriya: { 584 static const UChar oriyaNumerals[10] = { 585 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F 586 }; 587 return toNumeric(value, oriyaNumerals); 588 } 589 case Persian: 590 case Urdu: { 591 static const UChar urduNumerals[10] = { 592 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9 593 }; 594 return toNumeric(value, urduNumerals); 595 } 596 case Telugu: { 597 static const UChar teluguNumerals[10] = { 598 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F 599 }; 600 return toNumeric(value, teluguNumerals); 601 } 602 case Tibetan: { 603 static const UChar tibetanNumerals[10] = { 604 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29 605 }; 606 return toNumeric(value, tibetanNumerals); 607 } 608 case Thai: { 609 static const UChar thaiNumerals[10] = { 610 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59 611 }; 612 return toNumeric(value, thaiNumerals); 613 } 614 case UpperHexadecimal: { 615 static const UChar upperHexadecimalNumerals[16] = { 616 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 617 }; 618 return toNumeric(value, upperHexadecimalNumerals); 619 } 620 621 case LowerAlpha: 622 case LowerLatin: { 623 static const UChar lowerLatinAlphabet[26] = { 624 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 625 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' 626 }; 627 return toAlphabetic(value, lowerLatinAlphabet); 628 } 629 case UpperAlpha: 630 case UpperLatin: { 631 static const UChar upperLatinAlphabet[26] = { 632 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 633 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' 634 }; 635 return toAlphabetic(value, upperLatinAlphabet); 636 } 637 case LowerGreek: { 638 static const UChar lowerGreekAlphabet[24] = { 639 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 640 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 641 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9 642 }; 643 return toAlphabetic(value, lowerGreekAlphabet); 644 } 645 646 case Hiragana: { 647 // FIXME: This table comes from the CSS3 draft, and is probably 648 // incorrect, given the comments in that draft. 649 static const UChar hiraganaAlphabet[48] = { 650 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F, 651 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F, 652 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D, 653 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F, 654 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A, 655 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093 656 }; 657 return toAlphabetic(value, hiraganaAlphabet); 658 } 659 case HiraganaIroha: { 660 // FIXME: This table comes from the CSS3 draft, and is probably 661 // incorrect, given the comments in that draft. 662 static const UChar hiraganaIrohaAlphabet[47] = { 663 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061, 664 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F, 665 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046, 666 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075, 667 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081, 668 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059 669 }; 670 return toAlphabetic(value, hiraganaIrohaAlphabet); 671 } 672 case Katakana: { 673 // FIXME: This table comes from the CSS3 draft, and is probably 674 // incorrect, given the comments in that draft. 675 static const UChar katakanaAlphabet[48] = { 676 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF, 677 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF, 678 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 679 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF, 680 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA, 681 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3 682 }; 683 return toAlphabetic(value, katakanaAlphabet); 684 } 685 case KatakanaIroha: { 686 // FIXME: This table comes from the CSS3 draft, and is probably 687 // incorrect, given the comments in that draft. 688 static const UChar katakanaIrohaAlphabet[47] = { 689 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1, 690 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF, 691 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6, 692 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5, 693 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1, 694 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9 695 }; 696 return toAlphabetic(value, katakanaIrohaAlphabet); 697 } 698 699 case Afar: 700 case EthiopicHalehameAaEt: 701 case EthiopicHalehameAaEr: { 702 static const UChar ethiopicHalehameAaErAlphabet[18] = { 703 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290, 704 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348 705 }; 706 return toAlphabetic(value, ethiopicHalehameAaErAlphabet); 707 } 708 case Amharic: 709 case EthiopicHalehameAmEt: { 710 static const UChar ethiopicHalehameAmEtAlphabet[33] = { 711 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240, 712 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 713 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 714 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350 715 }; 716 return toAlphabetic(value, ethiopicHalehameAmEtAlphabet); 717 } 718 case AmharicAbegede: 719 case EthiopicAbegedeAmEt: { 720 static const UChar ethiopicAbegedeAmEtAlphabet[33] = { 721 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0, 722 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290, 723 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238, 724 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350 725 }; 726 return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet); 727 } 728 case CjkEarthlyBranch: { 729 static const UChar cjkEarthlyBranchAlphabet[12] = { 730 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533, 731 0x9149, 0x620C, 0x4EA5 732 }; 733 return toAlphabetic(value, cjkEarthlyBranchAlphabet); 734 } 735 case CjkHeavenlyStem: { 736 static const UChar cjkHeavenlyStemAlphabet[10] = { 737 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC, 738 0x7678 739 }; 740 return toAlphabetic(value, cjkHeavenlyStemAlphabet); 741 } 742 case Ethiopic: 743 case EthiopicHalehameGez: { 744 static const UChar ethiopicHalehameGezAlphabet[26] = { 745 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260, 746 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8, 747 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350 748 }; 749 return toAlphabetic(value, ethiopicHalehameGezAlphabet); 750 } 751 case EthiopicAbegede: 752 case EthiopicAbegedeGez: { 753 static const UChar ethiopicAbegedeGezAlphabet[26] = { 754 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320, 755 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338, 756 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350 757 }; 758 return toAlphabetic(value, ethiopicAbegedeGezAlphabet); 759 } 760 case HangulConsonant: { 761 static const UChar hangulConsonantAlphabet[14] = { 762 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148, 763 0x314A, 0x314B, 0x314C, 0x314D, 0x314E 764 }; 765 return toAlphabetic(value, hangulConsonantAlphabet); 766 } 767 case Hangul: { 768 static const UChar hangulAlphabet[14] = { 769 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790, 770 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558 771 }; 772 return toAlphabetic(value, hangulAlphabet); 773 } 774 case Oromo: 775 case EthiopicHalehameOmEt: { 776 static const UChar ethiopicHalehameOmEtAlphabet[25] = { 777 0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270, 778 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8, 779 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348 780 }; 781 return toAlphabetic(value, ethiopicHalehameOmEtAlphabet); 782 } 783 case Sidama: 784 case EthiopicHalehameSidEt: { 785 static const UChar ethiopicHalehameSidEtAlphabet[26] = { 786 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 787 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 788 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348 789 }; 790 return toAlphabetic(value, ethiopicHalehameSidEtAlphabet); 791 } 792 case Somali: 793 case EthiopicHalehameSoEt: { 794 static const UChar ethiopicHalehameSoEtAlphabet[22] = { 795 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 796 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 797 0x1300, 0x1308, 0x1338, 0x1348 798 }; 799 return toAlphabetic(value, ethiopicHalehameSoEtAlphabet); 800 } 801 case Tigre: 802 case EthiopicHalehameTig: { 803 static const UChar ethiopicHalehameTigAlphabet[27] = { 804 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 805 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8, 806 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350 807 }; 808 return toAlphabetic(value, ethiopicHalehameTigAlphabet); 809 } 810 case TigrinyaEr: 811 case EthiopicHalehameTiEr: { 812 static const UChar ethiopicHalehameTiErAlphabet[31] = { 813 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250, 814 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 815 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 816 0x1330, 0x1338, 0x1348, 0x1350 817 }; 818 return toAlphabetic(value, ethiopicHalehameTiErAlphabet); 819 } 820 case TigrinyaErAbegede: 821 case EthiopicAbegedeTiEr: { 822 static const UChar ethiopicAbegedeTiErAlphabet[31] = { 823 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0, 824 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290, 825 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238, 826 0x1270, 0x1278, 0x1330, 0x1350 827 }; 828 return toAlphabetic(value, ethiopicAbegedeTiErAlphabet); 829 } 830 case TigrinyaEt: 831 case EthiopicHalehameTiEt: { 832 static const UChar ethiopicHalehameTiEtAlphabet[34] = { 833 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240, 834 0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 835 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 836 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350 837 }; 838 return toAlphabetic(value, ethiopicHalehameTiEtAlphabet); 839 } 840 case TigrinyaEtAbegede: 841 case EthiopicAbegedeTiEt: { 842 static const UChar ethiopicAbegedeTiEtAlphabet[34] = { 843 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0, 844 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290, 845 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 846 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350 847 }; 848 return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet); 849 } 850 case UpperGreek: { 851 static const UChar upperGreekAlphabet[24] = { 852 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 853 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 854 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9 855 }; 856 return toAlphabetic(value, upperGreekAlphabet); 857 } 858 case LowerNorwegian: { 859 static const UChar lowerNorwegianAlphabet[29] = { 860 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 861 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 862 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E6, 863 0x00F8, 0x00E5 864 }; 865 return toAlphabetic(value, lowerNorwegianAlphabet); 866 } 867 case UpperNorwegian: { 868 static const UChar upperNorwegianAlphabet[29] = { 869 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 870 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 871 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00C6, 872 0x00D8, 0x00C5 873 }; 874 return toAlphabetic(value, upperNorwegianAlphabet); 875 } 876 case CJKIdeographic: { 877 static const UChar traditionalChineseInformalTable[16] = { 878 0x842C, 0x5104, 0x5146, 879 0x5341, 0x767E, 0x5343, 880 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB, 881 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D 882 }; 883 return toCJKIdeographic(value, traditionalChineseInformalTable); 884 } 885 886 case LowerRoman: 887 return toRoman(value, false); 888 case UpperRoman: 889 return toRoman(value, true); 890 891 case Armenian: 892 // CSS3 says "armenian" means "lower-armenian". 893 // But the CSS2.1 test suite contains uppercase test results for "armenian", 894 // so we'll match the test suite. 895 return toArmenian(value, true); 896 case Georgian: 897 return toGeorgian(value); 898 case Hebrew: 899 return toHebrew(value); 900 } 901 902 ASSERT_NOT_REACHED(); 903 return ""; 904 } 905 906 RenderListMarker::RenderListMarker(RenderListItem* item) 907 : RenderBox(item->document()) 908 , m_listItem(item) 909 { 910 // init RenderObject attributes 911 setInline(true); // our object is Inline 912 setReplaced(true); // pretend to be replaced 913 } 914 915 RenderListMarker::~RenderListMarker() 916 { 917 if (m_image) 918 m_image->removeClient(this); 919 } 920 921 void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) 922 { 923 if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType())) 924 setNeedsLayoutAndPrefWidthsRecalc(); 925 926 RenderBox::styleWillChange(diff, newStyle); 927 } 928 929 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 930 { 931 RenderBox::styleDidChange(diff, oldStyle); 932 933 if (m_image != style()->listStyleImage()) { 934 if (m_image) 935 m_image->removeClient(this); 936 m_image = style()->listStyleImage(); 937 if (m_image) 938 m_image->addClient(this); 939 } 940 } 941 942 InlineBox* RenderListMarker::createInlineBox() 943 { 944 InlineBox* result = RenderBox::createInlineBox(); 945 result->setIsText(isText()); 946 return result; 947 } 948 949 bool RenderListMarker::isImage() const 950 { 951 return m_image && !m_image->errorOccurred(); 952 } 953 954 IntRect RenderListMarker::localSelectionRect() 955 { 956 InlineBox* box = inlineBoxWrapper(); 957 if (!box) 958 return IntRect(); 959 RootInlineBox* root = box->root(); 960 return IntRect(x(), root->selectionTop() - y(), width(), root->selectionHeight()); 961 } 962 963 void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) 964 { 965 if (paintInfo.phase != PaintPhaseForeground) 966 return; 967 968 if (style()->visibility() != VISIBLE) 969 return; 970 971 IntRect marker = getRelativeMarkerRect(); 972 marker.move(tx, ty); 973 974 IntRect box(tx + x(), ty + y(), width(), height()); 975 976 if (box.y() > paintInfo.rect.bottom() || box.y() + box.height() < paintInfo.rect.y()) 977 return; 978 979 if (hasBoxDecorations()) 980 paintBoxDecorations(paintInfo, box.x(), box.y()); 981 982 GraphicsContext* context = paintInfo.context; 983 984 if (isImage()) { 985 #if PLATFORM(MAC) 986 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) 987 paintCustomHighlight(tx, ty, style()->highlight(), true); 988 #endif 989 context->drawImage(m_image->image(this, marker.size()), style()->colorSpace(), marker.location()); 990 if (selectionState() != SelectionNone) { 991 IntRect selRect = localSelectionRect(); 992 selRect.move(tx, ty); 993 context->fillRect(selRect, selectionBackgroundColor(), style()->colorSpace()); 994 } 995 return; 996 } 997 998 #if PLATFORM(MAC) 999 // FIXME: paint gap between marker and list item proper 1000 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) 1001 paintCustomHighlight(tx, ty, style()->highlight(), true); 1002 #endif 1003 1004 if (selectionState() != SelectionNone) { 1005 IntRect selRect = localSelectionRect(); 1006 selRect.move(tx, ty); 1007 context->fillRect(selRect, selectionBackgroundColor(), style()->colorSpace()); 1008 } 1009 1010 const Color color(style()->color()); 1011 context->setStrokeColor(color, style()->colorSpace()); 1012 context->setStrokeStyle(SolidStroke); 1013 context->setStrokeThickness(1.0f); 1014 context->setFillColor(color, style()->colorSpace()); 1015 1016 EListStyleType type = style()->listStyleType(); 1017 switch (type) { 1018 case Disc: 1019 context->drawEllipse(marker); 1020 return; 1021 case Circle: 1022 context->setFillColor(Color::transparent, DeviceColorSpace); 1023 context->drawEllipse(marker); 1024 return; 1025 case Square: 1026 context->drawRect(marker); 1027 return; 1028 case NoneListStyle: 1029 return; 1030 case Afar: 1031 case Amharic: 1032 case AmharicAbegede: 1033 case ArabicIndic: 1034 case Armenian: 1035 case BinaryListStyle: 1036 case Bengali: 1037 case Cambodian: 1038 case CJKIdeographic: 1039 case CjkEarthlyBranch: 1040 case CjkHeavenlyStem: 1041 case DecimalLeadingZero: 1042 case DecimalListStyle: 1043 case Devanagari: 1044 case Ethiopic: 1045 case EthiopicAbegede: 1046 case EthiopicAbegedeAmEt: 1047 case EthiopicAbegedeGez: 1048 case EthiopicAbegedeTiEr: 1049 case EthiopicAbegedeTiEt: 1050 case EthiopicHalehameAaEr: 1051 case EthiopicHalehameAaEt: 1052 case EthiopicHalehameAmEt: 1053 case EthiopicHalehameGez: 1054 case EthiopicHalehameOmEt: 1055 case EthiopicHalehameSidEt: 1056 case EthiopicHalehameSoEt: 1057 case EthiopicHalehameTiEr: 1058 case EthiopicHalehameTiEt: 1059 case EthiopicHalehameTig: 1060 case Georgian: 1061 case Gujarati: 1062 case Gurmukhi: 1063 case Hangul: 1064 case HangulConsonant: 1065 case Hebrew: 1066 case Hiragana: 1067 case HiraganaIroha: 1068 case Kannada: 1069 case Katakana: 1070 case KatakanaIroha: 1071 case Khmer: 1072 case Lao: 1073 case LowerAlpha: 1074 case LowerGreek: 1075 case LowerHexadecimal: 1076 case LowerLatin: 1077 case LowerNorwegian: 1078 case LowerRoman: 1079 case Malayalam: 1080 case Mongolian: 1081 case Myanmar: 1082 case Octal: 1083 case Oriya: 1084 case Oromo: 1085 case Persian: 1086 case Sidama: 1087 case Somali: 1088 case Telugu: 1089 case Thai: 1090 case Tibetan: 1091 case Tigre: 1092 case TigrinyaEr: 1093 case TigrinyaErAbegede: 1094 case TigrinyaEt: 1095 case TigrinyaEtAbegede: 1096 case UpperAlpha: 1097 case UpperGreek: 1098 case UpperHexadecimal: 1099 case UpperLatin: 1100 case UpperNorwegian: 1101 case UpperRoman: 1102 case Urdu: 1103 break; 1104 } 1105 if (m_text.isEmpty()) 1106 return; 1107 1108 TextRun textRun(m_text); 1109 1110 // Text is not arbitrary. We can judge whether it's RTL from the first character, 1111 // and we only need to handle the direction RightToLeft for now. 1112 bool textNeedsReversing = direction(m_text[0]) == RightToLeft; 1113 Vector<UChar> reversedText; 1114 if (textNeedsReversing) { 1115 int length = m_text.length(); 1116 reversedText.grow(length); 1117 for (int i = 0; i < length; ++i) 1118 reversedText[length - i - 1] = m_text[i]; 1119 textRun = TextRun(reversedText.data(), length); 1120 } 1121 1122 const Font& font = style()->font(); 1123 const UChar suffix = listMarkerSuffix(type); 1124 if (style()->direction() == LTR) { 1125 int width = font.width(textRun); 1126 context->drawText(style()->font(), textRun, marker.location()); 1127 UChar suffixSpace[2] = { suffix, ' ' }; 1128 context->drawText(style()->font(), TextRun(suffixSpace, 2), marker.location() + IntSize(width, 0)); 1129 } else { 1130 UChar spaceSuffix[2] = { ' ', suffix }; 1131 TextRun spaceSuffixRun(spaceSuffix, 2); 1132 int width = font.width(spaceSuffixRun); 1133 context->drawText(style()->font(), spaceSuffixRun, marker.location()); 1134 context->drawText(style()->font(), textRun, marker.location() + IntSize(width, 0)); 1135 } 1136 } 1137 1138 void RenderListMarker::layout() 1139 { 1140 ASSERT(needsLayout()); 1141 1142 if (isImage()) { 1143 setWidth(m_image->imageSize(this, style()->effectiveZoom()).width()); 1144 setHeight(m_image->imageSize(this, style()->effectiveZoom()).height()); 1145 } else { 1146 setWidth(minPrefWidth()); 1147 setHeight(style()->font().height()); 1148 } 1149 1150 m_marginLeft = m_marginRight = 0; 1151 1152 Length leftMargin = style()->marginLeft(); 1153 Length rightMargin = style()->marginRight(); 1154 if (leftMargin.isFixed()) 1155 m_marginLeft = leftMargin.value(); 1156 if (rightMargin.isFixed()) 1157 m_marginRight = rightMargin.value(); 1158 1159 setNeedsLayout(false); 1160 } 1161 1162 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*) 1163 { 1164 // A list marker can't have a background or border image, so no need to call the base class method. 1165 if (o != m_image->data()) 1166 return; 1167 1168 if (width() != m_image->imageSize(this, style()->effectiveZoom()).width() || height() != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred()) 1169 setNeedsLayoutAndPrefWidthsRecalc(); 1170 else 1171 repaint(); 1172 } 1173 1174 void RenderListMarker::calcPrefWidths() 1175 { 1176 ASSERT(prefWidthsDirty()); 1177 1178 m_text = ""; 1179 1180 const Font& font = style()->font(); 1181 1182 if (isImage()) { 1183 // FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful 1184 // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box. 1185 int bulletWidth = font.ascent() / 2; 1186 m_image->setImageContainerSize(IntSize(bulletWidth, bulletWidth)); 1187 m_minPrefWidth = m_maxPrefWidth = m_image->imageSize(this, style()->effectiveZoom()).width(); 1188 setPrefWidthsDirty(false); 1189 updateMargins(); 1190 return; 1191 } 1192 1193 int width = 0; 1194 EListStyleType type = style()->listStyleType(); 1195 switch (type) { 1196 case NoneListStyle: 1197 break; 1198 case Circle: 1199 case Disc: 1200 case Square: 1201 m_text = listMarkerText(type, 0); // value is ignored for these types 1202 width = (font.ascent() * 2 / 3 + 1) / 2 + 2; 1203 break; 1204 case Afar: 1205 case Amharic: 1206 case AmharicAbegede: 1207 case ArabicIndic: 1208 case Armenian: 1209 case BinaryListStyle: 1210 case Bengali: 1211 case Cambodian: 1212 case CJKIdeographic: 1213 case CjkEarthlyBranch: 1214 case CjkHeavenlyStem: 1215 case DecimalLeadingZero: 1216 case DecimalListStyle: 1217 case Devanagari: 1218 case Ethiopic: 1219 case EthiopicAbegede: 1220 case EthiopicAbegedeAmEt: 1221 case EthiopicAbegedeGez: 1222 case EthiopicAbegedeTiEr: 1223 case EthiopicAbegedeTiEt: 1224 case EthiopicHalehameAaEr: 1225 case EthiopicHalehameAaEt: 1226 case EthiopicHalehameAmEt: 1227 case EthiopicHalehameGez: 1228 case EthiopicHalehameOmEt: 1229 case EthiopicHalehameSidEt: 1230 case EthiopicHalehameSoEt: 1231 case EthiopicHalehameTiEr: 1232 case EthiopicHalehameTiEt: 1233 case EthiopicHalehameTig: 1234 case Georgian: 1235 case Gujarati: 1236 case Gurmukhi: 1237 case Hangul: 1238 case HangulConsonant: 1239 case Hebrew: 1240 case Hiragana: 1241 case HiraganaIroha: 1242 case Kannada: 1243 case Katakana: 1244 case KatakanaIroha: 1245 case Khmer: 1246 case Lao: 1247 case LowerAlpha: 1248 case LowerGreek: 1249 case LowerHexadecimal: 1250 case LowerLatin: 1251 case LowerNorwegian: 1252 case LowerRoman: 1253 case Malayalam: 1254 case Mongolian: 1255 case Myanmar: 1256 case Octal: 1257 case Oriya: 1258 case Oromo: 1259 case Persian: 1260 case Sidama: 1261 case Somali: 1262 case Telugu: 1263 case Thai: 1264 case Tibetan: 1265 case Tigre: 1266 case TigrinyaEr: 1267 case TigrinyaErAbegede: 1268 case TigrinyaEt: 1269 case TigrinyaEtAbegede: 1270 case UpperAlpha: 1271 case UpperGreek: 1272 case UpperHexadecimal: 1273 case UpperLatin: 1274 case UpperNorwegian: 1275 case UpperRoman: 1276 case Urdu: 1277 m_text = listMarkerText(type, m_listItem->value()); 1278 if (m_text.isEmpty()) 1279 width = 0; 1280 else { 1281 int itemWidth = font.width(m_text); 1282 UChar suffixSpace[2] = { listMarkerSuffix(type), ' ' }; 1283 int suffixSpaceWidth = font.width(TextRun(suffixSpace, 2)); 1284 width = itemWidth + suffixSpaceWidth; 1285 } 1286 break; 1287 } 1288 1289 m_minPrefWidth = width; 1290 m_maxPrefWidth = width; 1291 1292 setPrefWidthsDirty(false); 1293 1294 updateMargins(); 1295 } 1296 1297 void RenderListMarker::updateMargins() 1298 { 1299 const Font& font = style()->font(); 1300 1301 int marginLeft = 0; 1302 int marginRight = 0; 1303 1304 if (isInside()) { 1305 if (isImage()) { 1306 if (style()->direction() == LTR) 1307 marginRight = cMarkerPadding; 1308 else 1309 marginLeft = cMarkerPadding; 1310 } else switch (style()->listStyleType()) { 1311 case Disc: 1312 case Circle: 1313 case Square: 1314 if (style()->direction() == LTR) { 1315 marginLeft = -1; 1316 marginRight = font.ascent() - minPrefWidth() + 1; 1317 } else { 1318 marginLeft = font.ascent() - minPrefWidth() + 1; 1319 marginRight = -1; 1320 } 1321 break; 1322 default: 1323 break; 1324 } 1325 } else { 1326 if (style()->direction() == LTR) { 1327 if (isImage()) 1328 marginLeft = -minPrefWidth() - cMarkerPadding; 1329 else { 1330 int offset = font.ascent() * 2 / 3; 1331 switch (style()->listStyleType()) { 1332 case Disc: 1333 case Circle: 1334 case Square: 1335 marginLeft = -offset - cMarkerPadding - 1; 1336 break; 1337 case NoneListStyle: 1338 break; 1339 default: 1340 marginLeft = m_text.isEmpty() ? 0 : -minPrefWidth() - offset / 2; 1341 } 1342 } 1343 } else { 1344 if (isImage()) 1345 marginLeft = cMarkerPadding; 1346 else { 1347 int offset = font.ascent() * 2 / 3; 1348 switch (style()->listStyleType()) { 1349 case Disc: 1350 case Circle: 1351 case Square: 1352 marginLeft = offset + cMarkerPadding + 1 - minPrefWidth(); 1353 break; 1354 case NoneListStyle: 1355 break; 1356 default: 1357 marginLeft = m_text.isEmpty() ? 0 : offset / 2; 1358 } 1359 } 1360 } 1361 marginRight = -marginLeft - minPrefWidth(); 1362 } 1363 1364 style()->setMarginLeft(Length(marginLeft, Fixed)); 1365 style()->setMarginRight(Length(marginRight, Fixed)); 1366 } 1367 1368 int RenderListMarker::lineHeight(bool, bool) const 1369 { 1370 if (!isImage()) 1371 return m_listItem->lineHeight(false, true); 1372 return height(); 1373 } 1374 1375 int RenderListMarker::baselinePosition(bool, bool) const 1376 { 1377 if (!isImage()) { 1378 const Font& font = style()->font(); 1379 return font.ascent() + (lineHeight(false) - font.height())/2; 1380 } 1381 return height(); 1382 } 1383 1384 bool RenderListMarker::isInside() const 1385 { 1386 return m_listItem->notInList() || style()->listStylePosition() == INSIDE; 1387 } 1388 1389 IntRect RenderListMarker::getRelativeMarkerRect() 1390 { 1391 if (isImage()) 1392 return IntRect(x(), y(), m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height()); 1393 1394 EListStyleType type = style()->listStyleType(); 1395 switch (type) { 1396 case Disc: 1397 case Circle: 1398 case Square: { 1399 // FIXME: Are these particular rounding rules necessary? 1400 const Font& font = style()->font(); 1401 int ascent = font.ascent(); 1402 int bulletWidth = (ascent * 2 / 3 + 1) / 2; 1403 return IntRect(x() + 1, y() + 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth); 1404 } 1405 case NoneListStyle: 1406 return IntRect(); 1407 case Afar: 1408 case Amharic: 1409 case AmharicAbegede: 1410 case ArabicIndic: 1411 case Armenian: 1412 case BinaryListStyle: 1413 case Bengali: 1414 case Cambodian: 1415 case CJKIdeographic: 1416 case CjkEarthlyBranch: 1417 case CjkHeavenlyStem: 1418 case DecimalLeadingZero: 1419 case DecimalListStyle: 1420 case Devanagari: 1421 case Ethiopic: 1422 case EthiopicAbegede: 1423 case EthiopicAbegedeAmEt: 1424 case EthiopicAbegedeGez: 1425 case EthiopicAbegedeTiEr: 1426 case EthiopicAbegedeTiEt: 1427 case EthiopicHalehameAaEr: 1428 case EthiopicHalehameAaEt: 1429 case EthiopicHalehameAmEt: 1430 case EthiopicHalehameGez: 1431 case EthiopicHalehameOmEt: 1432 case EthiopicHalehameSidEt: 1433 case EthiopicHalehameSoEt: 1434 case EthiopicHalehameTiEr: 1435 case EthiopicHalehameTiEt: 1436 case EthiopicHalehameTig: 1437 case Georgian: 1438 case Gujarati: 1439 case Gurmukhi: 1440 case Hangul: 1441 case HangulConsonant: 1442 case Hebrew: 1443 case Hiragana: 1444 case HiraganaIroha: 1445 case Kannada: 1446 case Katakana: 1447 case KatakanaIroha: 1448 case Khmer: 1449 case Lao: 1450 case LowerAlpha: 1451 case LowerGreek: 1452 case LowerHexadecimal: 1453 case LowerLatin: 1454 case LowerNorwegian: 1455 case LowerRoman: 1456 case Malayalam: 1457 case Mongolian: 1458 case Myanmar: 1459 case Octal: 1460 case Oriya: 1461 case Oromo: 1462 case Persian: 1463 case Sidama: 1464 case Somali: 1465 case Telugu: 1466 case Thai: 1467 case Tibetan: 1468 case Tigre: 1469 case TigrinyaEr: 1470 case TigrinyaErAbegede: 1471 case TigrinyaEt: 1472 case TigrinyaEtAbegede: 1473 case UpperAlpha: 1474 case UpperGreek: 1475 case UpperHexadecimal: 1476 case UpperLatin: 1477 case UpperNorwegian: 1478 case UpperRoman: 1479 case Urdu: 1480 if (m_text.isEmpty()) 1481 return IntRect(); 1482 const Font& font = style()->font(); 1483 int itemWidth = font.width(m_text); 1484 UChar suffixSpace[2] = { listMarkerSuffix(type), ' ' }; 1485 int suffixSpaceWidth = font.width(TextRun(suffixSpace, 2)); 1486 return IntRect(x(), y() + font.ascent(), itemWidth + suffixSpaceWidth, font.height()); 1487 } 1488 1489 return IntRect(); 1490 } 1491 1492 void RenderListMarker::setSelectionState(SelectionState state) 1493 { 1494 RenderBox::setSelectionState(state); 1495 if (InlineBox* box = inlineBoxWrapper()) 1496 if (RootInlineBox* root = box->root()) 1497 root->setHasSelectedChildren(state != SelectionNone); 1498 containingBlock()->setSelectionState(state); 1499 } 1500 1501 IntRect RenderListMarker::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent) 1502 { 1503 ASSERT(!needsLayout()); 1504 1505 if (selectionState() == SelectionNone || !inlineBoxWrapper()) 1506 return IntRect(); 1507 1508 RootInlineBox* root = inlineBoxWrapper()->root(); 1509 IntRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight()); 1510 1511 if (clipToVisibleContent) 1512 computeRectForRepaint(repaintContainer, rect); 1513 else 1514 rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox(); 1515 1516 return rect; 1517 } 1518 1519 } // namespace WebCore 1520