1 /* 2 * Copyright (C) 2000 Lars Knoll (knoll (at) kde.org) 3 * Copyright (C) 2003, 2004, 2006, 2007, 2008 Apple Inc. All right reserved. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 * 20 */ 21 22 #ifndef BidiResolver_h 23 #define BidiResolver_h 24 25 #include "BidiContext.h" 26 #include "BidiRunList.h" 27 #include <wtf/Noncopyable.h> 28 #include <wtf/PassRefPtr.h> 29 #include <wtf/Vector.h> 30 31 namespace WebCore { 32 33 template <class Iterator> struct MidpointState { 34 MidpointState() 35 { 36 reset(); 37 } 38 39 void reset() 40 { 41 numMidpoints = 0; 42 currentMidpoint = 0; 43 betweenMidpoints = false; 44 } 45 46 // The goal is to reuse the line state across multiple 47 // lines so we just keep an array around for midpoints and never clear it across multiple 48 // lines. We track the number of items and position using the two other variables. 49 Vector<Iterator> midpoints; 50 unsigned numMidpoints; 51 unsigned currentMidpoint; 52 bool betweenMidpoints; 53 }; 54 55 // The BidiStatus at a given position (typically the end of a line) can 56 // be cached and then used to restart bidi resolution at that position. 57 struct BidiStatus { 58 BidiStatus() 59 : eor(WTF::Unicode::OtherNeutral) 60 , lastStrong(WTF::Unicode::OtherNeutral) 61 , last(WTF::Unicode::OtherNeutral) 62 { 63 } 64 65 BidiStatus(WTF::Unicode::Direction eorDir, WTF::Unicode::Direction lastStrongDir, WTF::Unicode::Direction lastDir, PassRefPtr<BidiContext> bidiContext) 66 : eor(eorDir) 67 , lastStrong(lastStrongDir) 68 , last(lastDir) 69 , context(bidiContext) 70 { 71 } 72 73 WTF::Unicode::Direction eor; 74 WTF::Unicode::Direction lastStrong; 75 WTF::Unicode::Direction last; 76 RefPtr<BidiContext> context; 77 }; 78 79 class BidiEmbedding { 80 public: 81 BidiEmbedding(WTF::Unicode::Direction direction, BidiEmbeddingSource source) 82 : m_direction(direction) 83 , m_source(source) 84 { 85 } 86 87 WTF::Unicode::Direction direction() const { return m_direction; } 88 BidiEmbeddingSource source() const { return m_source; } 89 private: 90 WTF::Unicode::Direction m_direction; 91 BidiEmbeddingSource m_source; 92 }; 93 94 inline bool operator==(const BidiStatus& status1, const BidiStatus& status2) 95 { 96 return status1.eor == status2.eor && status1.last == status2.last && status1.lastStrong == status2.lastStrong && *(status1.context) == *(status2.context); 97 } 98 99 inline bool operator!=(const BidiStatus& status1, const BidiStatus& status2) 100 { 101 return !(status1 == status2); 102 } 103 104 struct BidiCharacterRun { 105 BidiCharacterRun(int start, int stop, BidiContext* context, WTF::Unicode::Direction dir) 106 : m_start(start) 107 , m_stop(stop) 108 , m_override(context->override()) 109 , m_next(0) 110 { 111 if (dir == WTF::Unicode::OtherNeutral) 112 dir = context->dir(); 113 114 m_level = context->level(); 115 116 // add level of run (cases I1 & I2) 117 if (m_level % 2) { 118 if (dir == WTF::Unicode::LeftToRight || dir == WTF::Unicode::ArabicNumber || dir == WTF::Unicode::EuropeanNumber) 119 m_level++; 120 } else { 121 if (dir == WTF::Unicode::RightToLeft) 122 m_level++; 123 else if (dir == WTF::Unicode::ArabicNumber || dir == WTF::Unicode::EuropeanNumber) 124 m_level += 2; 125 } 126 } 127 128 void destroy() { delete this; } 129 130 int start() const { return m_start; } 131 int stop() const { return m_stop; } 132 unsigned char level() const { return m_level; } 133 bool reversed(bool visuallyOrdered) { return m_level % 2 && !visuallyOrdered; } 134 bool dirOverride(bool visuallyOrdered) { return m_override || visuallyOrdered; } 135 136 BidiCharacterRun* next() const { return m_next; } 137 138 unsigned char m_level; 139 int m_start; 140 int m_stop; 141 bool m_override; 142 BidiCharacterRun* m_next; 143 }; 144 145 enum VisualDirectionOverride { 146 NoVisualOverride, 147 VisualLeftToRightOverride, 148 VisualRightToLeftOverride 149 }; 150 151 // BidiResolver is WebKit's implementation of the Unicode Bidi Algorithm 152 // http://unicode.org/reports/tr9 153 template <class Iterator, class Run> class BidiResolver { 154 WTF_MAKE_NONCOPYABLE(BidiResolver); 155 public: 156 BidiResolver() 157 : m_direction(WTF::Unicode::OtherNeutral) 158 , m_reachedEndOfLine(false) 159 , m_emptyRun(true) 160 { 161 } 162 163 const Iterator& position() const { return m_current; } 164 void setPosition(const Iterator& position) { m_current = position; } 165 166 void increment() { m_current.increment(); } 167 168 BidiContext* context() const { return m_status.context.get(); } 169 void setContext(PassRefPtr<BidiContext> c) { m_status.context = c; } 170 171 void setLastDir(WTF::Unicode::Direction lastDir) { m_status.last = lastDir; } 172 void setLastStrongDir(WTF::Unicode::Direction lastStrongDir) { m_status.lastStrong = lastStrongDir; } 173 void setEorDir(WTF::Unicode::Direction eorDir) { m_status.eor = eorDir; } 174 175 WTF::Unicode::Direction dir() const { return m_direction; } 176 void setDir(WTF::Unicode::Direction d) { m_direction = d; } 177 178 const BidiStatus& status() const { return m_status; } 179 void setStatus(const BidiStatus s) { m_status = s; } 180 181 MidpointState<Iterator>& midpointState() { return m_midpointState; } 182 183 void embed(WTF::Unicode::Direction, BidiEmbeddingSource); 184 bool commitExplicitEmbedding(); 185 186 void createBidiRunsForLine(const Iterator& end, VisualDirectionOverride = NoVisualOverride, bool hardLineBreak = false); 187 188 BidiRunList<Run>& runs() { return m_runs; } 189 190 // FIXME: This used to be part of deleteRuns() but was a layering violation. 191 // It's unclear if this is still needed. 192 void markCurrentRunEmpty() { m_emptyRun = true; } 193 194 protected: 195 // FIXME: Instead of InlineBidiResolvers subclassing this method, we should 196 // pass in some sort of Traits object which knows how to create runs for appending. 197 void appendRun(); 198 199 Iterator m_current; 200 // sor and eor are "start of run" and "end of run" respectively and correpond 201 // to abreviations used in UBA spec: http://unicode.org/reports/tr9/#BD7 202 Iterator m_sor; 203 Iterator m_eor; 204 Iterator m_last; 205 BidiStatus m_status; 206 WTF::Unicode::Direction m_direction; 207 Iterator endOfLine; 208 bool m_reachedEndOfLine; 209 Iterator m_lastBeforeET; // Before a EuropeanNumberTerminator 210 bool m_emptyRun; 211 212 // FIXME: This should not belong to the resolver, but rather be passed 213 // into createBidiRunsForLine by the caller. 214 BidiRunList<Run> m_runs; 215 216 MidpointState<Iterator> m_midpointState; 217 218 private: 219 void raiseExplicitEmbeddingLevel(WTF::Unicode::Direction from, WTF::Unicode::Direction to); 220 void lowerExplicitEmbeddingLevel(WTF::Unicode::Direction from); 221 void checkDirectionInLowerRaiseEmbeddingLevel(); 222 223 void updateStatusLastFromCurrentDirection(WTF::Unicode::Direction); 224 void reorderRunsFromLevels(); 225 226 Vector<BidiEmbedding, 8> m_currentExplicitEmbeddingSequence; 227 }; 228 229 template <class Iterator, class Run> 230 void BidiResolver<Iterator, Run>::appendRun() 231 { 232 if (!m_emptyRun && !m_eor.atEnd()) { 233 unsigned startOffset = m_sor.offset(); 234 unsigned endOffset = m_eor.offset(); 235 236 if (!endOfLine.atEnd() && endOffset >= endOfLine.offset()) { 237 m_reachedEndOfLine = true; 238 endOffset = endOfLine.offset(); 239 } 240 241 if (endOffset >= startOffset) 242 m_runs.addRun(new Run(startOffset, endOffset + 1, context(), m_direction)); 243 244 m_eor.increment(); 245 m_sor = m_eor; 246 } 247 248 m_direction = WTF::Unicode::OtherNeutral; 249 m_status.eor = WTF::Unicode::OtherNeutral; 250 } 251 252 template <class Iterator, class Run> 253 void BidiResolver<Iterator, Run>::embed(WTF::Unicode::Direction dir, BidiEmbeddingSource source) 254 { 255 using namespace WTF::Unicode; 256 257 ASSERT(dir == PopDirectionalFormat || dir == LeftToRightEmbedding || dir == LeftToRightOverride || dir == RightToLeftEmbedding || dir == RightToLeftOverride); 258 m_currentExplicitEmbeddingSequence.append(BidiEmbedding(dir, source)); 259 } 260 261 template <class Iterator, class Run> 262 void BidiResolver<Iterator, Run>::checkDirectionInLowerRaiseEmbeddingLevel() 263 { 264 using namespace WTF::Unicode; 265 266 ASSERT(m_status.eor != OtherNeutral || m_eor.atEnd()); 267 ASSERT(m_status.last != NonSpacingMark 268 && m_status.last != BoundaryNeutral 269 && m_status.last != RightToLeftEmbedding 270 && m_status.last != LeftToRightEmbedding 271 && m_status.last != RightToLeftOverride 272 && m_status.last != LeftToRightOverride 273 && m_status.last != PopDirectionalFormat); 274 if (m_direction == OtherNeutral) 275 m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft; 276 } 277 278 template <class Iterator, class Run> 279 void BidiResolver<Iterator, Run>::lowerExplicitEmbeddingLevel(WTF::Unicode::Direction from) 280 { 281 using namespace WTF::Unicode; 282 283 if (!m_emptyRun && m_eor != m_last) { 284 checkDirectionInLowerRaiseEmbeddingLevel(); 285 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last 286 if (from == LeftToRight) { 287 // bidi.sor ... bidi.eor ... bidi.last L 288 if (m_status.eor == EuropeanNumber) { 289 if (m_status.lastStrong != LeftToRight) { 290 m_direction = EuropeanNumber; 291 appendRun(); 292 } 293 } else if (m_status.eor == ArabicNumber) { 294 m_direction = ArabicNumber; 295 appendRun(); 296 } else if (m_status.lastStrong != LeftToRight) { 297 appendRun(); 298 m_direction = LeftToRight; 299 } 300 } else if (m_status.eor == EuropeanNumber || m_status.eor == ArabicNumber || m_status.lastStrong == LeftToRight) { 301 appendRun(); 302 m_direction = RightToLeft; 303 } 304 m_eor = m_last; 305 } 306 307 appendRun(); 308 m_emptyRun = true; 309 310 // sor for the new run is determined by the higher level (rule X10) 311 setLastDir(from); 312 setLastStrongDir(from); 313 m_eor = Iterator(); 314 } 315 316 template <class Iterator, class Run> 317 void BidiResolver<Iterator, Run>::raiseExplicitEmbeddingLevel(WTF::Unicode::Direction from, WTF::Unicode::Direction to) 318 { 319 using namespace WTF::Unicode; 320 321 if (!m_emptyRun && m_eor != m_last) { 322 checkDirectionInLowerRaiseEmbeddingLevel(); 323 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last 324 if (to == LeftToRight) { 325 // bidi.sor ... bidi.eor ... bidi.last L 326 if (m_status.eor == EuropeanNumber) { 327 if (m_status.lastStrong != LeftToRight) { 328 m_direction = EuropeanNumber; 329 appendRun(); 330 } 331 } else if (m_status.eor == ArabicNumber) { 332 m_direction = ArabicNumber; 333 appendRun(); 334 } else if (m_status.lastStrong != LeftToRight && from == LeftToRight) { 335 appendRun(); 336 m_direction = LeftToRight; 337 } 338 } else if (m_status.eor == ArabicNumber 339 || (m_status.eor == EuropeanNumber && (m_status.lastStrong != LeftToRight || from == RightToLeft)) 340 || (m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && from == RightToLeft)) { 341 appendRun(); 342 m_direction = RightToLeft; 343 } 344 m_eor = m_last; 345 } 346 347 appendRun(); 348 m_emptyRun = true; 349 350 setLastDir(to); 351 setLastStrongDir(to); 352 m_eor = Iterator(); 353 } 354 355 template <class Iterator, class Run> 356 bool BidiResolver<Iterator, Run>::commitExplicitEmbedding() 357 { 358 using namespace WTF::Unicode; 359 360 unsigned char fromLevel = context()->level(); 361 RefPtr<BidiContext> toContext = context(); 362 363 for (size_t i = 0; i < m_currentExplicitEmbeddingSequence.size(); ++i) { 364 BidiEmbedding embedding = m_currentExplicitEmbeddingSequence[i]; 365 if (embedding.direction() == PopDirectionalFormat) { 366 if (BidiContext* parentContext = toContext->parent()) 367 toContext = parentContext; 368 } else { 369 Direction direction = (embedding.direction() == RightToLeftEmbedding || embedding.direction() == RightToLeftOverride) ? RightToLeft : LeftToRight; 370 bool override = embedding.direction() == LeftToRightOverride || embedding.direction() == RightToLeftOverride; 371 unsigned char level = toContext->level(); 372 if (direction == RightToLeft) 373 level = nextGreaterOddLevel(level); 374 else 375 level = nextGreaterEvenLevel(level); 376 if (level < 61) 377 toContext = BidiContext::create(level, direction, override, embedding.source(), toContext.get()); 378 } 379 } 380 381 unsigned char toLevel = toContext->level(); 382 383 if (toLevel > fromLevel) 384 raiseExplicitEmbeddingLevel(fromLevel % 2 ? RightToLeft : LeftToRight, toLevel % 2 ? RightToLeft : LeftToRight); 385 else if (toLevel < fromLevel) 386 lowerExplicitEmbeddingLevel(fromLevel % 2 ? RightToLeft : LeftToRight); 387 388 setContext(toContext); 389 390 m_currentExplicitEmbeddingSequence.clear(); 391 392 return fromLevel != toLevel; 393 } 394 395 template <class Iterator, class Run> 396 inline void BidiResolver<Iterator, Run>::updateStatusLastFromCurrentDirection(WTF::Unicode::Direction dirCurrent) 397 { 398 using namespace WTF::Unicode; 399 switch (dirCurrent) { 400 case EuropeanNumberTerminator: 401 if (m_status.last != EuropeanNumber) 402 m_status.last = EuropeanNumberTerminator; 403 break; 404 case EuropeanNumberSeparator: 405 case CommonNumberSeparator: 406 case SegmentSeparator: 407 case WhiteSpaceNeutral: 408 case OtherNeutral: 409 switch (m_status.last) { 410 case LeftToRight: 411 case RightToLeft: 412 case RightToLeftArabic: 413 case EuropeanNumber: 414 case ArabicNumber: 415 m_status.last = dirCurrent; 416 break; 417 default: 418 m_status.last = OtherNeutral; 419 } 420 break; 421 case NonSpacingMark: 422 case BoundaryNeutral: 423 case RightToLeftEmbedding: 424 case LeftToRightEmbedding: 425 case RightToLeftOverride: 426 case LeftToRightOverride: 427 case PopDirectionalFormat: 428 // ignore these 429 break; 430 case EuropeanNumber: 431 // fall through 432 default: 433 m_status.last = dirCurrent; 434 } 435 } 436 437 template <class Iterator, class Run> 438 inline void BidiResolver<Iterator, Run>::reorderRunsFromLevels() 439 { 440 unsigned char levelLow = 128; 441 unsigned char levelHigh = 0; 442 for (Run* run = m_runs.firstRun(); run; run = run->next()) { 443 levelHigh = std::max(run->level(), levelHigh); 444 levelLow = std::min(run->level(), levelLow); 445 } 446 447 // This implements reordering of the line (L2 according to Bidi spec): 448 // http://unicode.org/reports/tr9/#L2 449 // L2. From the highest level found in the text to the lowest odd level on each line, 450 // reverse any contiguous sequence of characters that are at that level or higher. 451 452 // Reversing is only done up to the lowest odd level. 453 if (!(levelLow % 2)) 454 levelLow++; 455 456 unsigned count = m_runs.runCount() - 1; 457 458 while (levelHigh >= levelLow) { 459 unsigned i = 0; 460 Run* run = m_runs.firstRun(); 461 while (i < count) { 462 for (;i < count && run && run->level() < levelHigh; i++) 463 run = run->next(); 464 unsigned start = i; 465 for (;i <= count && run && run->level() >= levelHigh; i++) 466 run = run->next(); 467 unsigned end = i - 1; 468 m_runs.reverseRuns(start, end); 469 } 470 levelHigh--; 471 } 472 } 473 474 template <class Iterator, class Run> 475 void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& end, VisualDirectionOverride override, bool hardLineBreak) 476 { 477 using namespace WTF::Unicode; 478 479 ASSERT(m_direction == OtherNeutral); 480 481 if (override != NoVisualOverride) { 482 m_emptyRun = false; 483 m_sor = m_current; 484 m_eor = Iterator(); 485 while (m_current != end && !m_current.atEnd()) { 486 m_eor = m_current; 487 increment(); 488 } 489 m_direction = override == VisualLeftToRightOverride ? LeftToRight : RightToLeft; 490 appendRun(); 491 m_runs.setLogicallyLastRun(m_runs.lastRun()); 492 if (override == VisualRightToLeftOverride) 493 m_runs.reverseRuns(0, m_runs.runCount() - 1); 494 return; 495 } 496 497 m_emptyRun = true; 498 499 m_eor = Iterator(); 500 501 m_last = m_current; 502 bool pastEnd = false; 503 BidiResolver<Iterator, Run> stateAtEnd; 504 505 while (true) { 506 Direction dirCurrent; 507 if (pastEnd && (hardLineBreak || m_current.atEnd())) { 508 BidiContext* c = context(); 509 if (hardLineBreak) { 510 // A deviation from the Unicode Bidi Algorithm in order to match 511 // WinIE and user expectations: hard line breaks reset bidi state 512 // coming from unicode bidi control characters, but not those from 513 // DOM nodes with specified directionality 514 stateAtEnd.setContext(c->copyStackRemovingUnicodeEmbeddingContexts()); 515 516 dirCurrent = stateAtEnd.context()->dir(); 517 stateAtEnd.setEorDir(dirCurrent); 518 stateAtEnd.setLastDir(dirCurrent); 519 stateAtEnd.setLastStrongDir(dirCurrent); 520 } else { 521 while (c->parent()) 522 c = c->parent(); 523 dirCurrent = c->dir(); 524 } 525 } else { 526 dirCurrent = m_current.direction(); 527 if (context()->override() 528 && dirCurrent != RightToLeftEmbedding 529 && dirCurrent != LeftToRightEmbedding 530 && dirCurrent != RightToLeftOverride 531 && dirCurrent != LeftToRightOverride 532 && dirCurrent != PopDirectionalFormat) 533 dirCurrent = context()->dir(); 534 else if (dirCurrent == NonSpacingMark) 535 dirCurrent = m_status.last; 536 } 537 538 ASSERT(m_status.eor != OtherNeutral || m_eor.atEnd()); 539 switch (dirCurrent) { 540 541 // embedding and overrides (X1-X9 in the Bidi specs) 542 case RightToLeftEmbedding: 543 case LeftToRightEmbedding: 544 case RightToLeftOverride: 545 case LeftToRightOverride: 546 case PopDirectionalFormat: 547 embed(dirCurrent, FromUnicode); 548 commitExplicitEmbedding(); 549 break; 550 551 // strong types 552 case LeftToRight: 553 switch(m_status.last) { 554 case RightToLeft: 555 case RightToLeftArabic: 556 case EuropeanNumber: 557 case ArabicNumber: 558 if (m_status.last != EuropeanNumber || m_status.lastStrong != LeftToRight) 559 appendRun(); 560 break; 561 case LeftToRight: 562 break; 563 case EuropeanNumberSeparator: 564 case EuropeanNumberTerminator: 565 case CommonNumberSeparator: 566 case BoundaryNeutral: 567 case BlockSeparator: 568 case SegmentSeparator: 569 case WhiteSpaceNeutral: 570 case OtherNeutral: 571 if (m_status.eor == EuropeanNumber) { 572 if (m_status.lastStrong != LeftToRight) { 573 // the numbers need to be on a higher embedding level, so let's close that run 574 m_direction = EuropeanNumber; 575 appendRun(); 576 if (context()->dir() != LeftToRight) { 577 // the neutrals take the embedding direction, which is R 578 m_eor = m_last; 579 m_direction = RightToLeft; 580 appendRun(); 581 } 582 } 583 } else if (m_status.eor == ArabicNumber) { 584 // Arabic numbers are always on a higher embedding level, so let's close that run 585 m_direction = ArabicNumber; 586 appendRun(); 587 if (context()->dir() != LeftToRight) { 588 // the neutrals take the embedding direction, which is R 589 m_eor = m_last; 590 m_direction = RightToLeft; 591 appendRun(); 592 } 593 } else if (m_status.lastStrong != LeftToRight) { 594 //last stuff takes embedding dir 595 if (context()->dir() == RightToLeft) { 596 m_eor = m_last; 597 m_direction = RightToLeft; 598 } 599 appendRun(); 600 } 601 default: 602 break; 603 } 604 m_eor = m_current; 605 m_status.eor = LeftToRight; 606 m_status.lastStrong = LeftToRight; 607 m_direction = LeftToRight; 608 break; 609 case RightToLeftArabic: 610 case RightToLeft: 611 switch (m_status.last) { 612 case LeftToRight: 613 case EuropeanNumber: 614 case ArabicNumber: 615 appendRun(); 616 case RightToLeft: 617 case RightToLeftArabic: 618 break; 619 case EuropeanNumberSeparator: 620 case EuropeanNumberTerminator: 621 case CommonNumberSeparator: 622 case BoundaryNeutral: 623 case BlockSeparator: 624 case SegmentSeparator: 625 case WhiteSpaceNeutral: 626 case OtherNeutral: 627 if (m_status.eor == EuropeanNumber) { 628 if (m_status.lastStrong == LeftToRight && context()->dir() == LeftToRight) 629 m_eor = m_last; 630 appendRun(); 631 } else if (m_status.eor == ArabicNumber) 632 appendRun(); 633 else if (m_status.lastStrong == LeftToRight) { 634 if (context()->dir() == LeftToRight) 635 m_eor = m_last; 636 appendRun(); 637 } 638 default: 639 break; 640 } 641 m_eor = m_current; 642 m_status.eor = RightToLeft; 643 m_status.lastStrong = dirCurrent; 644 m_direction = RightToLeft; 645 break; 646 647 // weak types: 648 649 case EuropeanNumber: 650 if (m_status.lastStrong != RightToLeftArabic) { 651 // if last strong was AL change EN to AN 652 switch (m_status.last) { 653 case EuropeanNumber: 654 case LeftToRight: 655 break; 656 case RightToLeft: 657 case RightToLeftArabic: 658 case ArabicNumber: 659 m_eor = m_last; 660 appendRun(); 661 m_direction = EuropeanNumber; 662 break; 663 case EuropeanNumberSeparator: 664 case CommonNumberSeparator: 665 if (m_status.eor == EuropeanNumber) 666 break; 667 case EuropeanNumberTerminator: 668 case BoundaryNeutral: 669 case BlockSeparator: 670 case SegmentSeparator: 671 case WhiteSpaceNeutral: 672 case OtherNeutral: 673 if (m_status.eor == EuropeanNumber) { 674 if (m_status.lastStrong == RightToLeft) { 675 // ENs on both sides behave like Rs, so the neutrals should be R. 676 // Terminate the EN run. 677 appendRun(); 678 // Make an R run. 679 m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last; 680 m_direction = RightToLeft; 681 appendRun(); 682 // Begin a new EN run. 683 m_direction = EuropeanNumber; 684 } 685 } else if (m_status.eor == ArabicNumber) { 686 // Terminate the AN run. 687 appendRun(); 688 if (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft) { 689 // Make an R run. 690 m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last; 691 m_direction = RightToLeft; 692 appendRun(); 693 // Begin a new EN run. 694 m_direction = EuropeanNumber; 695 } 696 } else if (m_status.lastStrong == RightToLeft) { 697 // Extend the R run to include the neutrals. 698 m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last; 699 m_direction = RightToLeft; 700 appendRun(); 701 // Begin a new EN run. 702 m_direction = EuropeanNumber; 703 } 704 default: 705 break; 706 } 707 m_eor = m_current; 708 m_status.eor = EuropeanNumber; 709 if (m_direction == OtherNeutral) 710 m_direction = LeftToRight; 711 break; 712 } 713 case ArabicNumber: 714 dirCurrent = ArabicNumber; 715 switch (m_status.last) { 716 case LeftToRight: 717 if (context()->dir() == LeftToRight) 718 appendRun(); 719 break; 720 case ArabicNumber: 721 break; 722 case RightToLeft: 723 case RightToLeftArabic: 724 case EuropeanNumber: 725 m_eor = m_last; 726 appendRun(); 727 break; 728 case CommonNumberSeparator: 729 if (m_status.eor == ArabicNumber) 730 break; 731 case EuropeanNumberSeparator: 732 case EuropeanNumberTerminator: 733 case BoundaryNeutral: 734 case BlockSeparator: 735 case SegmentSeparator: 736 case WhiteSpaceNeutral: 737 case OtherNeutral: 738 if (m_status.eor == ArabicNumber 739 || (m_status.eor == EuropeanNumber && (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft)) 740 || (m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && context()->dir() == RightToLeft)) { 741 // Terminate the run before the neutrals. 742 appendRun(); 743 // Begin an R run for the neutrals. 744 m_direction = RightToLeft; 745 } else if (m_direction == OtherNeutral) 746 m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft; 747 m_eor = m_last; 748 appendRun(); 749 default: 750 break; 751 } 752 m_eor = m_current; 753 m_status.eor = ArabicNumber; 754 if (m_direction == OtherNeutral) 755 m_direction = ArabicNumber; 756 break; 757 case EuropeanNumberSeparator: 758 case CommonNumberSeparator: 759 break; 760 case EuropeanNumberTerminator: 761 if (m_status.last == EuropeanNumber) { 762 dirCurrent = EuropeanNumber; 763 m_eor = m_current; 764 m_status.eor = dirCurrent; 765 } else if (m_status.last != EuropeanNumberTerminator) 766 m_lastBeforeET = m_emptyRun ? m_eor : m_last; 767 break; 768 769 // boundary neutrals should be ignored 770 case BoundaryNeutral: 771 if (m_eor == m_last) 772 m_eor = m_current; 773 break; 774 // neutrals 775 case BlockSeparator: 776 // ### what do we do with newline and paragraph seperators that come to here? 777 break; 778 case SegmentSeparator: 779 // ### implement rule L1 780 break; 781 case WhiteSpaceNeutral: 782 break; 783 case OtherNeutral: 784 break; 785 default: 786 break; 787 } 788 789 if (pastEnd && m_eor == m_current) { 790 if (!m_reachedEndOfLine) { 791 m_eor = endOfLine; 792 switch (m_status.eor) { 793 case LeftToRight: 794 case RightToLeft: 795 case ArabicNumber: 796 m_direction = m_status.eor; 797 break; 798 case EuropeanNumber: 799 m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : EuropeanNumber; 800 break; 801 default: 802 ASSERT(false); 803 } 804 appendRun(); 805 } 806 m_current = end; 807 m_status = stateAtEnd.m_status; 808 m_sor = stateAtEnd.m_sor; 809 m_eor = stateAtEnd.m_eor; 810 m_last = stateAtEnd.m_last; 811 m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine; 812 m_lastBeforeET = stateAtEnd.m_lastBeforeET; 813 m_emptyRun = stateAtEnd.m_emptyRun; 814 m_direction = OtherNeutral; 815 break; 816 } 817 818 updateStatusLastFromCurrentDirection(dirCurrent); 819 m_last = m_current; 820 821 if (m_emptyRun) { 822 m_sor = m_current; 823 m_emptyRun = false; 824 } 825 826 increment(); 827 if (!m_currentExplicitEmbeddingSequence.isEmpty()) { 828 bool committed = commitExplicitEmbedding(); 829 if (committed && pastEnd) { 830 m_current = end; 831 m_status = stateAtEnd.m_status; 832 m_sor = stateAtEnd.m_sor; 833 m_eor = stateAtEnd.m_eor; 834 m_last = stateAtEnd.m_last; 835 m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine; 836 m_lastBeforeET = stateAtEnd.m_lastBeforeET; 837 m_emptyRun = stateAtEnd.m_emptyRun; 838 m_direction = OtherNeutral; 839 break; 840 } 841 } 842 843 if (!pastEnd && (m_current == end || m_current.atEnd())) { 844 if (m_emptyRun) 845 break; 846 stateAtEnd.m_status = m_status; 847 stateAtEnd.m_sor = m_sor; 848 stateAtEnd.m_eor = m_eor; 849 stateAtEnd.m_last = m_last; 850 stateAtEnd.m_reachedEndOfLine = m_reachedEndOfLine; 851 stateAtEnd.m_lastBeforeET = m_lastBeforeET; 852 stateAtEnd.m_emptyRun = m_emptyRun; 853 endOfLine = m_last; 854 pastEnd = true; 855 } 856 } 857 858 m_runs.setLogicallyLastRun(m_runs.lastRun()); 859 reorderRunsFromLevels(); 860 endOfLine = Iterator(); 861 } 862 863 } // namespace WebCore 864 865 #endif // BidiResolver_h 866