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