2 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2003, 2004, 2006, 2007, 2008 Apple Inc. All right reserved.
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.
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.
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.
22 #ifndef BidiResolver_h
23 #define BidiResolver_h
25 #include "BidiContext.h"
26 #include "BidiRunList.h"
27 #include "TextDirection.h"
28 #include <wtf/Noncopyable.h>
29 #include <wtf/PassRefPtr.h>
30 #include <wtf/Vector.h>
34 template <class Iterator> struct MidpointState {
44 betweenMidpoints = false;
47 // The goal is to reuse the line state across multiple
48 // lines so we just keep an array around for midpoints and never clear it across multiple
49 // lines. We track the number of items and position using the two other variables.
50 Vector<Iterator> midpoints;
51 unsigned numMidpoints;
52 unsigned currentMidpoint;
53 bool betweenMidpoints;
56 // The BidiStatus at a given position (typically the end of a line) can
57 // be cached and then used to restart bidi resolution at that position.
60 : eor(WTF::Unicode::OtherNeutral)
61 , lastStrong(WTF::Unicode::OtherNeutral)
62 , last(WTF::Unicode::OtherNeutral)
66 // Creates a BidiStatus representing a new paragraph root with a default direction.
67 // Uses TextDirection as it only has two possibilities instead of WTF::Unicode::Direction which has 19.
68 BidiStatus(TextDirection textDirection, bool isOverride)
70 WTF::Unicode::Direction direction = textDirection == LTR ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
71 eor = lastStrong = last = direction;
72 context = BidiContext::create(textDirection == LTR ? 0 : 1, direction, isOverride);
75 BidiStatus(WTF::Unicode::Direction eorDir, WTF::Unicode::Direction lastStrongDir, WTF::Unicode::Direction lastDir, PassRefPtr<BidiContext> bidiContext)
77 , lastStrong(lastStrongDir)
79 , context(bidiContext)
83 WTF::Unicode::Direction eor;
84 WTF::Unicode::Direction lastStrong;
85 WTF::Unicode::Direction last;
86 RefPtr<BidiContext> context;
91 BidiEmbedding(WTF::Unicode::Direction direction, BidiEmbeddingSource source)
92 : m_direction(direction)
97 WTF::Unicode::Direction direction() const { return m_direction; }
98 BidiEmbeddingSource source() const { return m_source; }
100 WTF::Unicode::Direction m_direction;
101 BidiEmbeddingSource m_source;
104 inline bool operator==(const BidiStatus& status1, const BidiStatus& status2)
106 return status1.eor == status2.eor && status1.last == status2.last && status1.lastStrong == status2.lastStrong && *(status1.context) == *(status2.context);
109 inline bool operator!=(const BidiStatus& status1, const BidiStatus& status2)
111 return !(status1 == status2);
114 struct BidiCharacterRun {
115 BidiCharacterRun(int start, int stop, BidiContext* context, WTF::Unicode::Direction dir)
118 , m_override(context->override())
121 if (dir == WTF::Unicode::OtherNeutral)
122 dir = context->dir();
124 m_level = context->level();
126 // add level of run (cases I1 & I2)
128 if (dir == WTF::Unicode::LeftToRight || dir == WTF::Unicode::ArabicNumber || dir == WTF::Unicode::EuropeanNumber)
131 if (dir == WTF::Unicode::RightToLeft)
133 else if (dir == WTF::Unicode::ArabicNumber || dir == WTF::Unicode::EuropeanNumber)
138 void destroy() { delete this; }
140 int start() const { return m_start; }
141 int stop() const { return m_stop; }
142 unsigned char level() const { return m_level; }
143 bool reversed(bool visuallyOrdered) { return m_level % 2 && !visuallyOrdered; }
144 bool dirOverride(bool visuallyOrdered) { return m_override || visuallyOrdered; }
146 BidiCharacterRun* next() const { return m_next; }
147 void setNext(BidiCharacterRun* next) { m_next = next; }
149 unsigned char m_level;
153 BidiCharacterRun* m_next;
156 enum VisualDirectionOverride {
158 VisualLeftToRightOverride,
159 VisualRightToLeftOverride
162 // BidiResolver is WebKit's implementation of the Unicode Bidi Algorithm
163 // http://unicode.org/reports/tr9
164 template <class Iterator, class Run> class BidiResolver {
165 WTF_MAKE_NONCOPYABLE(BidiResolver);
168 : m_direction(WTF::Unicode::OtherNeutral)
169 , m_reachedEndOfLine(false)
171 , m_nestedIsolateCount(0)
179 const Iterator& position() const { return m_current; }
180 void setPosition(const Iterator& position) { m_current = position; }
182 void increment() { m_current.increment(); }
184 BidiContext* context() const { return m_status.context.get(); }
185 void setContext(PassRefPtr<BidiContext> c) { m_status.context = c; }
187 void setLastDir(WTF::Unicode::Direction lastDir) { m_status.last = lastDir; }
188 void setLastStrongDir(WTF::Unicode::Direction lastStrongDir) { m_status.lastStrong = lastStrongDir; }
189 void setEorDir(WTF::Unicode::Direction eorDir) { m_status.eor = eorDir; }
191 WTF::Unicode::Direction dir() const { return m_direction; }
192 void setDir(WTF::Unicode::Direction d) { m_direction = d; }
194 const BidiStatus& status() const { return m_status; }
195 void setStatus(const BidiStatus s) { m_status = s; }
197 MidpointState<Iterator>& midpointState() { return m_midpointState; }
199 // The current algorithm handles nested isolates one layer of nesting at a time.
200 // But when we layout each isolated span, we will walk into (and ignore) all
201 // child isolated spans.
202 void enterIsolate() { m_nestedIsolateCount++; }
203 void exitIsolate() { ASSERT(m_nestedIsolateCount >= 1); m_nestedIsolateCount--; }
204 bool inIsolate() const { return m_nestedIsolateCount; }
206 void embed(WTF::Unicode::Direction, BidiEmbeddingSource);
207 bool commitExplicitEmbedding();
209 void createBidiRunsForLine(const Iterator& end, VisualDirectionOverride = NoVisualOverride, bool hardLineBreak = false);
211 BidiRunList<Run>& runs() { return m_runs; }
213 // FIXME: This used to be part of deleteRuns() but was a layering violation.
214 // It's unclear if this is still needed.
215 void markCurrentRunEmpty() { m_emptyRun = true; }
217 Vector<Run*>& isolatedRuns() { return m_isolatedRuns; }
220 // FIXME: Instead of InlineBidiResolvers subclassing this method, we should
221 // pass in some sort of Traits object which knows how to create runs for appending.
225 // sor and eor are "start of run" and "end of run" respectively and correpond
226 // to abreviations used in UBA spec: http://unicode.org/reports/tr9/#BD7
227 Iterator m_sor; // Points to the first character in the current run.
228 Iterator m_eor; // Points to the last character in the current run.
231 WTF::Unicode::Direction m_direction;
233 bool m_reachedEndOfLine;
234 Iterator m_lastBeforeET; // Before a EuropeanNumberTerminator
237 // FIXME: This should not belong to the resolver, but rather be passed
238 // into createBidiRunsForLine by the caller.
239 BidiRunList<Run> m_runs;
241 MidpointState<Iterator> m_midpointState;
243 unsigned m_nestedIsolateCount;
244 Vector<Run*> m_isolatedRuns;
247 void raiseExplicitEmbeddingLevel(WTF::Unicode::Direction from, WTF::Unicode::Direction to);
248 void lowerExplicitEmbeddingLevel(WTF::Unicode::Direction from);
249 void checkDirectionInLowerRaiseEmbeddingLevel();
251 void updateStatusLastFromCurrentDirection(WTF::Unicode::Direction);
252 void reorderRunsFromLevels();
254 Vector<BidiEmbedding, 8> m_currentExplicitEmbeddingSequence;
258 template <class Iterator, class Run>
259 BidiResolver<Iterator, Run>::~BidiResolver()
261 // The owner of this resolver should have handled the isolated runs
262 // or should never have called enterIsolate().
263 ASSERT(m_isolatedRuns.isEmpty());
264 ASSERT(!m_nestedIsolateCount);
268 template <class Iterator, class Run>
269 void BidiResolver<Iterator, Run>::appendRun()
271 if (!m_emptyRun && !m_eor.atEnd()) {
272 unsigned startOffset = m_sor.offset();
273 unsigned endOffset = m_eor.offset();
275 if (!endOfLine.atEnd() && endOffset >= endOfLine.offset()) {
276 m_reachedEndOfLine = true;
277 endOffset = endOfLine.offset();
280 if (endOffset >= startOffset)
281 m_runs.addRun(new Run(startOffset, endOffset + 1, context(), m_direction));
287 m_direction = WTF::Unicode::OtherNeutral;
288 m_status.eor = WTF::Unicode::OtherNeutral;
291 template <class Iterator, class Run>
292 void BidiResolver<Iterator, Run>::embed(WTF::Unicode::Direction dir, BidiEmbeddingSource source)
294 // Isolated spans compute base directionality during their own UBA run.
295 // Do not insert fake embed characters once we enter an isolated span.
296 ASSERT(!inIsolate());
297 using namespace WTF::Unicode;
299 ASSERT(dir == PopDirectionalFormat || dir == LeftToRightEmbedding || dir == LeftToRightOverride || dir == RightToLeftEmbedding || dir == RightToLeftOverride);
300 m_currentExplicitEmbeddingSequence.append(BidiEmbedding(dir, source));
303 template <class Iterator, class Run>
304 void BidiResolver<Iterator, Run>::checkDirectionInLowerRaiseEmbeddingLevel()
306 using namespace WTF::Unicode;
308 ASSERT(m_status.eor != OtherNeutral || m_eor.atEnd());
309 ASSERT(m_status.last != NonSpacingMark
310 && m_status.last != BoundaryNeutral
311 && m_status.last != RightToLeftEmbedding
312 && m_status.last != LeftToRightEmbedding
313 && m_status.last != RightToLeftOverride
314 && m_status.last != LeftToRightOverride
315 && m_status.last != PopDirectionalFormat);
316 if (m_direction == OtherNeutral)
317 m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft;
320 template <class Iterator, class Run>
321 void BidiResolver<Iterator, Run>::lowerExplicitEmbeddingLevel(WTF::Unicode::Direction from)
323 using namespace WTF::Unicode;
325 if (!m_emptyRun && m_eor != m_last) {
326 checkDirectionInLowerRaiseEmbeddingLevel();
327 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
328 if (from == LeftToRight) {
329 // bidi.sor ... bidi.eor ... bidi.last L
330 if (m_status.eor == EuropeanNumber) {
331 if (m_status.lastStrong != LeftToRight) {
332 m_direction = EuropeanNumber;
335 } else if (m_status.eor == ArabicNumber) {
336 m_direction = ArabicNumber;
338 } else if (m_status.lastStrong != LeftToRight) {
340 m_direction = LeftToRight;
342 } else if (m_status.eor == EuropeanNumber || m_status.eor == ArabicNumber || m_status.lastStrong == LeftToRight) {
344 m_direction = RightToLeft;
352 // sor for the new run is determined by the higher level (rule X10)
354 setLastStrongDir(from);
358 template <class Iterator, class Run>
359 void BidiResolver<Iterator, Run>::raiseExplicitEmbeddingLevel(WTF::Unicode::Direction from, WTF::Unicode::Direction to)
361 using namespace WTF::Unicode;
363 if (!m_emptyRun && m_eor != m_last) {
364 checkDirectionInLowerRaiseEmbeddingLevel();
365 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
366 if (to == LeftToRight) {
367 // bidi.sor ... bidi.eor ... bidi.last L
368 if (m_status.eor == EuropeanNumber) {
369 if (m_status.lastStrong != LeftToRight) {
370 m_direction = EuropeanNumber;
373 } else if (m_status.eor == ArabicNumber) {
374 m_direction = ArabicNumber;
376 } else if (m_status.lastStrong != LeftToRight && from == LeftToRight) {
378 m_direction = LeftToRight;
380 } else if (m_status.eor == ArabicNumber
381 || (m_status.eor == EuropeanNumber && (m_status.lastStrong != LeftToRight || from == RightToLeft))
382 || (m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && from == RightToLeft)) {
384 m_direction = RightToLeft;
393 setLastStrongDir(to);
397 template <class Iterator, class Run>
398 bool BidiResolver<Iterator, Run>::commitExplicitEmbedding()
400 // This gets called from bidiFirst when setting up our start position.
401 ASSERT(!inIsolate() || m_currentExplicitEmbeddingSequence.isEmpty());
403 using namespace WTF::Unicode;
405 unsigned char fromLevel = context()->level();
406 RefPtr<BidiContext> toContext = context();
408 for (size_t i = 0; i < m_currentExplicitEmbeddingSequence.size(); ++i) {
409 BidiEmbedding embedding = m_currentExplicitEmbeddingSequence[i];
410 if (embedding.direction() == PopDirectionalFormat) {
411 if (BidiContext* parentContext = toContext->parent())
412 toContext = parentContext;
414 Direction direction = (embedding.direction() == RightToLeftEmbedding || embedding.direction() == RightToLeftOverride) ? RightToLeft : LeftToRight;
415 bool override = embedding.direction() == LeftToRightOverride || embedding.direction() == RightToLeftOverride;
416 unsigned char level = toContext->level();
417 if (direction == RightToLeft)
418 level = nextGreaterOddLevel(level);
420 level = nextGreaterEvenLevel(level);
422 toContext = BidiContext::create(level, direction, override, embedding.source(), toContext.get());
426 unsigned char toLevel = toContext->level();
428 if (toLevel > fromLevel)
429 raiseExplicitEmbeddingLevel(fromLevel % 2 ? RightToLeft : LeftToRight, toLevel % 2 ? RightToLeft : LeftToRight);
430 else if (toLevel < fromLevel)
431 lowerExplicitEmbeddingLevel(fromLevel % 2 ? RightToLeft : LeftToRight);
433 setContext(toContext);
435 m_currentExplicitEmbeddingSequence.clear();
437 return fromLevel != toLevel;
440 template <class Iterator, class Run>
441 inline void BidiResolver<Iterator, Run>::updateStatusLastFromCurrentDirection(WTF::Unicode::Direction dirCurrent)
443 using namespace WTF::Unicode;
444 switch (dirCurrent) {
445 case EuropeanNumberTerminator:
446 if (m_status.last != EuropeanNumber)
447 m_status.last = EuropeanNumberTerminator;
449 case EuropeanNumberSeparator:
450 case CommonNumberSeparator:
451 case SegmentSeparator:
452 case WhiteSpaceNeutral:
454 switch (m_status.last) {
457 case RightToLeftArabic:
460 m_status.last = dirCurrent;
463 m_status.last = OtherNeutral;
467 case BoundaryNeutral:
468 case RightToLeftEmbedding:
469 case LeftToRightEmbedding:
470 case RightToLeftOverride:
471 case LeftToRightOverride:
472 case PopDirectionalFormat:
478 m_status.last = dirCurrent;
482 template <class Iterator, class Run>
483 inline void BidiResolver<Iterator, Run>::reorderRunsFromLevels()
485 unsigned char levelLow = 128;
486 unsigned char levelHigh = 0;
487 for (Run* run = m_runs.firstRun(); run; run = run->next()) {
488 levelHigh = std::max(run->level(), levelHigh);
489 levelLow = std::min(run->level(), levelLow);
492 // This implements reordering of the line (L2 according to Bidi spec):
493 // http://unicode.org/reports/tr9/#L2
494 // L2. From the highest level found in the text to the lowest odd level on each line,
495 // reverse any contiguous sequence of characters that are at that level or higher.
497 // Reversing is only done up to the lowest odd level.
501 unsigned count = m_runs.runCount() - 1;
503 while (levelHigh >= levelLow) {
505 Run* run = m_runs.firstRun();
507 for (;i < count && run && run->level() < levelHigh; i++)
510 for (;i <= count && run && run->level() >= levelHigh; i++)
512 unsigned end = i - 1;
513 m_runs.reverseRuns(start, end);
519 template <class Iterator, class Run>
520 void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& end, VisualDirectionOverride override, bool hardLineBreak)
522 using namespace WTF::Unicode;
524 ASSERT(m_direction == OtherNeutral);
526 if (override != NoVisualOverride) {
530 while (m_current != end && !m_current.atEnd()) {
534 m_direction = override == VisualLeftToRightOverride ? LeftToRight : RightToLeft;
536 m_runs.setLogicallyLastRun(m_runs.lastRun());
537 if (override == VisualRightToLeftOverride)
538 m_runs.reverseRuns(0, m_runs.runCount() - 1);
547 bool pastEnd = false;
548 BidiResolver<Iterator, Run> stateAtEnd;
551 Direction dirCurrent;
552 if (pastEnd && (hardLineBreak || m_current.atEnd())) {
553 BidiContext* c = context();
555 // A deviation from the Unicode Bidi Algorithm in order to match
556 // WinIE and user expectations: hard line breaks reset bidi state
557 // coming from unicode bidi control characters, but not those from
558 // DOM nodes with specified directionality
559 stateAtEnd.setContext(c->copyStackRemovingUnicodeEmbeddingContexts());
561 dirCurrent = stateAtEnd.context()->dir();
562 stateAtEnd.setEorDir(dirCurrent);
563 stateAtEnd.setLastDir(dirCurrent);
564 stateAtEnd.setLastStrongDir(dirCurrent);
568 dirCurrent = c->dir();
571 dirCurrent = m_current.direction();
572 if (context()->override()
573 && dirCurrent != RightToLeftEmbedding
574 && dirCurrent != LeftToRightEmbedding
575 && dirCurrent != RightToLeftOverride
576 && dirCurrent != LeftToRightOverride
577 && dirCurrent != PopDirectionalFormat)
578 dirCurrent = context()->dir();
579 else if (dirCurrent == NonSpacingMark)
580 dirCurrent = m_status.last;
583 // We ignore all character directionality while in unicode-bidi: isolate spans.
584 // We'll handle ordering the isolated characters in a second pass.
586 dirCurrent = OtherNeutral;
588 ASSERT(m_status.eor != OtherNeutral || m_eor.atEnd());
589 switch (dirCurrent) {
591 // embedding and overrides (X1-X9 in the Bidi specs)
592 case RightToLeftEmbedding:
593 case LeftToRightEmbedding:
594 case RightToLeftOverride:
595 case LeftToRightOverride:
596 case PopDirectionalFormat:
597 embed(dirCurrent, FromUnicode);
598 commitExplicitEmbedding();
603 switch(m_status.last) {
605 case RightToLeftArabic:
608 if (m_status.last != EuropeanNumber || m_status.lastStrong != LeftToRight)
613 case EuropeanNumberSeparator:
614 case EuropeanNumberTerminator:
615 case CommonNumberSeparator:
616 case BoundaryNeutral:
618 case SegmentSeparator:
619 case WhiteSpaceNeutral:
621 if (m_status.eor == EuropeanNumber) {
622 if (m_status.lastStrong != LeftToRight) {
623 // the numbers need to be on a higher embedding level, so let's close that run
624 m_direction = EuropeanNumber;
626 if (context()->dir() != LeftToRight) {
627 // the neutrals take the embedding direction, which is R
629 m_direction = RightToLeft;
633 } else if (m_status.eor == ArabicNumber) {
634 // Arabic numbers are always on a higher embedding level, so let's close that run
635 m_direction = ArabicNumber;
637 if (context()->dir() != LeftToRight) {
638 // the neutrals take the embedding direction, which is R
640 m_direction = RightToLeft;
643 } else if (m_status.lastStrong != LeftToRight) {
644 //last stuff takes embedding dir
645 if (context()->dir() == RightToLeft) {
647 m_direction = RightToLeft;
655 m_status.eor = LeftToRight;
656 m_status.lastStrong = LeftToRight;
657 m_direction = LeftToRight;
659 case RightToLeftArabic:
661 switch (m_status.last) {
667 case RightToLeftArabic:
669 case EuropeanNumberSeparator:
670 case EuropeanNumberTerminator:
671 case CommonNumberSeparator:
672 case BoundaryNeutral:
674 case SegmentSeparator:
675 case WhiteSpaceNeutral:
677 if (m_status.eor == EuropeanNumber) {
678 if (m_status.lastStrong == LeftToRight && context()->dir() == LeftToRight)
681 } else if (m_status.eor == ArabicNumber)
683 else if (m_status.lastStrong == LeftToRight) {
684 if (context()->dir() == LeftToRight)
692 m_status.eor = RightToLeft;
693 m_status.lastStrong = dirCurrent;
694 m_direction = RightToLeft;
700 if (m_status.lastStrong != RightToLeftArabic) {
701 // if last strong was AL change EN to AN
702 switch (m_status.last) {
707 case RightToLeftArabic:
711 m_direction = EuropeanNumber;
713 case EuropeanNumberSeparator:
714 case CommonNumberSeparator:
715 if (m_status.eor == EuropeanNumber)
717 case EuropeanNumberTerminator:
718 case BoundaryNeutral:
720 case SegmentSeparator:
721 case WhiteSpaceNeutral:
723 if (m_status.eor == EuropeanNumber) {
724 if (m_status.lastStrong == RightToLeft) {
725 // ENs on both sides behave like Rs, so the neutrals should be R.
726 // Terminate the EN run.
729 m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last;
730 m_direction = RightToLeft;
732 // Begin a new EN run.
733 m_direction = EuropeanNumber;
735 } else if (m_status.eor == ArabicNumber) {
736 // Terminate the AN run.
738 if (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft) {
740 m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last;
741 m_direction = RightToLeft;
743 // Begin a new EN run.
744 m_direction = EuropeanNumber;
746 } else if (m_status.lastStrong == RightToLeft) {
747 // Extend the R run to include the neutrals.
748 m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last;
749 m_direction = RightToLeft;
751 // Begin a new EN run.
752 m_direction = EuropeanNumber;
758 m_status.eor = EuropeanNumber;
759 if (m_direction == OtherNeutral)
760 m_direction = LeftToRight;
764 dirCurrent = ArabicNumber;
765 switch (m_status.last) {
767 if (context()->dir() == LeftToRight)
773 case RightToLeftArabic:
778 case CommonNumberSeparator:
779 if (m_status.eor == ArabicNumber)
781 case EuropeanNumberSeparator:
782 case EuropeanNumberTerminator:
783 case BoundaryNeutral:
785 case SegmentSeparator:
786 case WhiteSpaceNeutral:
788 if (m_status.eor == ArabicNumber
789 || (m_status.eor == EuropeanNumber && (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft))
790 || (m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && context()->dir() == RightToLeft)) {
791 // Terminate the run before the neutrals.
793 // Begin an R run for the neutrals.
794 m_direction = RightToLeft;
795 } else if (m_direction == OtherNeutral)
796 m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft;
803 m_status.eor = ArabicNumber;
804 if (m_direction == OtherNeutral)
805 m_direction = ArabicNumber;
807 case EuropeanNumberSeparator:
808 case CommonNumberSeparator:
810 case EuropeanNumberTerminator:
811 if (m_status.last == EuropeanNumber) {
812 dirCurrent = EuropeanNumber;
814 m_status.eor = dirCurrent;
815 } else if (m_status.last != EuropeanNumberTerminator)
816 m_lastBeforeET = m_emptyRun ? m_eor : m_last;
819 // boundary neutrals should be ignored
820 case BoundaryNeutral:
826 // ### what do we do with newline and paragraph seperators that come to here?
828 case SegmentSeparator:
829 // ### implement rule L1
831 case WhiteSpaceNeutral:
839 if (pastEnd && m_eor == m_current) {
840 if (!m_reachedEndOfLine) {
842 switch (m_status.eor) {
846 m_direction = m_status.eor;
849 m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : EuropeanNumber;
857 m_status = stateAtEnd.m_status;
858 m_sor = stateAtEnd.m_sor;
859 m_eor = stateAtEnd.m_eor;
860 m_last = stateAtEnd.m_last;
861 m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine;
862 m_lastBeforeET = stateAtEnd.m_lastBeforeET;
863 m_emptyRun = stateAtEnd.m_emptyRun;
864 m_direction = OtherNeutral;
868 updateStatusLastFromCurrentDirection(dirCurrent);
877 if (!m_currentExplicitEmbeddingSequence.isEmpty()) {
878 bool committed = commitExplicitEmbedding();
879 if (committed && pastEnd) {
881 m_status = stateAtEnd.m_status;
882 m_sor = stateAtEnd.m_sor;
883 m_eor = stateAtEnd.m_eor;
884 m_last = stateAtEnd.m_last;
885 m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine;
886 m_lastBeforeET = stateAtEnd.m_lastBeforeET;
887 m_emptyRun = stateAtEnd.m_emptyRun;
888 m_direction = OtherNeutral;
893 if (!pastEnd && (m_current == end || m_current.atEnd())) {
896 stateAtEnd.m_status = m_status;
897 stateAtEnd.m_sor = m_sor;
898 stateAtEnd.m_eor = m_eor;
899 stateAtEnd.m_last = m_last;
900 stateAtEnd.m_reachedEndOfLine = m_reachedEndOfLine;
901 stateAtEnd.m_lastBeforeET = m_lastBeforeET;
902 stateAtEnd.m_emptyRun = m_emptyRun;
908 m_runs.setLogicallyLastRun(m_runs.lastRun());
909 reorderRunsFromLevels();
910 endOfLine = Iterator();
913 } // namespace WebCore
915 #endif // BidiResolver_h