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