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 "platform/text/BidiCharacterRun.h"
26 #include "platform/text/BidiContext.h"
27 #include "platform/text/BidiRunList.h"
28 #include "platform/text/TextDirection.h"
29 #include "wtf/HashMap.h"
30 #include "wtf/Noncopyable.h"
31 #include "wtf/PassRefPtr.h"
32 #include "wtf/Vector.h"
38 template <class Iterator> class MidpointState {
48 m_currentMidpoint = 0;
49 m_betweenMidpoints = false;
52 void startIgnoringSpaces(const Iterator& midpoint)
54 ASSERT(!(m_numMidpoints % 2));
55 addMidpoint(midpoint);
58 void stopIgnoringSpaces(const Iterator& midpoint)
60 ASSERT(m_numMidpoints % 2);
61 addMidpoint(midpoint);
64 // When ignoring spaces, this needs to be called for objects that need line boxes such as RenderInlines or
65 // hard line breaks to ensure that they're not ignored.
66 void ensureLineBoxInsideIgnoredSpaces(RenderObject* renderer)
68 Iterator midpoint(0, renderer, 0);
69 stopIgnoringSpaces(midpoint);
70 startIgnoringSpaces(midpoint);
73 // Adding a pair of midpoints before a character will split it out into a new line box.
74 void ensureCharacterGetsLineBox(Iterator& textParagraphSeparator)
76 Iterator midpoint(0, textParagraphSeparator.object(), textParagraphSeparator.offset());
77 startIgnoringSpaces(Iterator(0, textParagraphSeparator.object(), textParagraphSeparator.offset() - 1));
78 stopIgnoringSpaces(Iterator(0, textParagraphSeparator.object(), textParagraphSeparator.offset()));
81 void checkMidpoints(Iterator& lBreak)
83 // Check to see if our last midpoint is a start point beyond the line break. If so,
84 // shave it off the list, and shave off a trailing space if the previous end point doesn't
85 // preserve whitespace.
86 if (lBreak.object() && m_numMidpoints && !(m_numMidpoints % 2)) {
87 Iterator* midpointsIterator = m_midpoints.data();
88 Iterator& endpoint = midpointsIterator[m_numMidpoints - 2];
89 const Iterator& startpoint = midpointsIterator[m_numMidpoints - 1];
90 Iterator currpoint = endpoint;
91 while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak)
92 currpoint.increment();
93 if (currpoint == lBreak) {
94 // We hit the line break before the start point. Shave off the start point.
96 if (endpoint.object()->style()->collapseWhiteSpace() && endpoint.object()->isText())
97 endpoint.setOffset(endpoint.offset() - 1);
102 Vector<Iterator>& midpoints() { return m_midpoints; }
103 const unsigned& numMidpoints() const { return m_numMidpoints; }
104 const unsigned& currentMidpoint() const { return m_currentMidpoint; }
105 void incrementCurrentMidpoint() { m_currentMidpoint++; }
106 const bool& betweenMidpoints() const { return m_betweenMidpoints; }
107 void setBetweenMidpoints(bool betweenMidpoint) { m_betweenMidpoints = betweenMidpoint; }
109 // The goal is to reuse the line state across multiple
110 // lines so we just keep an array around for midpoints and never clear it across multiple
111 // lines. We track the number of items and position using the two other variables.
112 Vector<Iterator> m_midpoints;
113 unsigned m_numMidpoints;
114 unsigned m_currentMidpoint;
115 bool m_betweenMidpoints;
117 void addMidpoint(const Iterator& midpoint)
119 if (m_midpoints.size() <= m_numMidpoints)
120 m_midpoints.grow(m_numMidpoints + 10);
122 Iterator* midpointsIterator = m_midpoints.data();
123 midpointsIterator[m_numMidpoints++] = midpoint;
127 // The BidiStatus at a given position (typically the end of a line) can
128 // be cached and then used to restart bidi resolution at that position.
131 : eor(WTF::Unicode::OtherNeutral)
132 , lastStrong(WTF::Unicode::OtherNeutral)
133 , last(WTF::Unicode::OtherNeutral)
137 // Creates a BidiStatus representing a new paragraph root with a default direction.
138 // Uses TextDirection as it only has two possibilities instead of WTF::Unicode::Direction which has 19.
139 BidiStatus(TextDirection textDirection, bool isOverride)
141 WTF::Unicode::Direction direction = textDirection == LTR ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
142 eor = lastStrong = last = direction;
143 context = BidiContext::create(textDirection == LTR ? 0 : 1, direction, isOverride);
146 BidiStatus(WTF::Unicode::Direction eorDir, WTF::Unicode::Direction lastStrongDir, WTF::Unicode::Direction lastDir, PassRefPtr<BidiContext> bidiContext)
148 , lastStrong(lastStrongDir)
150 , context(bidiContext)
154 WTF::Unicode::Direction eor;
155 WTF::Unicode::Direction lastStrong;
156 WTF::Unicode::Direction last;
157 RefPtr<BidiContext> context;
160 class BidiEmbedding {
162 BidiEmbedding(WTF::Unicode::Direction direction, BidiEmbeddingSource source)
163 : m_direction(direction)
168 WTF::Unicode::Direction direction() const { return m_direction; }
169 BidiEmbeddingSource source() const { return m_source; }
171 WTF::Unicode::Direction m_direction;
172 BidiEmbeddingSource m_source;
175 inline bool operator==(const BidiStatus& status1, const BidiStatus& status2)
177 return status1.eor == status2.eor && status1.last == status2.last && status1.lastStrong == status2.lastStrong && *(status1.context) == *(status2.context);
180 inline bool operator!=(const BidiStatus& status1, const BidiStatus& status2)
182 return !(status1 == status2);
185 enum VisualDirectionOverride {
187 VisualLeftToRightOverride,
188 VisualRightToLeftOverride
191 // BidiResolver is WebKit's implementation of the Unicode Bidi Algorithm
192 // http://unicode.org/reports/tr9
193 template <class Iterator, class Run> class BidiResolver {
194 WTF_MAKE_NONCOPYABLE(BidiResolver);
197 : m_direction(WTF::Unicode::OtherNeutral)
198 , m_reachedEndOfLine(false)
200 , m_nestedIsolateCount(0)
201 , m_trailingSpaceRun(0)
209 const Iterator& position() const { return m_current; }
210 Iterator& position() { return m_current; }
211 void setPositionIgnoringNestedIsolates(const Iterator& position) { m_current = position; }
212 void setPosition(const Iterator& position, unsigned nestedIsolatedCount)
214 m_current = position;
215 m_nestedIsolateCount = nestedIsolatedCount;
218 BidiContext* context() const { return m_status.context.get(); }
219 void setContext(PassRefPtr<BidiContext> c) { m_status.context = c; }
221 void setLastDir(WTF::Unicode::Direction lastDir) { m_status.last = lastDir; }
222 void setLastStrongDir(WTF::Unicode::Direction lastStrongDir) { m_status.lastStrong = lastStrongDir; }
223 void setEorDir(WTF::Unicode::Direction eorDir) { m_status.eor = eorDir; }
225 WTF::Unicode::Direction dir() const { return m_direction; }
226 void setDir(WTF::Unicode::Direction d) { m_direction = d; }
228 const BidiStatus& status() const { return m_status; }
229 void setStatus(const BidiStatus s)
233 m_paragraphDirectionality = s.context->dir() == WTF::Unicode::LeftToRight ? LTR : RTL;
236 MidpointState<Iterator>& midpointState() { return m_midpointState; }
238 // The current algorithm handles nested isolates one layer of nesting at a time.
239 // But when we layout each isolated span, we will walk into (and ignore) all
240 // child isolated spans.
241 void enterIsolate() { m_nestedIsolateCount++; }
242 void exitIsolate() { ASSERT(m_nestedIsolateCount >= 1); m_nestedIsolateCount--; }
243 bool inIsolate() const { return m_nestedIsolateCount; }
245 void embed(WTF::Unicode::Direction, BidiEmbeddingSource);
246 bool commitExplicitEmbedding();
248 void createBidiRunsForLine(const Iterator& end, VisualDirectionOverride = NoVisualOverride, bool hardLineBreak = false, bool reorderRuns = true);
250 BidiRunList<Run>& runs() { return m_runs; }
252 // FIXME: This used to be part of deleteRuns() but was a layering violation.
253 // It's unclear if this is still needed.
254 void markCurrentRunEmpty() { m_emptyRun = true; }
256 Vector<Run*>& isolatedRuns() { return m_isolatedRuns; }
258 bool isEndOfLine(const Iterator& end) { return m_current == end || m_current.atEnd(); }
260 TextDirection determineParagraphDirectionality(bool* hasStrongDirectionality = 0);
262 void setMidpointStateForIsolatedRun(Run*, const MidpointState<Iterator>&);
263 MidpointState<Iterator> midpointStateForIsolatedRun(Run*);
265 Iterator endOfLine() const { return m_endOfLine; }
267 Run* trailingSpaceRun() const { return m_trailingSpaceRun; }
270 void increment() { m_current.increment(); }
271 // FIXME: Instead of InlineBidiResolvers subclassing this method, we should
272 // pass in some sort of Traits object which knows how to create runs for appending.
275 Run* addTrailingRun(int, int, Run*, BidiContext*, TextDirection) { return 0; }
277 // sor and eor are "start of run" and "end of run" respectively and correpond
278 // to abreviations used in UBA spec: http://unicode.org/reports/tr9/#BD7
279 Iterator m_sor; // Points to the first character in the current run.
280 Iterator m_eor; // Points to the last character in the current run.
283 WTF::Unicode::Direction m_direction;
284 // m_endOfRunAtEndOfLine is "the position last eor in the end of line"
285 Iterator m_endOfRunAtEndOfLine;
286 Iterator m_endOfLine;
287 bool m_reachedEndOfLine;
288 Iterator m_lastBeforeET; // Before a EuropeanNumberTerminator
291 // FIXME: This should not belong to the resolver, but rather be passed
292 // into createBidiRunsForLine by the caller.
293 BidiRunList<Run> m_runs;
295 MidpointState<Iterator> m_midpointState;
297 unsigned m_nestedIsolateCount;
298 Vector<Run*> m_isolatedRuns;
299 Run* m_trailingSpaceRun;
300 TextDirection m_paragraphDirectionality;
303 void raiseExplicitEmbeddingLevel(WTF::Unicode::Direction from, WTF::Unicode::Direction to);
304 void lowerExplicitEmbeddingLevel(WTF::Unicode::Direction from);
305 void checkDirectionInLowerRaiseEmbeddingLevel();
307 void updateStatusLastFromCurrentDirection(WTF::Unicode::Direction);
308 void reorderRunsFromLevels();
310 bool needsToApplyL1Rule() { return false; }
311 int findFirstTrailingSpaceAtRun(Run*) { return 0; }
312 // http://www.unicode.org/reports/tr9/#L1
315 Vector<BidiEmbedding, 8> m_currentExplicitEmbeddingSequence;
316 HashMap<Run *, MidpointState<Iterator> > m_midpointStateForIsolatedRun;
320 template <class Iterator, class Run>
321 BidiResolver<Iterator, Run>::~BidiResolver()
323 // The owner of this resolver should have handled the isolated runs.
324 ASSERT(m_isolatedRuns.isEmpty());
328 template <class Iterator, class Run>
329 void BidiResolver<Iterator, Run>::appendRun()
331 if (!m_emptyRun && !m_eor.atEnd()) {
332 unsigned startOffset = m_sor.offset();
333 unsigned endOffset = m_eor.offset();
335 if (!m_endOfRunAtEndOfLine.atEnd() && endOffset >= m_endOfRunAtEndOfLine.offset()) {
336 m_reachedEndOfLine = true;
337 endOffset = m_endOfRunAtEndOfLine.offset();
340 if (endOffset >= startOffset)
341 m_runs.addRun(new Run(startOffset, endOffset + 1, context(), m_direction));
347 m_direction = WTF::Unicode::OtherNeutral;
348 m_status.eor = WTF::Unicode::OtherNeutral;
351 template <class Iterator, class Run>
352 void BidiResolver<Iterator, Run>::embed(WTF::Unicode::Direction dir, BidiEmbeddingSource source)
354 // Isolated spans compute base directionality during their own UBA run.
355 // Do not insert fake embed characters once we enter an isolated span.
356 ASSERT(!inIsolate());
357 using namespace WTF::Unicode;
359 ASSERT(dir == PopDirectionalFormat || dir == LeftToRightEmbedding || dir == LeftToRightOverride || dir == RightToLeftEmbedding || dir == RightToLeftOverride);
360 m_currentExplicitEmbeddingSequence.append(BidiEmbedding(dir, source));
363 template <class Iterator, class Run>
364 void BidiResolver<Iterator, Run>::checkDirectionInLowerRaiseEmbeddingLevel()
366 using namespace WTF::Unicode;
368 ASSERT(m_status.eor != OtherNeutral || m_eor.atEnd());
369 ASSERT(m_status.last != NonSpacingMark
370 && m_status.last != BoundaryNeutral
371 && m_status.last != RightToLeftEmbedding
372 && m_status.last != LeftToRightEmbedding
373 && m_status.last != RightToLeftOverride
374 && m_status.last != LeftToRightOverride
375 && m_status.last != PopDirectionalFormat);
376 if (m_direction == OtherNeutral)
377 m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft;
380 template <class Iterator, class Run>
381 void BidiResolver<Iterator, Run>::lowerExplicitEmbeddingLevel(WTF::Unicode::Direction from)
383 using namespace WTF::Unicode;
385 if (!m_emptyRun && m_eor != m_last) {
386 checkDirectionInLowerRaiseEmbeddingLevel();
387 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
388 if (from == LeftToRight) {
389 // bidi.sor ... bidi.eor ... bidi.last L
390 if (m_status.eor == EuropeanNumber) {
391 if (m_status.lastStrong != LeftToRight) {
392 m_direction = EuropeanNumber;
395 } else if (m_status.eor == ArabicNumber) {
396 m_direction = ArabicNumber;
398 } else if (m_status.lastStrong != LeftToRight) {
400 m_direction = LeftToRight;
402 } else if (m_status.eor == EuropeanNumber || m_status.eor == ArabicNumber || m_status.lastStrong == LeftToRight) {
404 m_direction = RightToLeft;
412 // sor for the new run is determined by the higher level (rule X10)
414 setLastStrongDir(from);
418 template <class Iterator, class Run>
419 void BidiResolver<Iterator, Run>::raiseExplicitEmbeddingLevel(WTF::Unicode::Direction from, WTF::Unicode::Direction to)
421 using namespace WTF::Unicode;
423 if (!m_emptyRun && m_eor != m_last) {
424 checkDirectionInLowerRaiseEmbeddingLevel();
425 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
426 if (to == LeftToRight) {
427 // bidi.sor ... bidi.eor ... bidi.last L
428 if (m_status.eor == EuropeanNumber) {
429 if (m_status.lastStrong != LeftToRight) {
430 m_direction = EuropeanNumber;
433 } else if (m_status.eor == ArabicNumber) {
434 m_direction = ArabicNumber;
436 } else if (m_status.lastStrong != LeftToRight && from == LeftToRight) {
438 m_direction = LeftToRight;
440 } else if (m_status.eor == ArabicNumber
441 || (m_status.eor == EuropeanNumber && (m_status.lastStrong != LeftToRight || from == RightToLeft))
442 || (m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && from == RightToLeft)) {
444 m_direction = RightToLeft;
453 setLastStrongDir(to);
457 template <class Iterator, class Run>
458 void BidiResolver<Iterator, Run>::applyL1Rule()
460 ASSERT(m_runs.runCount());
461 if (!needsToApplyL1Rule())
464 Run* trailingSpaceRun = m_runs.logicallyLastRun();
466 int firstSpace = findFirstTrailingSpaceAtRun(trailingSpaceRun);
467 if (firstSpace == trailingSpaceRun->stop())
470 bool shouldReorder = trailingSpaceRun != (m_paragraphDirectionality == LTR ? m_runs.lastRun() : m_runs.firstRun());
471 if (firstSpace != trailingSpaceRun->start()) {
472 BidiContext* baseContext = context();
473 while (BidiContext* parent = baseContext->parent())
474 baseContext = parent;
476 m_trailingSpaceRun = addTrailingRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun, baseContext, m_paragraphDirectionality);
477 ASSERT(m_trailingSpaceRun);
478 trailingSpaceRun->m_stop = firstSpace;
481 if (!shouldReorder) {
482 m_trailingSpaceRun = trailingSpaceRun;
486 if (m_paragraphDirectionality == LTR) {
487 m_runs.moveRunToEnd(trailingSpaceRun);
488 trailingSpaceRun->m_level = 0;
490 m_runs.moveRunToBeginning(trailingSpaceRun);
491 trailingSpaceRun->m_level = 1;
493 m_trailingSpaceRun = trailingSpaceRun;
496 template <class Iterator, class Run>
497 bool BidiResolver<Iterator, Run>::commitExplicitEmbedding()
499 // When we're "inIsolate()" we're resolving the parent context which
500 // ignores (skips over) the isolated content, including embedding levels.
501 // We should never accrue embedding levels while skipping over isolated content.
502 ASSERT(!inIsolate() || m_currentExplicitEmbeddingSequence.isEmpty());
504 using namespace WTF::Unicode;
506 unsigned char fromLevel = context()->level();
507 RefPtr<BidiContext> toContext = context();
509 for (size_t i = 0; i < m_currentExplicitEmbeddingSequence.size(); ++i) {
510 BidiEmbedding embedding = m_currentExplicitEmbeddingSequence[i];
511 if (embedding.direction() == PopDirectionalFormat) {
512 if (BidiContext* parentContext = toContext->parent())
513 toContext = parentContext;
515 Direction direction = (embedding.direction() == RightToLeftEmbedding || embedding.direction() == RightToLeftOverride) ? RightToLeft : LeftToRight;
516 bool override = embedding.direction() == LeftToRightOverride || embedding.direction() == RightToLeftOverride;
517 unsigned char level = toContext->level();
518 if (direction == RightToLeft)
519 level = nextGreaterOddLevel(level);
521 level = nextGreaterEvenLevel(level);
522 if (level < BidiContext::kMaxLevel)
523 toContext = BidiContext::create(level, direction, override, embedding.source(), toContext.get());
527 unsigned char toLevel = toContext->level();
529 if (toLevel > fromLevel)
530 raiseExplicitEmbeddingLevel(fromLevel % 2 ? RightToLeft : LeftToRight, toLevel % 2 ? RightToLeft : LeftToRight);
531 else if (toLevel < fromLevel)
532 lowerExplicitEmbeddingLevel(fromLevel % 2 ? RightToLeft : LeftToRight);
534 setContext(toContext);
536 m_currentExplicitEmbeddingSequence.clear();
538 return fromLevel != toLevel;
541 template <class Iterator, class Run>
542 inline void BidiResolver<Iterator, Run>::updateStatusLastFromCurrentDirection(WTF::Unicode::Direction dirCurrent)
544 using namespace WTF::Unicode;
545 switch (dirCurrent) {
546 case EuropeanNumberTerminator:
547 if (m_status.last != EuropeanNumber)
548 m_status.last = EuropeanNumberTerminator;
550 case EuropeanNumberSeparator:
551 case CommonNumberSeparator:
552 case SegmentSeparator:
553 case WhiteSpaceNeutral:
555 switch (m_status.last) {
558 case RightToLeftArabic:
561 m_status.last = dirCurrent;
564 m_status.last = OtherNeutral;
568 case BoundaryNeutral:
569 case RightToLeftEmbedding:
570 case LeftToRightEmbedding:
571 case RightToLeftOverride:
572 case LeftToRightOverride:
573 case PopDirectionalFormat:
579 m_status.last = dirCurrent;
583 template <class Iterator, class Run>
584 inline void BidiResolver<Iterator, Run>::reorderRunsFromLevels()
586 unsigned char levelLow = BidiContext::kMaxLevel;
587 unsigned char levelHigh = 0;
588 for (Run* run = m_runs.firstRun(); run; run = run->next()) {
589 levelHigh = std::max(run->level(), levelHigh);
590 levelLow = std::min(run->level(), levelLow);
593 // This implements reordering of the line (L2 according to Bidi spec):
594 // http://unicode.org/reports/tr9/#L2
595 // L2. From the highest level found in the text to the lowest odd level on each line,
596 // reverse any contiguous sequence of characters that are at that level or higher.
598 // Reversing is only done up to the lowest odd level.
602 unsigned count = m_runs.runCount() - 1;
604 while (levelHigh >= levelLow) {
606 Run* run = m_runs.firstRun();
608 for (;i < count && run && run->level() < levelHigh; i++)
611 for (;i <= count && run && run->level() >= levelHigh; i++)
613 unsigned end = i - 1;
614 m_runs.reverseRuns(start, end);
620 template <class Iterator, class Run>
621 TextDirection BidiResolver<Iterator, Run>::determineParagraphDirectionality(bool* hasStrongDirectionality)
623 while (!m_current.atEnd()) {
628 if (m_current.atParagraphSeparator())
630 UChar32 current = m_current.current();
631 if (UNLIKELY(U16_IS_SURROGATE(current))) {
633 // If this not the high part of the surrogate pair, then drop it and move to the next.
634 if (!U16_IS_SURROGATE_LEAD(current))
636 UChar high = static_cast<UChar>(current);
637 if (m_current.atEnd())
639 UChar low = m_current.current();
640 // Verify the low part. If invalid, then assume an invalid surrogate pair and retry.
641 if (!U16_IS_TRAIL(low))
643 current = U16_GET_SUPPLEMENTARY(high, low);
645 WTF::Unicode::Direction charDirection = WTF::Unicode::direction(current);
646 if (charDirection == WTF::Unicode::LeftToRight) {
647 if (hasStrongDirectionality)
648 *hasStrongDirectionality = true;
651 if (charDirection == WTF::Unicode::RightToLeft || charDirection == WTF::Unicode::RightToLeftArabic) {
652 if (hasStrongDirectionality)
653 *hasStrongDirectionality = true;
658 if (hasStrongDirectionality)
659 *hasStrongDirectionality = false;
663 template <class Iterator, class Run>
664 void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& end, VisualDirectionOverride override, bool hardLineBreak, bool reorderRuns)
666 using namespace WTF::Unicode;
668 ASSERT(m_direction == OtherNeutral);
669 m_trailingSpaceRun = 0;
673 if (override != NoVisualOverride) {
677 while (m_current != end && !m_current.atEnd()) {
681 m_direction = override == VisualLeftToRightOverride ? LeftToRight : RightToLeft;
683 m_runs.setLogicallyLastRun(m_runs.lastRun());
684 if (override == VisualRightToLeftOverride && m_runs.runCount())
685 m_runs.reverseRuns(0, m_runs.runCount() - 1);
694 bool lastLineEnded = false;
695 BidiResolver<Iterator, Run> stateAtEnd;
698 if (inIsolate() && m_emptyRun) {
703 if (!lastLineEnded && isEndOfLine(end)) {
707 stateAtEnd.m_status = m_status;
708 stateAtEnd.m_sor = m_sor;
709 stateAtEnd.m_eor = m_eor;
710 stateAtEnd.m_last = m_last;
711 stateAtEnd.m_reachedEndOfLine = m_reachedEndOfLine;
712 stateAtEnd.m_lastBeforeET = m_lastBeforeET;
713 stateAtEnd.m_emptyRun = m_emptyRun;
714 m_endOfRunAtEndOfLine = m_last;
715 lastLineEnded = true;
717 Direction dirCurrent;
718 if (lastLineEnded && (hardLineBreak || m_current.atEnd())) {
719 BidiContext* c = context();
721 // A deviation from the Unicode Bidi Algorithm in order to match
722 // WinIE and user expectations: hard line breaks reset bidi state
723 // coming from unicode bidi control characters, but not those from
724 // DOM nodes with specified directionality
725 stateAtEnd.setContext(c->copyStackRemovingUnicodeEmbeddingContexts());
727 dirCurrent = stateAtEnd.context()->dir();
728 stateAtEnd.setEorDir(dirCurrent);
729 stateAtEnd.setLastDir(dirCurrent);
730 stateAtEnd.setLastStrongDir(dirCurrent);
734 dirCurrent = c->dir();
737 dirCurrent = m_current.direction();
738 if (context()->override()
739 && dirCurrent != RightToLeftEmbedding
740 && dirCurrent != LeftToRightEmbedding
741 && dirCurrent != RightToLeftOverride
742 && dirCurrent != LeftToRightOverride
743 && dirCurrent != PopDirectionalFormat)
744 dirCurrent = context()->dir();
745 else if (dirCurrent == NonSpacingMark)
746 dirCurrent = m_status.last;
749 // We ignore all character directionality while in unicode-bidi: isolate spans.
750 // We'll handle ordering the isolated characters in a second pass.
752 dirCurrent = OtherNeutral;
754 ASSERT(m_status.eor != OtherNeutral || m_eor.atEnd());
755 switch (dirCurrent) {
757 // embedding and overrides (X1-X9 in the Bidi specs)
758 case RightToLeftEmbedding:
759 case LeftToRightEmbedding:
760 case RightToLeftOverride:
761 case LeftToRightOverride:
762 case PopDirectionalFormat:
763 embed(dirCurrent, FromUnicode);
764 commitExplicitEmbedding();
769 switch (m_status.last) {
771 case RightToLeftArabic:
774 if (m_status.last != EuropeanNumber || m_status.lastStrong != LeftToRight)
779 case EuropeanNumberSeparator:
780 case EuropeanNumberTerminator:
781 case CommonNumberSeparator:
782 case BoundaryNeutral:
784 case SegmentSeparator:
785 case WhiteSpaceNeutral:
787 if (m_status.eor == EuropeanNumber) {
788 if (m_status.lastStrong != LeftToRight) {
789 // the numbers need to be on a higher embedding level, so let's close that run
790 m_direction = EuropeanNumber;
792 if (context()->dir() != LeftToRight) {
793 // the neutrals take the embedding direction, which is R
795 m_direction = RightToLeft;
799 } else if (m_status.eor == ArabicNumber) {
800 // Arabic numbers are always on a higher embedding level, so let's close that run
801 m_direction = ArabicNumber;
803 if (context()->dir() != LeftToRight) {
804 // the neutrals take the embedding direction, which is R
806 m_direction = RightToLeft;
809 } else if (m_status.lastStrong != LeftToRight) {
810 // last stuff takes embedding dir
811 if (context()->dir() == RightToLeft) {
813 m_direction = RightToLeft;
821 m_status.eor = LeftToRight;
822 m_status.lastStrong = LeftToRight;
823 m_direction = LeftToRight;
825 case RightToLeftArabic:
827 switch (m_status.last) {
833 case RightToLeftArabic:
835 case EuropeanNumberSeparator:
836 case EuropeanNumberTerminator:
837 case CommonNumberSeparator:
838 case BoundaryNeutral:
840 case SegmentSeparator:
841 case WhiteSpaceNeutral:
843 if (m_status.eor == EuropeanNumber) {
844 if (m_status.lastStrong == LeftToRight && context()->dir() == LeftToRight)
847 } else if (m_status.eor == ArabicNumber) {
849 } else if (m_status.lastStrong == LeftToRight) {
850 if (context()->dir() == LeftToRight)
858 m_status.eor = RightToLeft;
859 m_status.lastStrong = dirCurrent;
860 m_direction = RightToLeft;
866 if (m_status.lastStrong != RightToLeftArabic) {
867 // if last strong was AL change EN to AN
868 switch (m_status.last) {
873 case RightToLeftArabic:
877 m_direction = EuropeanNumber;
879 case EuropeanNumberSeparator:
880 case CommonNumberSeparator:
881 if (m_status.eor == EuropeanNumber)
883 case EuropeanNumberTerminator:
884 case BoundaryNeutral:
886 case SegmentSeparator:
887 case WhiteSpaceNeutral:
889 if (m_status.eor == EuropeanNumber) {
890 if (m_status.lastStrong == RightToLeft) {
891 // ENs on both sides behave like Rs, so the neutrals should be R.
892 // Terminate the EN run.
895 m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last;
896 m_direction = RightToLeft;
898 // Begin a new EN run.
899 m_direction = EuropeanNumber;
901 } else if (m_status.eor == ArabicNumber) {
902 // Terminate the AN run.
904 if (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft) {
906 m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last;
907 m_direction = RightToLeft;
909 // Begin a new EN run.
910 m_direction = EuropeanNumber;
912 } else if (m_status.lastStrong == RightToLeft) {
913 // Extend the R run to include the neutrals.
914 m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last;
915 m_direction = RightToLeft;
917 // Begin a new EN run.
918 m_direction = EuropeanNumber;
924 m_status.eor = EuropeanNumber;
925 if (m_direction == OtherNeutral)
926 m_direction = LeftToRight;
930 dirCurrent = ArabicNumber;
931 switch (m_status.last) {
933 if (context()->dir() == LeftToRight)
939 case RightToLeftArabic:
944 case CommonNumberSeparator:
945 if (m_status.eor == ArabicNumber)
947 case EuropeanNumberSeparator:
948 case EuropeanNumberTerminator:
949 case BoundaryNeutral:
951 case SegmentSeparator:
952 case WhiteSpaceNeutral:
954 if (m_status.eor == ArabicNumber
955 || (m_status.eor == EuropeanNumber && (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft))
956 || (m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && context()->dir() == RightToLeft)) {
957 // Terminate the run before the neutrals.
959 // Begin an R run for the neutrals.
960 m_direction = RightToLeft;
961 } else if (m_direction == OtherNeutral) {
962 m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft;
970 m_status.eor = ArabicNumber;
971 if (m_direction == OtherNeutral)
972 m_direction = ArabicNumber;
974 case EuropeanNumberSeparator:
975 case CommonNumberSeparator:
977 case EuropeanNumberTerminator:
978 if (m_status.last == EuropeanNumber) {
979 dirCurrent = EuropeanNumber;
981 m_status.eor = dirCurrent;
982 } else if (m_status.last != EuropeanNumberTerminator) {
983 m_lastBeforeET = m_emptyRun ? m_eor : m_last;
987 // boundary neutrals should be ignored
988 case BoundaryNeutral:
994 // ### what do we do with newline and paragraph seperators that come to here?
996 case SegmentSeparator:
997 // ### implement rule L1
999 case WhiteSpaceNeutral:
1007 if (lastLineEnded && m_eor == m_current) {
1008 if (!m_reachedEndOfLine) {
1009 m_eor = m_endOfRunAtEndOfLine;
1010 switch (m_status.eor) {
1014 m_direction = m_status.eor;
1016 case EuropeanNumber:
1017 m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : EuropeanNumber;
1020 ASSERT_NOT_REACHED();
1025 m_status = stateAtEnd.m_status;
1026 m_sor = stateAtEnd.m_sor;
1027 m_eor = stateAtEnd.m_eor;
1028 m_last = stateAtEnd.m_last;
1029 m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine;
1030 m_lastBeforeET = stateAtEnd.m_lastBeforeET;
1031 m_emptyRun = stateAtEnd.m_emptyRun;
1032 m_direction = OtherNeutral;
1036 updateStatusLastFromCurrentDirection(dirCurrent);
1045 if (!m_currentExplicitEmbeddingSequence.isEmpty()) {
1046 bool committed = commitExplicitEmbedding();
1047 if (committed && lastLineEnded) {
1049 m_status = stateAtEnd.m_status;
1050 m_sor = stateAtEnd.m_sor;
1051 m_eor = stateAtEnd.m_eor;
1052 m_last = stateAtEnd.m_last;
1053 m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine;
1054 m_lastBeforeET = stateAtEnd.m_lastBeforeET;
1055 m_emptyRun = stateAtEnd.m_emptyRun;
1056 m_direction = OtherNeutral;
1062 m_runs.setLogicallyLastRun(m_runs.lastRun());
1064 reorderRunsFromLevels();
1065 m_endOfRunAtEndOfLine = Iterator();
1066 m_endOfLine = Iterator();
1068 if (!hardLineBreak && m_runs.runCount())
1072 template <class Iterator, class Run>
1073 void BidiResolver<Iterator, Run>::setMidpointStateForIsolatedRun(Run* run, const MidpointState<Iterator>& midpoint)
1075 ASSERT(!m_midpointStateForIsolatedRun.contains(run));
1076 m_midpointStateForIsolatedRun.add(run, midpoint);
1079 template<class Iterator, class Run>
1080 MidpointState<Iterator> BidiResolver<Iterator, Run>::midpointStateForIsolatedRun(Run* run)
1082 return m_midpointStateForIsolatedRun.take(run);
1086 } // namespace WebCore
1088 #endif // BidiResolver_h