2
* Copyright (C) 2000 Lars Knoll (knoll@kde.org)
3
* Copyright (C) 2003, 2004, 2006, 2007 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., 59 Temple Place - Suite 330,
18
* Boston, MA 02111-1307, USA.
22
#ifndef BidiResolver_h
23
#define BidiResolver_h
25
#include "BidiContext.h"
26
#include <wtf/PassRefPtr.h>
30
// The BidiStatus at a given position (typically the end of a line) can
31
// be cached and then used to restart bidi resolution at that position.
34
: eor(WTF::Unicode::OtherNeutral)
35
, lastStrong(WTF::Unicode::OtherNeutral)
36
, last(WTF::Unicode::OtherNeutral)
40
BidiStatus(WTF::Unicode::Direction eorDir, WTF::Unicode::Direction lastStrongDir, WTF::Unicode::Direction lastDir, PassRefPtr<BidiContext> bidiContext)
42
, lastStrong(lastStrongDir)
44
, context(bidiContext)
48
WTF::Unicode::Direction eor;
49
WTF::Unicode::Direction lastStrong;
50
WTF::Unicode::Direction last;
51
RefPtr<BidiContext> context;
54
inline bool operator==(const BidiStatus& status1, const BidiStatus& status2)
56
return status1.eor == status2.eor && status1.last == status2.last && status1.lastStrong == status2.lastStrong && *(status1.context) == *(status2.context);
59
inline bool operator!=(const BidiStatus& status1, const BidiStatus& status2)
61
return !(status1 == status2);
64
struct BidiCharacterRun {
65
BidiCharacterRun(int start, int stop, BidiContext* context, WTF::Unicode::Direction dir)
68
, m_override(context->override())
71
if (dir == WTF::Unicode::OtherNeutral)
74
m_level = context->level();
76
// add level of run (cases I1 & I2)
78
if (dir == WTF::Unicode::LeftToRight || dir == WTF::Unicode::ArabicNumber || dir == WTF::Unicode::EuropeanNumber)
81
if (dir == WTF::Unicode::RightToLeft)
83
else if (dir == WTF::Unicode::ArabicNumber || dir == WTF::Unicode::EuropeanNumber)
88
int start() const { return m_start; }
89
int stop() const { return m_stop; }
90
unsigned char level() const { return m_level; }
91
bool reversed(bool visuallyOrdered) { return m_level % 2 && !visuallyOrdered; }
92
bool dirOverride(bool visuallyOrdered) { return m_override || visuallyOrdered; }
94
BidiCharacterRun* next() const { return m_next; }
96
unsigned char m_level;
100
BidiCharacterRun* m_next;
103
template <class Iterator, class Run> class BidiResolver {
106
: m_direction(WTF::Unicode::OtherNeutral)
107
, m_adjustEmbedding(false)
108
, reachedEndOfLine(false)
116
BidiContext* context() const { return m_status.context.get(); }
117
void setContext(PassRefPtr<BidiContext> c) { m_status.context = c; }
119
void setLastDir(WTF::Unicode::Direction lastDir) { m_status.last = lastDir; }
120
void setLastStrongDir(WTF::Unicode::Direction lastStrongDir) { m_status.lastStrong = lastStrongDir; }
121
void setEorDir(WTF::Unicode::Direction eorDir) { m_status.eor = eorDir; }
123
WTF::Unicode::Direction dir() const { return m_direction; }
124
void setDir(WTF::Unicode::Direction d) { m_direction = d; }
126
const BidiStatus& status() const { return m_status; }
127
void setStatus(const BidiStatus s) { m_status = s; }
129
bool adjustEmbedding() const { return m_adjustEmbedding; }
130
void setAdjustEmbedding(bool adjsutEmbedding) { m_adjustEmbedding = adjsutEmbedding; }
132
void embed(WTF::Unicode::Direction);
133
void createBidiRunsForLine(const Iterator& start, const Iterator& end, bool visualOrder = false, bool hardLineBreak = false);
135
Run* firstRun() const { return m_firstRun; }
136
Run* lastRun() const { return m_lastRun; }
137
int runCount() const { return m_runCount; }
144
void reverseRuns(int start, int end);
151
WTF::Unicode::Direction m_direction;
152
bool m_adjustEmbedding;
154
bool reachedEndOfLine;
155
Iterator lastBeforeET;
163
template <class Iterator, class Run>
164
void BidiResolver<Iterator, Run>::appendRun()
166
if (emptyRun || eor.atEnd())
169
Run* bidiRun = new Run(sor.offset(), eor.offset() + 1, context(), m_direction);
171
m_firstRun = bidiRun;
173
m_lastRun->m_next = bidiRun;
177
eor.increment(*this);
179
m_direction = WTF::Unicode::OtherNeutral;
180
m_status.eor = WTF::Unicode::OtherNeutral;
183
template <class Iterator, class Run>
184
void BidiResolver<Iterator, Run>::embed(WTF::Unicode::Direction d)
186
using namespace WTF::Unicode;
188
bool b = m_adjustEmbedding;
189
m_adjustEmbedding = false;
190
if (d == PopDirectionalFormat) {
191
BidiContext* c = context()->parent();
193
if (!emptyRun && eor != last) {
194
ASSERT(m_status.eor != OtherNeutral || eor.atEnd());
195
// bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
196
ASSERT(m_status.last == EuropeanNumberSeparator
197
|| m_status.last == EuropeanNumberTerminator
198
|| m_status.last == CommonNumberSeparator
199
|| m_status.last == BoundaryNeutral
200
|| m_status.last == BlockSeparator
201
|| m_status.last == SegmentSeparator
202
|| m_status.last == WhiteSpaceNeutral
203
|| m_status.last == OtherNeutral);
204
if (m_direction == OtherNeutral)
205
m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft;
206
if (context()->dir() == LeftToRight) {
207
// bidi.sor ... bidi.eor ... bidi.last L
208
if (m_status.eor == EuropeanNumber) {
209
if (m_status.lastStrong != LeftToRight) {
210
m_direction = EuropeanNumber;
213
} else if (m_status.eor == ArabicNumber) {
214
m_direction = ArabicNumber;
216
} else if (m_status.lastStrong != LeftToRight) {
217
if (context()->dir() == RightToLeft)
218
m_direction = RightToLeft;
221
m_direction = LeftToRight;
224
} else if (m_status.eor == EuropeanNumber || m_status.eor == ArabicNumber || m_status.lastStrong == LeftToRight) {
226
m_direction = RightToLeft;
232
// sor for the new run is determined by the higher level (rule X10)
233
setLastDir(context()->dir());
234
setLastStrongDir(context()->dir());
240
if (d == RightToLeftEmbedding || d == RightToLeftOverride)
241
runDir = RightToLeft;
243
runDir = LeftToRight;
244
bool override = d == LeftToRightOverride || d == RightToLeftOverride;
246
unsigned char level = context()->level();
247
if (runDir == RightToLeft) {
248
if (level % 2) // we have an odd level
253
if (level % 2) // we have an odd level
260
if (!emptyRun && eor != last) {
261
ASSERT(m_status.eor != OtherNeutral || eor.atEnd());
262
// bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
263
ASSERT(m_status.last == EuropeanNumberSeparator
264
|| m_status.last == EuropeanNumberTerminator
265
|| m_status.last == CommonNumberSeparator
266
|| m_status.last == BoundaryNeutral
267
|| m_status.last == BlockSeparator
268
|| m_status.last == SegmentSeparator
269
|| m_status.last == WhiteSpaceNeutral
270
|| m_status.last == OtherNeutral);
271
if (m_direction == OtherNeutral)
272
m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft;
273
if (runDir == LeftToRight) {
274
// bidi.sor ... bidi.eor ... bidi.last L
275
if (m_status.eor == EuropeanNumber) {
276
if (m_status.lastStrong != LeftToRight) {
277
m_direction = EuropeanNumber;
280
} else if (m_status.eor == ArabicNumber) {
281
m_direction = ArabicNumber;
283
} else if (m_status.lastStrong != LeftToRight && context()->dir() == LeftToRight) {
285
m_direction = LeftToRight;
287
} else if (m_status.eor == ArabicNumber
288
|| m_status.eor == EuropeanNumber && (m_status.lastStrong != LeftToRight || context()->dir() == RightToLeft)
289
|| m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && context()->dir() == RightToLeft) {
291
m_direction = RightToLeft;
297
setContext(new BidiContext(level, runDir, override, context()));
299
setLastStrongDir(runDir);
303
m_adjustEmbedding = b;
306
template <class Iterator, class Run>
307
void BidiResolver<Iterator, Run>::deleteRuns()
313
Run* curr = m_firstRun;
315
Run* s = curr->m_next;
325
template <class Iterator, class Run>
326
void BidiResolver<Iterator, Run>::reverseRuns(int start, int end)
331
ASSERT(start >= 0 && end < m_runCount);
333
// Get the item before the start of the runs to reverse and put it in
334
// |beforeStart|. |curr| should point to the first run to reverse.
335
Run* curr = m_firstRun;
336
Run* beforeStart = 0;
344
Run* startRun = curr;
350
Run* afterEnd = curr->next();
354
Run* newNext = afterEnd;
357
Run* next = curr->next();
358
curr->m_next = newNext;
364
// Now hook up beforeStart and afterEnd to the startRun and endRun.
366
beforeStart->m_next = endRun;
370
startRun->m_next = afterEnd;
372
m_lastRun = startRun;
375
template <class Iterator, class Run>
376
void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& start, const Iterator& end, bool visualOrder, bool hardLineBreak)
378
using namespace WTF::Unicode;
380
ASSERT(m_direction == OtherNeutral);
388
bool pastEnd = false;
389
BidiResolver<Iterator, Run> stateAtEnd;
392
Direction dirCurrent;
393
if (pastEnd && (hardLineBreak || current.atEnd())) {
394
BidiContext* c = context();
397
dirCurrent = c->dir();
399
// A deviation from the Unicode Bidi Algorithm in order to match
400
// Mac OS X text and WinIE: a hard line break resets bidi state.
401
stateAtEnd.setContext(c);
402
stateAtEnd.setEorDir(dirCurrent);
403
stateAtEnd.setLastDir(dirCurrent);
404
stateAtEnd.setLastStrongDir(dirCurrent);
407
dirCurrent = current.direction();
408
if (context()->override()
409
&& dirCurrent != RightToLeftEmbedding
410
&& dirCurrent != LeftToRightEmbedding
411
&& dirCurrent != RightToLeftOverride
412
&& dirCurrent != LeftToRightOverride
413
&& dirCurrent != PopDirectionalFormat)
414
dirCurrent = context()->dir();
415
else if (dirCurrent == NonSpacingMark)
416
dirCurrent = m_status.last;
419
ASSERT(m_status.eor != OtherNeutral || eor.atEnd());
420
switch (dirCurrent) {
422
// embedding and overrides (X1-X9 in the Bidi specs)
423
case RightToLeftEmbedding:
424
case LeftToRightEmbedding:
425
case RightToLeftOverride:
426
case LeftToRightOverride:
427
case PopDirectionalFormat:
433
switch(m_status.last) {
435
case RightToLeftArabic:
438
if (m_status.last != EuropeanNumber || m_status.lastStrong != LeftToRight)
443
case EuropeanNumberSeparator:
444
case EuropeanNumberTerminator:
445
case CommonNumberSeparator:
446
case BoundaryNeutral:
448
case SegmentSeparator:
449
case WhiteSpaceNeutral:
451
if (m_status.eor == EuropeanNumber) {
452
if (m_status.lastStrong != LeftToRight) {
453
// the numbers need to be on a higher embedding level, so let's close that run
454
m_direction = EuropeanNumber;
456
if (context()->dir() != LeftToRight) {
457
// the neutrals take the embedding direction, which is R
459
m_direction = RightToLeft;
463
} else if (m_status.eor == ArabicNumber) {
464
// Arabic numbers are always on a higher embedding level, so let's close that run
465
m_direction = ArabicNumber;
467
if (context()->dir() != LeftToRight) {
468
// the neutrals take the embedding direction, which is R
470
m_direction = RightToLeft;
473
} else if (m_status.lastStrong != LeftToRight) {
474
//last stuff takes embedding dir
475
if (context()->dir() == RightToLeft) {
477
m_direction = RightToLeft;
485
m_status.eor = LeftToRight;
486
m_status.lastStrong = LeftToRight;
487
m_direction = LeftToRight;
489
case RightToLeftArabic:
491
switch (m_status.last) {
497
case RightToLeftArabic:
499
case EuropeanNumberSeparator:
500
case EuropeanNumberTerminator:
501
case CommonNumberSeparator:
502
case BoundaryNeutral:
504
case SegmentSeparator:
505
case WhiteSpaceNeutral:
507
if (m_status.eor == EuropeanNumber) {
508
if (m_status.lastStrong == LeftToRight && context()->dir() == LeftToRight)
511
} else if (m_status.eor == ArabicNumber)
513
else if (m_status.lastStrong == LeftToRight) {
514
if (context()->dir() == LeftToRight)
522
m_status.eor = RightToLeft;
523
m_status.lastStrong = dirCurrent;
524
m_direction = RightToLeft;
530
if (m_status.lastStrong != RightToLeftArabic) {
531
// if last strong was AL change EN to AN
532
switch (m_status.last) {
537
case RightToLeftArabic:
541
m_direction = EuropeanNumber;
543
case EuropeanNumberSeparator:
544
case CommonNumberSeparator:
545
if (m_status.eor == EuropeanNumber)
547
case EuropeanNumberTerminator:
548
case BoundaryNeutral:
550
case SegmentSeparator:
551
case WhiteSpaceNeutral:
553
if (m_status.eor == EuropeanNumber) {
554
if (m_status.lastStrong == RightToLeft) {
555
// ENs on both sides behave like Rs, so the neutrals should be R.
556
// Terminate the EN run.
559
eor = m_status.last == EuropeanNumberTerminator ? lastBeforeET : last;
560
m_direction = RightToLeft;
562
// Begin a new EN run.
563
m_direction = EuropeanNumber;
565
} else if (m_status.eor == ArabicNumber) {
566
// Terminate the AN run.
568
if (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft) {
570
eor = m_status.last == EuropeanNumberTerminator ? lastBeforeET : last;
571
m_direction = RightToLeft;
573
// Begin a new EN run.
574
m_direction = EuropeanNumber;
576
} else if (m_status.lastStrong == RightToLeft) {
577
// Extend the R run to include the neutrals.
578
eor = m_status.last == EuropeanNumberTerminator ? lastBeforeET : last;
579
m_direction = RightToLeft;
581
// Begin a new EN run.
582
m_direction = EuropeanNumber;
588
m_status.eor = EuropeanNumber;
589
if (m_direction == OtherNeutral)
590
m_direction = LeftToRight;
594
dirCurrent = ArabicNumber;
595
switch (m_status.last) {
597
if (context()->dir() == LeftToRight)
603
case RightToLeftArabic:
608
case CommonNumberSeparator:
609
if (m_status.eor == ArabicNumber)
611
case EuropeanNumberSeparator:
612
case EuropeanNumberTerminator:
613
case BoundaryNeutral:
615
case SegmentSeparator:
616
case WhiteSpaceNeutral:
618
if (m_status.eor == ArabicNumber
619
|| m_status.eor == EuropeanNumber && (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft)
620
|| m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && context()->dir() == RightToLeft) {
621
// Terminate the run before the neutrals.
623
// Begin an R run for the neutrals.
624
m_direction = RightToLeft;
625
} else if (m_direction == OtherNeutral)
626
m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft;
633
m_status.eor = ArabicNumber;
634
if (m_direction == OtherNeutral)
635
m_direction = ArabicNumber;
637
case EuropeanNumberSeparator:
638
case CommonNumberSeparator:
640
case EuropeanNumberTerminator:
641
if (m_status.last == EuropeanNumber) {
642
dirCurrent = EuropeanNumber;
644
m_status.eor = dirCurrent;
645
} else if (m_status.last != EuropeanNumberTerminator)
646
lastBeforeET = emptyRun ? eor : last;
649
// boundary neutrals should be ignored
650
case BoundaryNeutral:
656
// ### what do we do with newline and paragraph seperators that come to here?
658
case SegmentSeparator:
659
// ### implement rule L1
661
case WhiteSpaceNeutral:
670
if (eor == current) {
671
if (!reachedEndOfLine) {
673
switch (m_status.eor) {
677
m_direction = m_status.eor;
680
m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : EuropeanNumber;
687
m_status = stateAtEnd.m_status;
688
current = stateAtEnd.current;
689
sor = stateAtEnd.sor;
690
eor = stateAtEnd.eor;
691
last = stateAtEnd.last;
692
m_adjustEmbedding = stateAtEnd.m_adjustEmbedding;
693
reachedEndOfLine = stateAtEnd.reachedEndOfLine;
694
lastBeforeET = stateAtEnd.lastBeforeET;
695
emptyRun = stateAtEnd.emptyRun;
696
m_direction = OtherNeutral;
701
// set m_status.last as needed.
702
switch (dirCurrent) {
703
case EuropeanNumberTerminator:
704
if (m_status.last != EuropeanNumber)
705
m_status.last = EuropeanNumberTerminator;
707
case EuropeanNumberSeparator:
708
case CommonNumberSeparator:
709
case SegmentSeparator:
710
case WhiteSpaceNeutral:
712
switch(m_status.last) {
715
case RightToLeftArabic:
718
m_status.last = dirCurrent;
721
m_status.last = OtherNeutral;
725
case BoundaryNeutral:
726
case RightToLeftEmbedding:
727
case LeftToRightEmbedding:
728
case RightToLeftOverride:
729
case LeftToRightOverride:
730
case PopDirectionalFormat:
736
m_status.last = dirCurrent;
741
if (emptyRun && !(dirCurrent == RightToLeftEmbedding
742
|| dirCurrent == LeftToRightEmbedding
743
|| dirCurrent == RightToLeftOverride
744
|| dirCurrent == LeftToRightOverride
745
|| dirCurrent == PopDirectionalFormat)) {
750
// this causes the operator ++ to open and close embedding levels as needed
751
// for the CSS unicode-bidi property
752
m_adjustEmbedding = true;
753
current.increment(*this);
754
m_adjustEmbedding = false;
755
if (emptyRun && (dirCurrent == RightToLeftEmbedding
756
|| dirCurrent == LeftToRightEmbedding
757
|| dirCurrent == RightToLeftOverride
758
|| dirCurrent == LeftToRightOverride
759
|| dirCurrent == PopDirectionalFormat)) {
760
// exclude the embedding char itself from the new run so that ATSUI will never see it
766
if (!pastEnd && (current == end || current.atEnd())) {
775
// reorder line according to run structure...
776
// do not reverse for visually ordered web sites
779
// first find highest and lowest levels
780
unsigned char levelLow = 128;
781
unsigned char levelHigh = 0;
784
if (r->m_level > levelHigh)
785
levelHigh = r->m_level;
786
if (r->m_level < levelLow)
787
levelLow = r->m_level;
791
// implements reordering of the line (L2 according to Bidi spec):
792
// L2. From the highest level found in the text to the lowest odd level on each line,
793
// reverse any contiguous sequence of characters that are at that level or higher.
795
// reversing is only done up to the lowest odd level
799
int count = runCount() - 1;
801
while (levelHigh >= levelLow) {
803
Run* currRun = firstRun();
805
while (i < count && currRun && currRun->m_level < levelHigh) {
807
currRun = currRun->next();
810
while (i <= count && currRun && currRun->m_level >= levelHigh) {
812
currRun = currRun->next();
815
reverseRuns(start, end);
820
endOfLine = Iterator();
823
} // namespace WebCore
825
#endif // BidiResolver_h