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>
36 template <class Iterator> class MidpointState {
47 betweenMidpoints = false;
50 // The goal is to reuse the line state across multiple
51 // lines so we just keep an array around for midpoints and never clear it across multiple
52 // lines. We track the number of items and position using the two other variables.
53 Vector<Iterator> midpoints;
54 unsigned numMidpoints;
55 unsigned currentMidpoint;
56 bool betweenMidpoints;
58 void startIgnoringSpaces(const Iterator& midpoint)
60 ASSERT(!(numMidpoints % 2));
61 deprecatedAddMidpoint(midpoint);
64 void stopIgnoringSpaces(const Iterator& midpoint)
66 ASSERT(numMidpoints % 2);
67 deprecatedAddMidpoint(midpoint);
70 // When ignoring spaces, this needs to be called for objects that need line boxes such as RenderInlines or
71 // hard line breaks to ensure that they're not ignored.
72 void ensureLineBoxInsideIgnoredSpaces(RenderObject* renderer)
74 Iterator midpoint(0, renderer, 0);
75 stopIgnoringSpaces(midpoint);
76 startIgnoringSpaces(midpoint);
79 void deprecatedAddMidpoint(const Iterator& midpoint)
81 if (midpoints.size() <= numMidpoints)
82 midpoints.grow(numMidpoints + 10);
84 Iterator* midpointsIterator = midpoints.data();
85 midpointsIterator[numMidpoints++] = midpoint;
89 // The BidiStatus at a given position (typically the end of a line) can
90 // be cached and then used to restart bidi resolution at that position.
93 : eor(U_OTHER_NEUTRAL)
94 , lastStrong(U_OTHER_NEUTRAL)
95 , last(U_OTHER_NEUTRAL)
99 // Creates a BidiStatus representing a new paragraph root with a default direction.
100 // Uses TextDirection as it only has two possibilities instead of UCharDirection which has at least 19.
101 BidiStatus(TextDirection textDirection, bool isOverride)
103 UCharDirection direction = textDirection == LTR ? U_LEFT_TO_RIGHT : U_RIGHT_TO_LEFT;
104 eor = lastStrong = last = direction;
105 context = BidiContext::create(textDirection == LTR ? 0 : 1, direction, isOverride);
108 BidiStatus(UCharDirection eorDir, UCharDirection lastStrongDir, UCharDirection lastDir, PassRefPtr<BidiContext> bidiContext)
110 , lastStrong(lastStrongDir)
112 , context(bidiContext)
117 UCharDirection lastStrong;
119 RefPtr<BidiContext> context;
122 class BidiEmbedding {
124 BidiEmbedding(UCharDirection direction, BidiEmbeddingSource source)
125 : m_direction(direction)
130 UCharDirection direction() const { return m_direction; }
131 BidiEmbeddingSource source() const { return m_source; }
133 UCharDirection m_direction;
134 BidiEmbeddingSource m_source;
137 inline bool operator==(const BidiStatus& status1, const BidiStatus& status2)
139 return status1.eor == status2.eor && status1.last == status2.last && status1.lastStrong == status2.lastStrong && *(status1.context) == *(status2.context);
142 inline bool operator!=(const BidiStatus& status1, const BidiStatus& status2)
144 return !(status1 == status2);
147 struct BidiCharacterRun {
148 BidiCharacterRun(int start, int stop, BidiContext* context, UCharDirection direction)
149 : m_override(context->override())
154 if (direction == U_OTHER_NEUTRAL)
155 direction = context->dir();
157 m_level = context->level();
159 // add level of run (cases I1 & I2)
161 if (direction == U_LEFT_TO_RIGHT || direction == U_ARABIC_NUMBER || direction == U_EUROPEAN_NUMBER)
164 if (direction == U_RIGHT_TO_LEFT)
166 else if (direction == U_ARABIC_NUMBER || direction == U_EUROPEAN_NUMBER)
171 int start() const { return m_start; }
172 int stop() const { return m_stop; }
173 unsigned char level() const { return m_level; }
174 bool reversed(bool visuallyOrdered) { return m_level % 2 && !visuallyOrdered; }
175 bool dirOverride(bool visuallyOrdered) { return m_override || visuallyOrdered; }
177 BidiCharacterRun* next() const { return m_next; }
178 void setNext(BidiCharacterRun* next) { m_next = next; }
180 // Do not add anything apart from bitfields until after m_next. See https://bugs.webkit.org/show_bug.cgi?id=100173
182 bool m_hasHyphen : 1; // Used by BidiRun subclass which is a layering violation but enables us to save 8 bytes per object on 64-bit.
183 #if ENABLE(CSS_SHAPES)
184 bool m_startsSegment : 1; // Same comment as m_hasHyphen.
186 unsigned char m_level;
187 BidiCharacterRun* m_next;
192 enum VisualDirectionOverride {
194 VisualLeftToRightOverride,
195 VisualRightToLeftOverride
198 // BidiResolver is WebKit's implementation of the Unicode Bidi Algorithm
199 // http://unicode.org/reports/tr9
200 template <class Iterator, class Run> class BidiResolver {
201 WTF_MAKE_NONCOPYABLE(BidiResolver);
204 : m_direction(U_OTHER_NEUTRAL)
205 , m_reachedEndOfLine(false)
207 , m_nestedIsolateCount(0)
215 const Iterator& position() const { return m_current; }
216 void setPositionIgnoringNestedIsolates(const Iterator& position) { m_current = position; }
217 void setPosition(const Iterator& position, unsigned nestedIsolatedCount)
219 m_current = position;
220 m_nestedIsolateCount = nestedIsolatedCount;
223 void increment() { m_current.increment(); }
225 BidiContext* context() const { return m_status.context.get(); }
226 void setContext(PassRefPtr<BidiContext> c) { m_status.context = c; }
228 void setLastDir(UCharDirection lastDir) { m_status.last = lastDir; }
229 void setLastStrongDir(UCharDirection lastStrongDir) { m_status.lastStrong = lastStrongDir; }
230 void setEorDir(UCharDirection eorDir) { m_status.eor = eorDir; }
232 UCharDirection dir() const { return m_direction; }
233 void setDir(UCharDirection d) { m_direction = d; }
235 const BidiStatus& status() const { return m_status; }
236 void setStatus(const BidiStatus s) { m_status = s; }
238 MidpointState<Iterator>& midpointState() { return m_midpointState; }
240 // The current algorithm handles nested isolates one layer of nesting at a time.
241 // But when we layout each isolated span, we will walk into (and ignore) all
242 // child isolated spans.
243 void enterIsolate() { m_nestedIsolateCount++; }
244 void exitIsolate() { ASSERT(m_nestedIsolateCount >= 1); m_nestedIsolateCount--; }
245 bool inIsolate() const { return m_nestedIsolateCount; }
247 void embed(UCharDirection, BidiEmbeddingSource);
248 bool commitExplicitEmbedding();
250 void createBidiRunsForLine(const Iterator& end, VisualDirectionOverride = NoVisualOverride, bool hardLineBreak = false);
252 BidiRunList<Run>& runs() { return m_runs; }
254 // FIXME: This used to be part of deleteRuns() but was a layering violation.
255 // It's unclear if this is still needed.
256 void markCurrentRunEmpty() { m_emptyRun = true; }
258 Vector<Run*>& isolatedRuns() { return m_isolatedRuns; }
261 // FIXME: Instead of InlineBidiResolvers subclassing this method, we should
262 // pass in some sort of Traits object which knows how to create runs for appending.
266 // sor and eor are "start of run" and "end of run" respectively and correpond
267 // to abreviations used in UBA spec: http://unicode.org/reports/tr9/#BD7
268 Iterator m_sor; // Points to the first character in the current run.
269 Iterator m_eor; // Points to the last character in the current run.
272 UCharDirection m_direction;
274 bool m_reachedEndOfLine;
275 Iterator m_lastBeforeET; // Before a U_EUROPEAN_NUMBER_TERMINATOR
278 // FIXME: This should not belong to the resolver, but rather be passed
279 // into createBidiRunsForLine by the caller.
280 BidiRunList<Run> m_runs;
282 MidpointState<Iterator> m_midpointState;
284 unsigned m_nestedIsolateCount;
285 Vector<Run*> m_isolatedRuns;
288 void raiseExplicitEmbeddingLevel(UCharDirection from, UCharDirection to);
289 void lowerExplicitEmbeddingLevel(UCharDirection from);
290 void checkDirectionInLowerRaiseEmbeddingLevel();
292 void updateStatusLastFromCurrentDirection(UCharDirection);
293 void reorderRunsFromLevels();
295 Vector<BidiEmbedding, 8> m_currentExplicitEmbeddingSequence;
299 template <class Iterator, class Run>
300 BidiResolver<Iterator, Run>::~BidiResolver()
302 // The owner of this resolver should have handled the isolated runs
303 // or should never have called enterIsolate().
304 ASSERT(m_isolatedRuns.isEmpty());
305 ASSERT(!m_nestedIsolateCount);
309 template <class Iterator, class Run>
310 void BidiResolver<Iterator, Run>::appendRun()
312 if (!m_emptyRun && !m_eor.atEnd()) {
313 unsigned startOffset = m_sor.offset();
314 unsigned endOffset = m_eor.offset();
316 if (!endOfLine.atEnd() && endOffset >= endOfLine.offset()) {
317 m_reachedEndOfLine = true;
318 endOffset = endOfLine.offset();
321 if (endOffset >= startOffset)
322 m_runs.addRun(new Run(startOffset, endOffset + 1, context(), m_direction));
328 m_direction = U_OTHER_NEUTRAL;
329 m_status.eor = U_OTHER_NEUTRAL;
332 template <class Iterator, class Run>
333 void BidiResolver<Iterator, Run>::embed(UCharDirection dir, BidiEmbeddingSource source)
335 // Isolated spans compute base directionality during their own UBA run.
336 // Do not insert fake embed characters once we enter an isolated span.
337 ASSERT(!inIsolate());
339 ASSERT(dir == U_POP_DIRECTIONAL_FORMAT || dir == U_LEFT_TO_RIGHT_EMBEDDING || dir == U_LEFT_TO_RIGHT_OVERRIDE || dir == U_RIGHT_TO_LEFT_EMBEDDING || dir == U_RIGHT_TO_LEFT_OVERRIDE);
340 m_currentExplicitEmbeddingSequence.append(BidiEmbedding(dir, source));
343 template <class Iterator, class Run>
344 void BidiResolver<Iterator, Run>::checkDirectionInLowerRaiseEmbeddingLevel()
346 ASSERT(m_status.eor != U_OTHER_NEUTRAL || m_eor.atEnd());
347 ASSERT(m_status.last != U_DIR_NON_SPACING_MARK
348 && m_status.last != U_BOUNDARY_NEUTRAL
349 && m_status.last != U_RIGHT_TO_LEFT_EMBEDDING
350 && m_status.last != U_LEFT_TO_RIGHT_EMBEDDING
351 && m_status.last != U_RIGHT_TO_LEFT_OVERRIDE
352 && m_status.last != U_LEFT_TO_RIGHT_OVERRIDE
353 && m_status.last != U_POP_DIRECTIONAL_FORMAT);
354 if (m_direction == U_OTHER_NEUTRAL)
355 m_direction = m_status.lastStrong == U_LEFT_TO_RIGHT ? U_LEFT_TO_RIGHT : U_RIGHT_TO_LEFT;
358 template <class Iterator, class Run>
359 void BidiResolver<Iterator, Run>::lowerExplicitEmbeddingLevel(UCharDirection from)
361 if (!m_emptyRun && m_eor != m_last) {
362 checkDirectionInLowerRaiseEmbeddingLevel();
363 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
364 if (from == U_LEFT_TO_RIGHT) {
365 // bidi.sor ... bidi.eor ... bidi.last L
366 if (m_status.eor == U_EUROPEAN_NUMBER) {
367 if (m_status.lastStrong != U_LEFT_TO_RIGHT) {
368 m_direction = U_EUROPEAN_NUMBER;
371 } else if (m_status.eor == U_ARABIC_NUMBER) {
372 m_direction = U_ARABIC_NUMBER;
374 } else if (m_status.lastStrong != U_LEFT_TO_RIGHT) {
376 m_direction = U_LEFT_TO_RIGHT;
378 } else if (m_status.eor == U_EUROPEAN_NUMBER || m_status.eor == U_ARABIC_NUMBER || m_status.lastStrong == U_LEFT_TO_RIGHT) {
380 m_direction = U_RIGHT_TO_LEFT;
388 // sor for the new run is determined by the higher level (rule X10)
390 setLastStrongDir(from);
394 template <class Iterator, class Run>
395 void BidiResolver<Iterator, Run>::raiseExplicitEmbeddingLevel(UCharDirection from, UCharDirection to)
397 if (!m_emptyRun && m_eor != m_last) {
398 checkDirectionInLowerRaiseEmbeddingLevel();
399 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
400 if (to == U_LEFT_TO_RIGHT) {
401 // bidi.sor ... bidi.eor ... bidi.last L
402 if (m_status.eor == U_EUROPEAN_NUMBER) {
403 if (m_status.lastStrong != U_LEFT_TO_RIGHT) {
404 m_direction = U_EUROPEAN_NUMBER;
407 } else if (m_status.eor == U_ARABIC_NUMBER) {
408 m_direction = U_ARABIC_NUMBER;
410 } else if (m_status.lastStrong != U_LEFT_TO_RIGHT && from == U_LEFT_TO_RIGHT) {
412 m_direction = U_LEFT_TO_RIGHT;
414 } else if (m_status.eor == U_ARABIC_NUMBER
415 || (m_status.eor == U_EUROPEAN_NUMBER && (m_status.lastStrong != U_LEFT_TO_RIGHT || from == U_RIGHT_TO_LEFT))
416 || (m_status.eor != U_EUROPEAN_NUMBER && m_status.lastStrong == U_LEFT_TO_RIGHT && from == U_RIGHT_TO_LEFT)) {
418 m_direction = U_RIGHT_TO_LEFT;
427 setLastStrongDir(to);
431 template <class Iterator, class Run>
432 bool BidiResolver<Iterator, Run>::commitExplicitEmbedding()
434 // When we're "inIsolate()" we're resolving the parent context which
435 // ignores (skips over) the isolated content, including embedding levels.
436 // We should never accrue embedding levels while skipping over isolated content.
437 ASSERT(!inIsolate() || m_currentExplicitEmbeddingSequence.isEmpty());
439 unsigned char fromLevel = context()->level();
440 RefPtr<BidiContext> toContext = context();
442 for (size_t i = 0; i < m_currentExplicitEmbeddingSequence.size(); ++i) {
443 BidiEmbedding embedding = m_currentExplicitEmbeddingSequence[i];
444 if (embedding.direction() == U_POP_DIRECTIONAL_FORMAT) {
445 if (BidiContext* parentContext = toContext->parent())
446 toContext = parentContext;
448 UCharDirection direction = (embedding.direction() == U_RIGHT_TO_LEFT_EMBEDDING || embedding.direction() == U_RIGHT_TO_LEFT_OVERRIDE) ? U_RIGHT_TO_LEFT : U_LEFT_TO_RIGHT;
449 bool override = embedding.direction() == U_LEFT_TO_RIGHT_OVERRIDE || embedding.direction() == U_RIGHT_TO_LEFT_OVERRIDE;
450 unsigned char level = toContext->level();
451 if (direction == U_RIGHT_TO_LEFT)
452 level = nextGreaterOddLevel(level);
454 level = nextGreaterEvenLevel(level);
456 toContext = BidiContext::create(level, direction, override, embedding.source(), toContext.get());
460 unsigned char toLevel = toContext->level();
462 if (toLevel > fromLevel)
463 raiseExplicitEmbeddingLevel(fromLevel % 2 ? U_RIGHT_TO_LEFT : U_LEFT_TO_RIGHT, toLevel % 2 ? U_RIGHT_TO_LEFT : U_LEFT_TO_RIGHT);
464 else if (toLevel < fromLevel)
465 lowerExplicitEmbeddingLevel(fromLevel % 2 ? U_RIGHT_TO_LEFT : U_LEFT_TO_RIGHT);
467 setContext(toContext);
469 m_currentExplicitEmbeddingSequence.clear();
471 return fromLevel != toLevel;
474 template <class Iterator, class Run>
475 inline void BidiResolver<Iterator, Run>::updateStatusLastFromCurrentDirection(UCharDirection dirCurrent)
477 switch (dirCurrent) {
478 case U_EUROPEAN_NUMBER_TERMINATOR:
479 if (m_status.last != U_EUROPEAN_NUMBER)
480 m_status.last = U_EUROPEAN_NUMBER_TERMINATOR;
482 case U_EUROPEAN_NUMBER_SEPARATOR:
483 case U_COMMON_NUMBER_SEPARATOR:
484 case U_SEGMENT_SEPARATOR:
485 case U_WHITE_SPACE_NEUTRAL:
486 case U_OTHER_NEUTRAL:
487 switch (m_status.last) {
488 case U_LEFT_TO_RIGHT:
489 case U_RIGHT_TO_LEFT:
490 case U_RIGHT_TO_LEFT_ARABIC:
491 case U_EUROPEAN_NUMBER:
492 case U_ARABIC_NUMBER:
493 m_status.last = dirCurrent;
496 m_status.last = U_OTHER_NEUTRAL;
499 case U_DIR_NON_SPACING_MARK:
500 case U_BOUNDARY_NEUTRAL:
501 case U_RIGHT_TO_LEFT_EMBEDDING:
502 case U_LEFT_TO_RIGHT_EMBEDDING:
503 case U_RIGHT_TO_LEFT_OVERRIDE:
504 case U_LEFT_TO_RIGHT_OVERRIDE:
505 case U_POP_DIRECTIONAL_FORMAT:
508 case U_EUROPEAN_NUMBER:
511 m_status.last = dirCurrent;
515 template <class Iterator, class Run>
516 inline void BidiResolver<Iterator, Run>::reorderRunsFromLevels()
518 unsigned char levelLow = 128;
519 unsigned char levelHigh = 0;
520 for (Run* run = m_runs.firstRun(); run; run = run->next()) {
521 levelHigh = std::max(run->level(), levelHigh);
522 levelLow = std::min(run->level(), levelLow);
525 // This implements reordering of the line (L2 according to Bidi spec):
526 // http://unicode.org/reports/tr9/#L2
527 // L2. From the highest level found in the text to the lowest odd level on each line,
528 // reverse any contiguous sequence of characters that are at that level or higher.
530 // Reversing is only done up to the lowest odd level.
534 unsigned count = m_runs.runCount() - 1;
536 while (levelHigh >= levelLow) {
538 Run* run = m_runs.firstRun();
540 for (;i < count && run && run->level() < levelHigh; i++)
543 for (;i <= count && run && run->level() >= levelHigh; i++)
545 unsigned end = i - 1;
546 m_runs.reverseRuns(start, end);
552 template <class Iterator, class Run>
553 void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& end, VisualDirectionOverride override, bool hardLineBreak)
555 ASSERT(m_direction == U_OTHER_NEUTRAL);
557 if (override != NoVisualOverride) {
561 while (m_current != end && !m_current.atEnd()) {
565 m_direction = override == VisualLeftToRightOverride ? U_LEFT_TO_RIGHT : U_RIGHT_TO_LEFT;
567 m_runs.setLogicallyLastRun(m_runs.lastRun());
568 if (override == VisualRightToLeftOverride && m_runs.runCount())
569 m_runs.reverseRuns(0, m_runs.runCount() - 1);
578 bool pastEnd = false;
579 BidiResolver<Iterator, Run> stateAtEnd;
582 UCharDirection dirCurrent;
583 if (pastEnd && (hardLineBreak || m_current.atEnd())) {
584 BidiContext* c = context();
586 // A deviation from the Unicode Bidi Algorithm in order to match
587 // WinIE and user expectations: hard line breaks reset bidi state
588 // coming from unicode bidi control characters, but not those from
589 // DOM nodes with specified directionality
590 stateAtEnd.setContext(c->copyStackRemovingUnicodeEmbeddingContexts());
592 dirCurrent = stateAtEnd.context()->dir();
593 stateAtEnd.setEorDir(dirCurrent);
594 stateAtEnd.setLastDir(dirCurrent);
595 stateAtEnd.setLastStrongDir(dirCurrent);
599 dirCurrent = c->dir();
602 dirCurrent = m_current.direction();
603 if (context()->override()
604 && dirCurrent != U_RIGHT_TO_LEFT_EMBEDDING
605 && dirCurrent != U_LEFT_TO_RIGHT_EMBEDDING
606 && dirCurrent != U_RIGHT_TO_LEFT_OVERRIDE
607 && dirCurrent != U_LEFT_TO_RIGHT_OVERRIDE
608 && dirCurrent != U_POP_DIRECTIONAL_FORMAT)
609 dirCurrent = context()->dir();
610 else if (dirCurrent == U_DIR_NON_SPACING_MARK)
611 dirCurrent = m_status.last;
614 // We ignore all character directionality while in unicode-bidi: isolate spans.
615 // We'll handle ordering the isolated characters in a second pass.
617 dirCurrent = U_OTHER_NEUTRAL;
619 ASSERT(m_status.eor != U_OTHER_NEUTRAL || m_eor.atEnd());
620 switch (dirCurrent) {
622 // embedding and overrides (X1-X9 in the Bidi specs)
623 case U_RIGHT_TO_LEFT_EMBEDDING:
624 case U_LEFT_TO_RIGHT_EMBEDDING:
625 case U_RIGHT_TO_LEFT_OVERRIDE:
626 case U_LEFT_TO_RIGHT_OVERRIDE:
627 case U_POP_DIRECTIONAL_FORMAT:
628 embed(dirCurrent, FromUnicode);
629 commitExplicitEmbedding();
633 case U_LEFT_TO_RIGHT:
634 switch(m_status.last) {
635 case U_RIGHT_TO_LEFT:
636 case U_RIGHT_TO_LEFT_ARABIC:
637 case U_EUROPEAN_NUMBER:
638 case U_ARABIC_NUMBER:
639 if (m_status.last != U_EUROPEAN_NUMBER || m_status.lastStrong != U_LEFT_TO_RIGHT)
642 case U_LEFT_TO_RIGHT:
644 case U_EUROPEAN_NUMBER_SEPARATOR:
645 case U_EUROPEAN_NUMBER_TERMINATOR:
646 case U_COMMON_NUMBER_SEPARATOR:
647 case U_BOUNDARY_NEUTRAL:
648 case U_BLOCK_SEPARATOR:
649 case U_SEGMENT_SEPARATOR:
650 case U_WHITE_SPACE_NEUTRAL:
651 case U_OTHER_NEUTRAL:
652 if (m_status.eor == U_EUROPEAN_NUMBER) {
653 if (m_status.lastStrong != U_LEFT_TO_RIGHT) {
654 // the numbers need to be on a higher embedding level, so let's close that run
655 m_direction = U_EUROPEAN_NUMBER;
657 if (context()->dir() != U_LEFT_TO_RIGHT) {
658 // the neutrals take the embedding direction, which is R
660 m_direction = U_RIGHT_TO_LEFT;
664 } else if (m_status.eor == U_ARABIC_NUMBER) {
665 // Arabic numbers are always on a higher embedding level, so let's close that run
666 m_direction = U_ARABIC_NUMBER;
668 if (context()->dir() != U_LEFT_TO_RIGHT) {
669 // the neutrals take the embedding direction, which is R
671 m_direction = U_RIGHT_TO_LEFT;
674 } else if (m_status.lastStrong != U_LEFT_TO_RIGHT) {
675 //last stuff takes embedding dir
676 if (context()->dir() == U_RIGHT_TO_LEFT) {
678 m_direction = U_RIGHT_TO_LEFT;
686 m_status.eor = U_LEFT_TO_RIGHT;
687 m_status.lastStrong = U_LEFT_TO_RIGHT;
688 m_direction = U_LEFT_TO_RIGHT;
690 case U_RIGHT_TO_LEFT_ARABIC:
691 case U_RIGHT_TO_LEFT:
692 switch (m_status.last) {
693 case U_LEFT_TO_RIGHT:
694 case U_EUROPEAN_NUMBER:
695 case U_ARABIC_NUMBER:
697 case U_RIGHT_TO_LEFT:
698 case U_RIGHT_TO_LEFT_ARABIC:
700 case U_EUROPEAN_NUMBER_SEPARATOR:
701 case U_EUROPEAN_NUMBER_TERMINATOR:
702 case U_COMMON_NUMBER_SEPARATOR:
703 case U_BOUNDARY_NEUTRAL:
704 case U_BLOCK_SEPARATOR:
705 case U_SEGMENT_SEPARATOR:
706 case U_WHITE_SPACE_NEUTRAL:
707 case U_OTHER_NEUTRAL:
708 if (m_status.eor == U_EUROPEAN_NUMBER) {
709 if (m_status.lastStrong == U_LEFT_TO_RIGHT && context()->dir() == U_LEFT_TO_RIGHT)
712 } else if (m_status.eor == U_ARABIC_NUMBER)
714 else if (m_status.lastStrong == U_LEFT_TO_RIGHT) {
715 if (context()->dir() == U_LEFT_TO_RIGHT)
723 m_status.eor = U_RIGHT_TO_LEFT;
724 m_status.lastStrong = dirCurrent;
725 m_direction = U_RIGHT_TO_LEFT;
730 case U_EUROPEAN_NUMBER:
731 if (m_status.lastStrong != U_RIGHT_TO_LEFT_ARABIC) {
732 // if last strong was AL change EN to AN
733 switch (m_status.last) {
734 case U_EUROPEAN_NUMBER:
735 case U_LEFT_TO_RIGHT:
737 case U_RIGHT_TO_LEFT:
738 case U_RIGHT_TO_LEFT_ARABIC:
739 case U_ARABIC_NUMBER:
742 m_direction = U_EUROPEAN_NUMBER;
744 case U_EUROPEAN_NUMBER_SEPARATOR:
745 case U_COMMON_NUMBER_SEPARATOR:
746 if (m_status.eor == U_EUROPEAN_NUMBER)
748 case U_EUROPEAN_NUMBER_TERMINATOR:
749 case U_BOUNDARY_NEUTRAL:
750 case U_BLOCK_SEPARATOR:
751 case U_SEGMENT_SEPARATOR:
752 case U_WHITE_SPACE_NEUTRAL:
753 case U_OTHER_NEUTRAL:
754 if (m_status.eor == U_EUROPEAN_NUMBER) {
755 if (m_status.lastStrong == U_RIGHT_TO_LEFT) {
756 // ENs on both sides behave like Rs, so the neutrals should be R.
757 // Terminate the EN run.
760 m_eor = m_status.last == U_EUROPEAN_NUMBER_TERMINATOR ? m_lastBeforeET : m_last;
761 m_direction = U_RIGHT_TO_LEFT;
763 // Begin a new EN run.
764 m_direction = U_EUROPEAN_NUMBER;
766 } else if (m_status.eor == U_ARABIC_NUMBER) {
767 // Terminate the AN run.
769 if (m_status.lastStrong == U_RIGHT_TO_LEFT || context()->dir() == U_RIGHT_TO_LEFT) {
771 m_eor = m_status.last == U_EUROPEAN_NUMBER_TERMINATOR ? m_lastBeforeET : m_last;
772 m_direction = U_RIGHT_TO_LEFT;
774 // Begin a new EN run.
775 m_direction = U_EUROPEAN_NUMBER;
777 } else if (m_status.lastStrong == U_RIGHT_TO_LEFT) {
778 // Extend the R run to include the neutrals.
779 m_eor = m_status.last == U_EUROPEAN_NUMBER_TERMINATOR ? m_lastBeforeET : m_last;
780 m_direction = U_RIGHT_TO_LEFT;
782 // Begin a new EN run.
783 m_direction = U_EUROPEAN_NUMBER;
789 m_status.eor = U_EUROPEAN_NUMBER;
790 if (m_direction == U_OTHER_NEUTRAL)
791 m_direction = U_LEFT_TO_RIGHT;
794 case U_ARABIC_NUMBER:
795 dirCurrent = U_ARABIC_NUMBER;
796 switch (m_status.last) {
797 case U_LEFT_TO_RIGHT:
798 if (context()->dir() == U_LEFT_TO_RIGHT)
801 case U_ARABIC_NUMBER:
803 case U_RIGHT_TO_LEFT:
804 case U_RIGHT_TO_LEFT_ARABIC:
805 case U_EUROPEAN_NUMBER:
809 case U_COMMON_NUMBER_SEPARATOR:
810 if (m_status.eor == U_ARABIC_NUMBER)
812 case U_EUROPEAN_NUMBER_SEPARATOR:
813 case U_EUROPEAN_NUMBER_TERMINATOR:
814 case U_BOUNDARY_NEUTRAL:
815 case U_BLOCK_SEPARATOR:
816 case U_SEGMENT_SEPARATOR:
817 case U_WHITE_SPACE_NEUTRAL:
818 case U_OTHER_NEUTRAL:
819 if (m_status.eor == U_ARABIC_NUMBER
820 || (m_status.eor == U_EUROPEAN_NUMBER && (m_status.lastStrong == U_RIGHT_TO_LEFT || context()->dir() == U_RIGHT_TO_LEFT))
821 || (m_status.eor != U_EUROPEAN_NUMBER && m_status.lastStrong == U_LEFT_TO_RIGHT && context()->dir() == U_RIGHT_TO_LEFT)) {
822 // Terminate the run before the neutrals.
824 // Begin an R run for the neutrals.
825 m_direction = U_RIGHT_TO_LEFT;
826 } else if (m_direction == U_OTHER_NEUTRAL)
827 m_direction = m_status.lastStrong == U_LEFT_TO_RIGHT ? U_LEFT_TO_RIGHT : U_RIGHT_TO_LEFT;
834 m_status.eor = U_ARABIC_NUMBER;
835 if (m_direction == U_OTHER_NEUTRAL)
836 m_direction = U_ARABIC_NUMBER;
838 case U_EUROPEAN_NUMBER_SEPARATOR:
839 case U_COMMON_NUMBER_SEPARATOR:
841 case U_EUROPEAN_NUMBER_TERMINATOR:
842 if (m_status.last == U_EUROPEAN_NUMBER) {
843 dirCurrent = U_EUROPEAN_NUMBER;
845 m_status.eor = dirCurrent;
846 } else if (m_status.last != U_EUROPEAN_NUMBER_TERMINATOR)
847 m_lastBeforeET = m_emptyRun ? m_eor : m_last;
850 // boundary neutrals should be ignored
851 case U_BOUNDARY_NEUTRAL:
856 case U_BLOCK_SEPARATOR:
857 // FIXME: What do we do with newline and paragraph separators that come to here?
859 case U_SEGMENT_SEPARATOR:
860 // FIXME: Implement rule L1.
862 case U_WHITE_SPACE_NEUTRAL:
864 case U_OTHER_NEUTRAL:
870 if (pastEnd && m_eor == m_current) {
871 if (!m_reachedEndOfLine) {
873 switch (m_status.eor) {
874 case U_LEFT_TO_RIGHT:
875 case U_RIGHT_TO_LEFT:
876 case U_ARABIC_NUMBER:
877 m_direction = m_status.eor;
879 case U_EUROPEAN_NUMBER:
880 m_direction = m_status.lastStrong == U_LEFT_TO_RIGHT ? U_LEFT_TO_RIGHT : U_EUROPEAN_NUMBER;
883 ASSERT_NOT_REACHED();
888 m_status = stateAtEnd.m_status;
889 m_sor = stateAtEnd.m_sor;
890 m_eor = stateAtEnd.m_eor;
891 m_last = stateAtEnd.m_last;
892 m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine;
893 m_lastBeforeET = stateAtEnd.m_lastBeforeET;
894 m_emptyRun = stateAtEnd.m_emptyRun;
895 m_direction = U_OTHER_NEUTRAL;
899 updateStatusLastFromCurrentDirection(dirCurrent);
908 if (!m_currentExplicitEmbeddingSequence.isEmpty()) {
909 bool committed = commitExplicitEmbedding();
910 if (committed && pastEnd) {
912 m_status = stateAtEnd.m_status;
913 m_sor = stateAtEnd.m_sor;
914 m_eor = stateAtEnd.m_eor;
915 m_last = stateAtEnd.m_last;
916 m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine;
917 m_lastBeforeET = stateAtEnd.m_lastBeforeET;
918 m_emptyRun = stateAtEnd.m_emptyRun;
919 m_direction = U_OTHER_NEUTRAL;
924 if (!pastEnd && (m_current == end || m_current.atEnd())) {
927 stateAtEnd.m_status = m_status;
928 stateAtEnd.m_sor = m_sor;
929 stateAtEnd.m_eor = m_eor;
930 stateAtEnd.m_last = m_last;
931 stateAtEnd.m_reachedEndOfLine = m_reachedEndOfLine;
932 stateAtEnd.m_lastBeforeET = m_lastBeforeET;
933 stateAtEnd.m_emptyRun = m_emptyRun;
939 m_runs.setLogicallyLastRun(m_runs.lastRun());
940 reorderRunsFromLevels();
941 endOfLine = Iterator();
944 } // namespace WebCore
946 #endif // BidiResolver_h