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 <wtf/Noncopyable.h>
27 #include <wtf/PassRefPtr.h>
28 #include <wtf/Vector.h>
32 // The BidiStatus at a given position (typically the end of a line) can
33 // be cached and then used to restart bidi resolution at that position.
36 : eor(WTF::Unicode::OtherNeutral
)
37 , lastStrong(WTF::Unicode::OtherNeutral
)
38 , last(WTF::Unicode::OtherNeutral
)
42 BidiStatus(WTF::Unicode::Direction eorDir
, WTF::Unicode::Direction lastStrongDir
, WTF::Unicode::Direction lastDir
, PassRefPtr
<BidiContext
> bidiContext
)
44 , lastStrong(lastStrongDir
)
46 , context(bidiContext
)
50 WTF::Unicode::Direction eor
;
51 WTF::Unicode::Direction lastStrong
;
52 WTF::Unicode::Direction last
;
53 RefPtr
<BidiContext
> context
;
56 inline bool operator==(const BidiStatus
& status1
, const BidiStatus
& status2
)
58 return status1
.eor
== status2
.eor
&& status1
.last
== status2
.last
&& status1
.lastStrong
== status2
.lastStrong
&& *(status1
.context
) == *(status2
.context
);
61 inline bool operator!=(const BidiStatus
& status1
, const BidiStatus
& status2
)
63 return !(status1
== status2
);
66 struct BidiCharacterRun
{
67 BidiCharacterRun(int start
, int stop
, BidiContext
* context
, WTF::Unicode::Direction dir
)
70 , m_override(context
->override())
73 if (dir
== WTF::Unicode::OtherNeutral
)
76 m_level
= context
->level();
78 // add level of run (cases I1 & I2)
80 if (dir
== WTF::Unicode::LeftToRight
|| dir
== WTF::Unicode::ArabicNumber
|| dir
== WTF::Unicode::EuropeanNumber
)
83 if (dir
== WTF::Unicode::RightToLeft
)
85 else if (dir
== WTF::Unicode::ArabicNumber
|| dir
== WTF::Unicode::EuropeanNumber
)
90 void destroy() { delete this; }
92 int start() const { return m_start
; }
93 int stop() const { return m_stop
; }
94 unsigned char level() const { return m_level
; }
95 bool reversed(bool visuallyOrdered
) { return m_level
% 2 && !visuallyOrdered
; }
96 bool dirOverride(bool visuallyOrdered
) { return m_override
|| visuallyOrdered
; }
98 BidiCharacterRun
* next() const { return m_next
; }
100 unsigned char m_level
;
104 BidiCharacterRun
* m_next
;
107 template <class Iterator
, class Run
> class BidiResolver
: public Noncopyable
{
110 : m_direction(WTF::Unicode::OtherNeutral
)
111 , reachedEndOfLine(false)
115 , m_logicallyLastRun(0)
120 const Iterator
& position() const { return current
; }
121 void setPosition(const Iterator
& position
) { current
= position
; }
123 void increment() { current
.increment(); }
125 BidiContext
* context() const { return m_status
.context
.get(); }
126 void setContext(PassRefPtr
<BidiContext
> c
) { m_status
.context
= c
; }
128 void setLastDir(WTF::Unicode::Direction lastDir
) { m_status
.last
= lastDir
; }
129 void setLastStrongDir(WTF::Unicode::Direction lastStrongDir
) { m_status
.lastStrong
= lastStrongDir
; }
130 void setEorDir(WTF::Unicode::Direction eorDir
) { m_status
.eor
= eorDir
; }
132 WTF::Unicode::Direction
dir() const { return m_direction
; }
133 void setDir(WTF::Unicode::Direction d
) { m_direction
= d
; }
135 const BidiStatus
& status() const { return m_status
; }
136 void setStatus(const BidiStatus s
) { m_status
= s
; }
138 void embed(WTF::Unicode::Direction
);
139 void commitExplicitEmbedding();
141 void createBidiRunsForLine(const Iterator
& end
, bool visualOrder
= false, bool hardLineBreak
= false);
143 Run
* firstRun() const { return m_firstRun
; }
144 Run
* lastRun() const { return m_lastRun
; }
145 Run
* logicallyLastRun() const { return m_logicallyLastRun
; }
146 unsigned runCount() const { return m_runCount
; }
149 void prependRun(Run
*);
151 void moveRunToEnd(Run
*);
152 void moveRunToBeginning(Run
*);
158 void reverseRuns(unsigned start
, unsigned end
);
165 WTF::Unicode::Direction m_direction
;
167 bool reachedEndOfLine
;
168 Iterator lastBeforeET
;
173 Run
* m_logicallyLastRun
;
177 void raiseExplicitEmbeddingLevel(WTF::Unicode::Direction from
, WTF::Unicode::Direction to
);
178 void lowerExplicitEmbeddingLevel(WTF::Unicode::Direction from
);
180 Vector
<WTF::Unicode::Direction
, 8> m_currentExplicitEmbeddingSequence
;
183 template <class Iterator
, class Run
>
184 inline void BidiResolver
<Iterator
, Run
>::addRun(Run
* run
)
189 m_lastRun
->m_next
= run
;
194 template <class Iterator
, class Run
>
195 inline void BidiResolver
<Iterator
, Run
>::prependRun(Run
* run
)
197 ASSERT(!run
->m_next
);
202 run
->m_next
= m_firstRun
;
207 template <class Iterator
, class Run
>
208 inline void BidiResolver
<Iterator
, Run
>::moveRunToEnd(Run
* run
)
215 Run
* next
= m_firstRun
;
216 while (next
!= run
) {
218 next
= current
->next();
222 m_firstRun
= run
->next();
224 current
->m_next
= run
->m_next
;
227 m_lastRun
->m_next
= run
;
231 template <class Iterator
, class Run
>
232 inline void BidiResolver
<Iterator
, Run
>::moveRunToBeginning(Run
* run
)
236 ASSERT(run
!= m_firstRun
);
238 Run
* current
= m_firstRun
;
239 Run
* next
= current
->next();
240 while (next
!= run
) {
242 next
= current
->next();
245 current
->m_next
= run
->m_next
;
246 if (run
== m_lastRun
)
249 run
->m_next
= m_firstRun
;
253 template <class Iterator
, class Run
>
254 void BidiResolver
<Iterator
, Run
>::appendRun()
256 if (!emptyRun
&& !eor
.atEnd()) {
257 unsigned startOffset
= sor
.offset();
258 unsigned endOffset
= eor
.offset();
260 if (!endOfLine
.atEnd() && endOffset
>= endOfLine
.offset()) {
261 reachedEndOfLine
= true;
262 endOffset
= endOfLine
.offset();
265 if (endOffset
>= startOffset
)
266 addRun(new Run(startOffset
, endOffset
+ 1, context(), m_direction
));
272 m_direction
= WTF::Unicode::OtherNeutral
;
273 m_status
.eor
= WTF::Unicode::OtherNeutral
;
276 template <class Iterator
, class Run
>
277 void BidiResolver
<Iterator
, Run
>::embed(WTF::Unicode::Direction d
)
279 using namespace WTF::Unicode
;
281 ASSERT(d
== PopDirectionalFormat
|| d
== LeftToRightEmbedding
|| d
== LeftToRightOverride
|| d
== RightToLeftEmbedding
|| d
== RightToLeftOverride
);
282 m_currentExplicitEmbeddingSequence
.append(d
);
285 template <class Iterator
, class Run
>
286 void BidiResolver
<Iterator
, Run
>::lowerExplicitEmbeddingLevel(WTF::Unicode::Direction from
)
288 using namespace WTF::Unicode
;
290 if (!emptyRun
&& eor
!= last
) {
291 ASSERT(m_status
.eor
!= OtherNeutral
|| eor
.atEnd());
292 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
293 ASSERT(m_status
.last
== EuropeanNumberSeparator
294 || m_status
.last
== EuropeanNumberTerminator
295 || m_status
.last
== CommonNumberSeparator
296 || m_status
.last
== BoundaryNeutral
297 || m_status
.last
== BlockSeparator
298 || m_status
.last
== SegmentSeparator
299 || m_status
.last
== WhiteSpaceNeutral
300 || m_status
.last
== OtherNeutral
);
301 if (m_direction
== OtherNeutral
)
302 m_direction
= m_status
.lastStrong
== LeftToRight
? LeftToRight
: RightToLeft
;
303 if (from
== LeftToRight
) {
304 // bidi.sor ... bidi.eor ... bidi.last L
305 if (m_status
.eor
== EuropeanNumber
) {
306 if (m_status
.lastStrong
!= LeftToRight
) {
307 m_direction
= EuropeanNumber
;
310 } else if (m_status
.eor
== ArabicNumber
) {
311 m_direction
= ArabicNumber
;
313 } else if (m_status
.lastStrong
!= LeftToRight
) {
315 m_direction
= LeftToRight
;
317 } else if (m_status
.eor
== EuropeanNumber
|| m_status
.eor
== ArabicNumber
|| m_status
.lastStrong
== LeftToRight
) {
319 m_direction
= RightToLeft
;
325 // sor for the new run is determined by the higher level (rule X10)
327 setLastStrongDir(from
);
331 template <class Iterator
, class Run
>
332 void BidiResolver
<Iterator
, Run
>::raiseExplicitEmbeddingLevel(WTF::Unicode::Direction from
, WTF::Unicode::Direction to
)
334 using namespace WTF::Unicode
;
336 if (!emptyRun
&& eor
!= last
) {
337 ASSERT(m_status
.eor
!= OtherNeutral
|| eor
.atEnd());
338 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
339 ASSERT(m_status
.last
== EuropeanNumberSeparator
340 || m_status
.last
== EuropeanNumberTerminator
341 || m_status
.last
== CommonNumberSeparator
342 || m_status
.last
== BoundaryNeutral
343 || m_status
.last
== BlockSeparator
344 || m_status
.last
== SegmentSeparator
345 || m_status
.last
== WhiteSpaceNeutral
346 || m_status
.last
== OtherNeutral
);
347 if (m_direction
== OtherNeutral
)
348 m_direction
= m_status
.lastStrong
== LeftToRight
? LeftToRight
: RightToLeft
;
349 if (to
== LeftToRight
) {
350 // bidi.sor ... bidi.eor ... bidi.last L
351 if (m_status
.eor
== EuropeanNumber
) {
352 if (m_status
.lastStrong
!= LeftToRight
) {
353 m_direction
= EuropeanNumber
;
356 } else if (m_status
.eor
== ArabicNumber
) {
357 m_direction
= ArabicNumber
;
359 } else if (m_status
.lastStrong
!= LeftToRight
&& from
== LeftToRight
) {
361 m_direction
= LeftToRight
;
363 } else if (m_status
.eor
== ArabicNumber
364 || m_status
.eor
== EuropeanNumber
&& (m_status
.lastStrong
!= LeftToRight
|| from
== RightToLeft
)
365 || m_status
.eor
!= EuropeanNumber
&& m_status
.lastStrong
== LeftToRight
&& from
== RightToLeft
) {
367 m_direction
= RightToLeft
;
374 setLastStrongDir(to
);
378 template <class Iterator
, class Run
>
379 void BidiResolver
<Iterator
, Run
>::commitExplicitEmbedding()
381 using namespace WTF::Unicode
;
383 unsigned char fromLevel
= context()->level();
384 RefPtr
<BidiContext
> toContext
= context();
386 for (size_t i
= 0; i
< m_currentExplicitEmbeddingSequence
.size(); ++i
) {
387 Direction embedding
= m_currentExplicitEmbeddingSequence
[i
];
388 if (embedding
== PopDirectionalFormat
) {
389 if (BidiContext
* parentContext
= toContext
->parent())
390 toContext
= parentContext
;
392 Direction direction
= (embedding
== RightToLeftEmbedding
|| embedding
== RightToLeftOverride
) ? RightToLeft
: LeftToRight
;
393 bool override
= embedding
== LeftToRightOverride
|| embedding
== RightToLeftOverride
;
394 unsigned char level
= toContext
->level();
395 if (direction
== RightToLeft
) {
396 // Go to the least greater odd integer
400 // Go to the least greater even integer
405 toContext
= new BidiContext(level
, direction
, override
, toContext
.get());
409 unsigned char toLevel
= toContext
->level();
411 if (toLevel
> fromLevel
)
412 raiseExplicitEmbeddingLevel(fromLevel
% 2 ? RightToLeft
: LeftToRight
, toLevel
% 2 ? RightToLeft
: LeftToRight
);
413 else if (toLevel
< fromLevel
)
414 lowerExplicitEmbeddingLevel(fromLevel
% 2 ? RightToLeft
: LeftToRight
);
416 setContext(toContext
);
418 m_currentExplicitEmbeddingSequence
.clear();
421 template <class Iterator
, class Run
>
422 void BidiResolver
<Iterator
, Run
>::deleteRuns()
428 Run
* curr
= m_firstRun
;
430 Run
* s
= curr
->next();
440 template <class Iterator
, class Run
>
441 void BidiResolver
<Iterator
, Run
>::reverseRuns(unsigned start
, unsigned end
)
446 ASSERT(end
< m_runCount
);
448 // Get the item before the start of the runs to reverse and put it in
449 // |beforeStart|. |curr| should point to the first run to reverse.
450 Run
* curr
= m_firstRun
;
451 Run
* beforeStart
= 0;
459 Run
* startRun
= curr
;
465 Run
* afterEnd
= curr
->next();
469 Run
* newNext
= afterEnd
;
472 Run
* next
= curr
->next();
473 curr
->m_next
= newNext
;
479 // Now hook up beforeStart and afterEnd to the startRun and endRun.
481 beforeStart
->m_next
= endRun
;
485 startRun
->m_next
= afterEnd
;
487 m_lastRun
= startRun
;
490 template <class Iterator
, class Run
>
491 void BidiResolver
<Iterator
, Run
>::createBidiRunsForLine(const Iterator
& end
, bool visualOrder
, bool hardLineBreak
)
493 using namespace WTF::Unicode
;
495 ASSERT(m_direction
== OtherNeutral
);
502 bool pastEnd
= false;
503 BidiResolver
<Iterator
, Run
> stateAtEnd
;
506 Direction dirCurrent
;
507 if (pastEnd
&& (hardLineBreak
|| current
.atEnd())) {
508 BidiContext
* c
= context();
511 dirCurrent
= c
->dir();
513 // A deviation from the Unicode Bidi Algorithm in order to match
514 // Mac OS X text and WinIE: a hard line break resets bidi state.
515 stateAtEnd
.setContext(c
);
516 stateAtEnd
.setEorDir(dirCurrent
);
517 stateAtEnd
.setLastDir(dirCurrent
);
518 stateAtEnd
.setLastStrongDir(dirCurrent
);
521 dirCurrent
= current
.direction();
522 if (context()->override()
523 && dirCurrent
!= RightToLeftEmbedding
524 && dirCurrent
!= LeftToRightEmbedding
525 && dirCurrent
!= RightToLeftOverride
526 && dirCurrent
!= LeftToRightOverride
527 && dirCurrent
!= PopDirectionalFormat
)
528 dirCurrent
= context()->dir();
529 else if (dirCurrent
== NonSpacingMark
)
530 dirCurrent
= m_status
.last
;
533 ASSERT(m_status
.eor
!= OtherNeutral
|| eor
.atEnd());
534 switch (dirCurrent
) {
536 // embedding and overrides (X1-X9 in the Bidi specs)
537 case RightToLeftEmbedding
:
538 case LeftToRightEmbedding
:
539 case RightToLeftOverride
:
540 case LeftToRightOverride
:
541 case PopDirectionalFormat
:
543 commitExplicitEmbedding();
548 switch(m_status
.last
) {
550 case RightToLeftArabic
:
553 if (m_status
.last
!= EuropeanNumber
|| m_status
.lastStrong
!= LeftToRight
)
558 case EuropeanNumberSeparator
:
559 case EuropeanNumberTerminator
:
560 case CommonNumberSeparator
:
561 case BoundaryNeutral
:
563 case SegmentSeparator
:
564 case WhiteSpaceNeutral
:
566 if (m_status
.eor
== EuropeanNumber
) {
567 if (m_status
.lastStrong
!= LeftToRight
) {
568 // the numbers need to be on a higher embedding level, so let's close that run
569 m_direction
= EuropeanNumber
;
571 if (context()->dir() != LeftToRight
) {
572 // the neutrals take the embedding direction, which is R
574 m_direction
= RightToLeft
;
578 } else if (m_status
.eor
== ArabicNumber
) {
579 // Arabic numbers are always on a higher embedding level, so let's close that run
580 m_direction
= ArabicNumber
;
582 if (context()->dir() != LeftToRight
) {
583 // the neutrals take the embedding direction, which is R
585 m_direction
= RightToLeft
;
588 } else if (m_status
.lastStrong
!= LeftToRight
) {
589 //last stuff takes embedding dir
590 if (context()->dir() == RightToLeft
) {
592 m_direction
= RightToLeft
;
600 m_status
.eor
= LeftToRight
;
601 m_status
.lastStrong
= LeftToRight
;
602 m_direction
= LeftToRight
;
604 case RightToLeftArabic
:
606 switch (m_status
.last
) {
612 case RightToLeftArabic
:
614 case EuropeanNumberSeparator
:
615 case EuropeanNumberTerminator
:
616 case CommonNumberSeparator
:
617 case BoundaryNeutral
:
619 case SegmentSeparator
:
620 case WhiteSpaceNeutral
:
622 if (m_status
.eor
== EuropeanNumber
) {
623 if (m_status
.lastStrong
== LeftToRight
&& context()->dir() == LeftToRight
)
626 } else if (m_status
.eor
== ArabicNumber
)
628 else if (m_status
.lastStrong
== LeftToRight
) {
629 if (context()->dir() == LeftToRight
)
637 m_status
.eor
= RightToLeft
;
638 m_status
.lastStrong
= dirCurrent
;
639 m_direction
= RightToLeft
;
645 if (m_status
.lastStrong
!= RightToLeftArabic
) {
646 // if last strong was AL change EN to AN
647 switch (m_status
.last
) {
652 case RightToLeftArabic
:
656 m_direction
= EuropeanNumber
;
658 case EuropeanNumberSeparator
:
659 case CommonNumberSeparator
:
660 if (m_status
.eor
== EuropeanNumber
)
662 case EuropeanNumberTerminator
:
663 case BoundaryNeutral
:
665 case SegmentSeparator
:
666 case WhiteSpaceNeutral
:
668 if (m_status
.eor
== EuropeanNumber
) {
669 if (m_status
.lastStrong
== RightToLeft
) {
670 // ENs on both sides behave like Rs, so the neutrals should be R.
671 // Terminate the EN run.
674 eor
= m_status
.last
== EuropeanNumberTerminator
? lastBeforeET
: last
;
675 m_direction
= RightToLeft
;
677 // Begin a new EN run.
678 m_direction
= EuropeanNumber
;
680 } else if (m_status
.eor
== ArabicNumber
) {
681 // Terminate the AN run.
683 if (m_status
.lastStrong
== RightToLeft
|| context()->dir() == RightToLeft
) {
685 eor
= m_status
.last
== EuropeanNumberTerminator
? lastBeforeET
: last
;
686 m_direction
= RightToLeft
;
688 // Begin a new EN run.
689 m_direction
= EuropeanNumber
;
691 } else if (m_status
.lastStrong
== RightToLeft
) {
692 // Extend the R run to include the neutrals.
693 eor
= m_status
.last
== EuropeanNumberTerminator
? lastBeforeET
: last
;
694 m_direction
= RightToLeft
;
696 // Begin a new EN run.
697 m_direction
= EuropeanNumber
;
703 m_status
.eor
= EuropeanNumber
;
704 if (m_direction
== OtherNeutral
)
705 m_direction
= LeftToRight
;
709 dirCurrent
= ArabicNumber
;
710 switch (m_status
.last
) {
712 if (context()->dir() == LeftToRight
)
718 case RightToLeftArabic
:
723 case CommonNumberSeparator
:
724 if (m_status
.eor
== ArabicNumber
)
726 case EuropeanNumberSeparator
:
727 case EuropeanNumberTerminator
:
728 case BoundaryNeutral
:
730 case SegmentSeparator
:
731 case WhiteSpaceNeutral
:
733 if (m_status
.eor
== ArabicNumber
734 || m_status
.eor
== EuropeanNumber
&& (m_status
.lastStrong
== RightToLeft
|| context()->dir() == RightToLeft
)
735 || m_status
.eor
!= EuropeanNumber
&& m_status
.lastStrong
== LeftToRight
&& context()->dir() == RightToLeft
) {
736 // Terminate the run before the neutrals.
738 // Begin an R run for the neutrals.
739 m_direction
= RightToLeft
;
740 } else if (m_direction
== OtherNeutral
)
741 m_direction
= m_status
.lastStrong
== LeftToRight
? LeftToRight
: RightToLeft
;
748 m_status
.eor
= ArabicNumber
;
749 if (m_direction
== OtherNeutral
)
750 m_direction
= ArabicNumber
;
752 case EuropeanNumberSeparator
:
753 case CommonNumberSeparator
:
755 case EuropeanNumberTerminator
:
756 if (m_status
.last
== EuropeanNumber
) {
757 dirCurrent
= EuropeanNumber
;
759 m_status
.eor
= dirCurrent
;
760 } else if (m_status
.last
!= EuropeanNumberTerminator
)
761 lastBeforeET
= emptyRun
? eor
: last
;
764 // boundary neutrals should be ignored
765 case BoundaryNeutral
:
771 // ### what do we do with newline and paragraph seperators that come to here?
773 case SegmentSeparator
:
774 // ### implement rule L1
776 case WhiteSpaceNeutral
:
785 if (eor
== current
) {
786 if (!reachedEndOfLine
) {
788 switch (m_status
.eor
) {
792 m_direction
= m_status
.eor
;
795 m_direction
= m_status
.lastStrong
== LeftToRight
? LeftToRight
: EuropeanNumber
;
803 m_status
= stateAtEnd
.m_status
;
804 sor
= stateAtEnd
.sor
;
805 eor
= stateAtEnd
.eor
;
806 last
= stateAtEnd
.last
;
807 reachedEndOfLine
= stateAtEnd
.reachedEndOfLine
;
808 lastBeforeET
= stateAtEnd
.lastBeforeET
;
809 emptyRun
= stateAtEnd
.emptyRun
;
810 m_direction
= OtherNeutral
;
815 // set m_status.last as needed.
816 switch (dirCurrent
) {
817 case EuropeanNumberTerminator
:
818 if (m_status
.last
!= EuropeanNumber
)
819 m_status
.last
= EuropeanNumberTerminator
;
821 case EuropeanNumberSeparator
:
822 case CommonNumberSeparator
:
823 case SegmentSeparator
:
824 case WhiteSpaceNeutral
:
826 switch(m_status
.last
) {
829 case RightToLeftArabic
:
832 m_status
.last
= dirCurrent
;
835 m_status
.last
= OtherNeutral
;
839 case BoundaryNeutral
:
840 case RightToLeftEmbedding
:
841 case LeftToRightEmbedding
:
842 case RightToLeftOverride
:
843 case LeftToRightOverride
:
844 case PopDirectionalFormat
:
850 m_status
.last
= dirCurrent
;
855 if (emptyRun
&& !(dirCurrent
== RightToLeftEmbedding
856 || dirCurrent
== LeftToRightEmbedding
857 || dirCurrent
== RightToLeftOverride
858 || dirCurrent
== LeftToRightOverride
859 || dirCurrent
== PopDirectionalFormat
)) {
865 if (!m_currentExplicitEmbeddingSequence
.isEmpty())
866 commitExplicitEmbedding();
868 if (emptyRun
&& (dirCurrent
== RightToLeftEmbedding
869 || dirCurrent
== LeftToRightEmbedding
870 || dirCurrent
== RightToLeftOverride
871 || dirCurrent
== LeftToRightOverride
872 || dirCurrent
== PopDirectionalFormat
)) {
873 // exclude the embedding char itself from the new run so that ATSUI will never see it
879 if (!pastEnd
&& (current
== end
|| current
.atEnd())) {
882 stateAtEnd
.m_status
= m_status
;
883 stateAtEnd
.sor
= sor
;
884 stateAtEnd
.eor
= eor
;
885 stateAtEnd
.last
= last
;
886 stateAtEnd
.reachedEndOfLine
= reachedEndOfLine
;
887 stateAtEnd
.lastBeforeET
= lastBeforeET
;
888 stateAtEnd
.emptyRun
= emptyRun
;
894 m_logicallyLastRun
= m_lastRun
;
896 // reorder line according to run structure...
897 // do not reverse for visually ordered web sites
900 // first find highest and lowest levels
901 unsigned char levelLow
= 128;
902 unsigned char levelHigh
= 0;
905 if (r
->m_level
> levelHigh
)
906 levelHigh
= r
->m_level
;
907 if (r
->m_level
< levelLow
)
908 levelLow
= r
->m_level
;
912 // implements reordering of the line (L2 according to Bidi spec):
913 // L2. From the highest level found in the text to the lowest odd level on each line,
914 // reverse any contiguous sequence of characters that are at that level or higher.
916 // reversing is only done up to the lowest odd level
920 unsigned count
= runCount() - 1;
922 while (levelHigh
>= levelLow
) {
924 Run
* currRun
= firstRun();
926 while (i
< count
&& currRun
&& currRun
->m_level
< levelHigh
) {
928 currRun
= currRun
->next();
931 while (i
<= count
&& currRun
&& currRun
->m_level
>= levelHigh
) {
933 currRun
= currRun
->next();
935 unsigned end
= i
- 1;
936 reverseRuns(start
, end
);
941 endOfLine
= Iterator();
944 } // namespace WebCore
946 #endif // BidiResolver_h