• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KHTML

BidiResolver.h

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
00003  * Copyright (C) 2003, 2004, 2006, 2007, 2008 Apple Inc.  All right reserved.
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Library General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Library General Public License
00016  * along with this library; see the file COPYING.LIB.  If not, write to
00017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019  *
00020  */
00021 
00022 #ifndef BidiResolver_h
00023 #define BidiResolver_h
00024 
00025 #include "BidiContext.h"
00026 #include <wtf/Noncopyable.h>
00027 #include <wtf/PassRefPtr.h>
00028 
00029 namespace WebCore {
00030 
00031 // The BidiStatus at a given position (typically the end of a line) can
00032 // be cached and then used to restart bidi resolution at that position.
00033 struct BidiStatus {
00034     BidiStatus()
00035         : eor(WTF::Unicode::OtherNeutral)
00036         , lastStrong(WTF::Unicode::OtherNeutral)
00037         , last(WTF::Unicode::OtherNeutral)
00038     {
00039     }
00040 
00041     BidiStatus(WTF::Unicode::Direction eorDir, WTF::Unicode::Direction lastStrongDir, WTF::Unicode::Direction lastDir, PassRefPtr<BidiContext> bidiContext)
00042         : eor(eorDir)
00043         , lastStrong(lastStrongDir)
00044         , last(lastDir)
00045         , context(bidiContext)
00046     {
00047     }
00048 
00049     WTF::Unicode::Direction eor;
00050     WTF::Unicode::Direction lastStrong;
00051     WTF::Unicode::Direction last;
00052     RefPtr<BidiContext> context;
00053 };
00054 
00055 inline bool operator==(const BidiStatus& status1, const BidiStatus& status2)
00056 {
00057     return status1.eor == status2.eor && status1.last == status2.last && status1.lastStrong == status2.lastStrong && *(status1.context) == *(status2.context);
00058 }
00059 
00060 inline bool operator!=(const BidiStatus& status1, const BidiStatus& status2)
00061 {
00062     return !(status1 == status2);
00063 }
00064 
00065 struct BidiCharacterRun {
00066     BidiCharacterRun(int start, int stop, BidiContext* context, WTF::Unicode::Direction dir)
00067         : m_start(start)
00068         , m_stop(stop)
00069         , m_override(context->override())
00070         , m_next(0)
00071     {
00072         if (dir == WTF::Unicode::OtherNeutral)
00073             dir = context->dir();
00074 
00075         m_level = context->level();
00076 
00077         // add level of run (cases I1 & I2)
00078         if (m_level % 2) {
00079             if (dir == WTF::Unicode::LeftToRight || dir == WTF::Unicode::ArabicNumber || dir == WTF::Unicode::EuropeanNumber)
00080                 m_level++;
00081         } else {
00082             if (dir == WTF::Unicode::RightToLeft)
00083                 m_level++;
00084             else if (dir == WTF::Unicode::ArabicNumber || dir == WTF::Unicode::EuropeanNumber)
00085                 m_level += 2;
00086         }
00087     }
00088 
00089     void destroy() { delete this; }
00090 
00091     int start() const { return m_start; }
00092     int stop() const { return m_stop; }
00093     unsigned char level() const { return m_level; }
00094     bool reversed(bool visuallyOrdered) { return m_level % 2 && !visuallyOrdered; }
00095     bool dirOverride(bool visuallyOrdered) { return m_override || visuallyOrdered; }
00096 
00097     BidiCharacterRun* next() const { return m_next; }
00098 
00099     unsigned char m_level;
00100     int m_start;
00101     int m_stop;
00102     bool m_override;
00103     BidiCharacterRun* m_next;
00104 };
00105 
00106 template <class Iterator, class Run> class BidiResolver : public Noncopyable {
00107 public :
00108     BidiResolver()
00109         : m_direction(WTF::Unicode::OtherNeutral)
00110         , reachedEndOfLine(false)
00111         , emptyRun(true)
00112         , m_firstRun(0)
00113         , m_lastRun(0)
00114         , m_logicallyLastRun(0)
00115         , m_runCount(0)
00116     {
00117     }
00118 
00119     const Iterator& position() const { return current; }
00120     void setPosition(const Iterator& position) { current = position; }
00121 
00122     void increment() { current.increment(); }
00123 
00124     BidiContext* context() const { return m_status.context.get(); }
00125     void setContext(PassRefPtr<BidiContext> c) { m_status.context = c; }
00126 
00127     void setLastDir(WTF::Unicode::Direction lastDir) { m_status.last = lastDir; }
00128     void setLastStrongDir(WTF::Unicode::Direction lastStrongDir) { m_status.lastStrong = lastStrongDir; }
00129     void setEorDir(WTF::Unicode::Direction eorDir) { m_status.eor = eorDir; }
00130 
00131     WTF::Unicode::Direction dir() const { return m_direction; }
00132     void setDir(WTF::Unicode::Direction d) { m_direction = d; }
00133 
00134     const BidiStatus& status() const { return m_status; }
00135     void setStatus(const BidiStatus s) { m_status = s; }
00136 
00137     void embed(WTF::Unicode::Direction);
00138     void createBidiRunsForLine(const Iterator& end, bool visualOrder = false, bool hardLineBreak = false);
00139 
00140     Run* firstRun() const { return m_firstRun; }
00141     Run* lastRun() const { return m_lastRun; }
00142     Run* logicallyLastRun() const { return m_logicallyLastRun; }
00143     unsigned runCount() const { return m_runCount; }
00144 
00145     void addRun(Run*);
00146     void prependRun(Run*);
00147     void deleteRuns();
00148 
00149 protected:
00150     void appendRun();
00151     void reverseRuns(unsigned start, unsigned end);
00152 
00153     Iterator current;
00154     Iterator sor;
00155     Iterator eor;
00156     Iterator last;
00157     BidiStatus m_status;
00158     WTF::Unicode::Direction m_direction;
00159     Iterator endOfLine;
00160     bool reachedEndOfLine;
00161     Iterator lastBeforeET;
00162     bool emptyRun;
00163 
00164     Run* m_firstRun;
00165     Run* m_lastRun;
00166     Run* m_logicallyLastRun;
00167     unsigned m_runCount;
00168 };
00169 
00170 template <class Iterator, class Run>
00171 inline void BidiResolver<Iterator, Run>::addRun(Run* run)
00172 {
00173     if (!m_firstRun)
00174         m_firstRun = run;
00175     else
00176         m_lastRun->m_next = run;
00177     m_lastRun = run;
00178     m_runCount++;
00179 }
00180 
00181 template <class Iterator, class Run>
00182 inline void BidiResolver<Iterator, Run>::prependRun(Run* run)
00183 {
00184     ASSERT(!run->m_next);
00185 
00186     if (!m_lastRun)
00187         m_lastRun = run;
00188     else
00189         run->m_next = m_firstRun;
00190     m_firstRun = run;
00191     m_runCount++;
00192 }
00193 
00194 template <class Iterator, class Run>
00195 void BidiResolver<Iterator, Run>::appendRun()
00196 {
00197     if (emptyRun || eor.atEnd())
00198         return;
00199 
00200     addRun(new Run(sor.offset(), eor.offset() + 1, context(), m_direction));
00201 
00202     eor.increment();
00203     sor = eor;
00204     m_direction = WTF::Unicode::OtherNeutral;
00205     m_status.eor = WTF::Unicode::OtherNeutral;
00206 }
00207 
00208 template <class Iterator, class Run>
00209 void BidiResolver<Iterator, Run>::embed(WTF::Unicode::Direction d)
00210 {
00211     using namespace WTF::Unicode;
00212 
00213     if (d == PopDirectionalFormat) {
00214         BidiContext* c = context()->parent();
00215         if (c) {
00216             if (!emptyRun && eor != last) {
00217                 ASSERT(m_status.eor != OtherNeutral || eor.atEnd());
00218                 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
00219                 ASSERT(m_status.last == EuropeanNumberSeparator
00220                     || m_status.last == EuropeanNumberTerminator
00221                     || m_status.last == CommonNumberSeparator
00222                     || m_status.last == BoundaryNeutral
00223                     || m_status.last == BlockSeparator
00224                     || m_status.last == SegmentSeparator
00225                     || m_status.last == WhiteSpaceNeutral
00226                     || m_status.last == OtherNeutral);
00227                 if (m_direction == OtherNeutral)
00228                     m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft;
00229                 if (context()->dir() == LeftToRight) {
00230                     // bidi.sor ... bidi.eor ... bidi.last L
00231                     if (m_status.eor == EuropeanNumber) {
00232                         if (m_status.lastStrong != LeftToRight) {
00233                             m_direction = EuropeanNumber;
00234                             appendRun();
00235                         }
00236                     } else if (m_status.eor == ArabicNumber) {
00237                         m_direction = ArabicNumber;
00238                         appendRun();
00239                     } else if (m_status.lastStrong != LeftToRight) {
00240                         if (context()->dir() == RightToLeft)
00241                             m_direction = RightToLeft;
00242                         else {
00243                             appendRun();
00244                             m_direction = LeftToRight;
00245                         }
00246                     }
00247                 } else if (m_status.eor == EuropeanNumber || m_status.eor == ArabicNumber || m_status.lastStrong == LeftToRight) {
00248                     appendRun();
00249                     m_direction = RightToLeft;
00250                 }
00251                 eor = last;
00252             }
00253             appendRun();
00254             emptyRun = true;
00255             // sor for the new run is determined by the higher level (rule X10)
00256             setLastDir(context()->dir());
00257             setLastStrongDir(context()->dir());
00258             setContext(c);
00259             eor = Iterator();
00260         }
00261     } else {
00262         Direction runDir;
00263         if (d == RightToLeftEmbedding || d == RightToLeftOverride)
00264             runDir = RightToLeft;
00265         else
00266             runDir = LeftToRight;
00267         bool override = d == LeftToRightOverride || d == RightToLeftOverride;
00268 
00269         unsigned char level = context()->level();
00270         if (runDir == RightToLeft) {
00271             if (level % 2) // we have an odd level
00272                 level += 2;
00273             else
00274                 level++;
00275         } else {
00276             if (level % 2) // we have an odd level
00277                 level++;
00278             else
00279                 level += 2;
00280         }
00281 
00282         if (level < 61) {
00283             if (!emptyRun && eor != last) {
00284                 ASSERT(m_status.eor != OtherNeutral || eor.atEnd());
00285                 // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
00286                 ASSERT(m_status.last == EuropeanNumberSeparator
00287                     || m_status.last == EuropeanNumberTerminator
00288                     || m_status.last == CommonNumberSeparator
00289                     || m_status.last == BoundaryNeutral
00290                     || m_status.last == BlockSeparator
00291                     || m_status.last == SegmentSeparator
00292                     || m_status.last == WhiteSpaceNeutral
00293                     || m_status.last == OtherNeutral);
00294                 if (m_direction == OtherNeutral)
00295                     m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft;
00296                 if (runDir == LeftToRight) {
00297                     // bidi.sor ... bidi.eor ... bidi.last L
00298                     if (m_status.eor == EuropeanNumber) {
00299                         if (m_status.lastStrong != LeftToRight) {
00300                             m_direction = EuropeanNumber;
00301                             appendRun();
00302                         }
00303                     } else if (m_status.eor == ArabicNumber) {
00304                         m_direction = ArabicNumber;
00305                         appendRun();
00306                     } else if (m_status.lastStrong != LeftToRight && context()->dir() == LeftToRight) {
00307                         appendRun();
00308                         m_direction = LeftToRight;
00309                     }
00310                 } else if (m_status.eor == ArabicNumber
00311                     || m_status.eor == EuropeanNumber && (m_status.lastStrong != LeftToRight || context()->dir() == RightToLeft)
00312                     || m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && context()->dir() == RightToLeft) {
00313                     appendRun();
00314                     m_direction = RightToLeft;
00315                 }
00316                 eor = last;
00317             }
00318             appendRun();
00319             emptyRun = true;
00320             setContext(new BidiContext(level, runDir, override, context()));
00321             setLastDir(runDir);
00322             setLastStrongDir(runDir);
00323             eor = Iterator();
00324         }
00325     }
00326 }
00327 
00328 template <class Iterator, class Run>
00329 void BidiResolver<Iterator, Run>::deleteRuns()
00330 {
00331     emptyRun = true;
00332     if (!m_firstRun)
00333         return;
00334 
00335     Run* curr = m_firstRun;
00336     while (curr) {
00337         Run* s = curr->next();
00338         curr->destroy();
00339         curr = s;
00340     }
00341 
00342     m_firstRun = 0;
00343     m_lastRun = 0;
00344     m_runCount = 0;
00345 }
00346 
00347 template <class Iterator, class Run>
00348 void BidiResolver<Iterator, Run>::reverseRuns(unsigned start, unsigned end)
00349 {
00350     if (start >= end)
00351         return;
00352 
00353     ASSERT(end < m_runCount);
00354     
00355     // Get the item before the start of the runs to reverse and put it in
00356     // |beforeStart|.  |curr| should point to the first run to reverse.
00357     Run* curr = m_firstRun;
00358     Run* beforeStart = 0;
00359     unsigned i = 0;
00360     while (i < start) {
00361         i++;
00362         beforeStart = curr;
00363         curr = curr->next();
00364     }
00365 
00366     Run* startRun = curr;
00367     while (i < end) {
00368         i++;
00369         curr = curr->next();
00370     }
00371     Run* endRun = curr;
00372     Run* afterEnd = curr->next();
00373 
00374     i = start;
00375     curr = startRun;
00376     Run* newNext = afterEnd;
00377     while (i <= end) {
00378         // Do the reversal.
00379         Run* next = curr->next();
00380         curr->m_next = newNext;
00381         newNext = curr;
00382         curr = next;
00383         i++;
00384     }
00385 
00386     // Now hook up beforeStart and afterEnd to the startRun and endRun.
00387     if (beforeStart)
00388         beforeStart->m_next = endRun;
00389     else
00390         m_firstRun = endRun;
00391 
00392     startRun->m_next = afterEnd;
00393     if (!afterEnd)
00394         m_lastRun = startRun;
00395 }
00396 
00397 template <class Iterator, class Run>
00398 void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& end, bool visualOrder, bool hardLineBreak)
00399 {
00400     using namespace WTF::Unicode;
00401 
00402     ASSERT(m_direction == OtherNeutral);
00403 
00404     emptyRun = true;
00405 
00406     eor = Iterator();
00407 
00408     last = current;
00409     bool pastEnd = false;
00410     BidiResolver<Iterator, Run> stateAtEnd;
00411 
00412     while (true) {
00413         Direction dirCurrent;
00414         if (pastEnd && (hardLineBreak || current.atEnd())) {
00415             BidiContext* c = context();
00416             while (c->parent())
00417                 c = c->parent();
00418             dirCurrent = c->dir();
00419             if (hardLineBreak) {
00420                 // A deviation from the Unicode Bidi Algorithm in order to match
00421                 // Mac OS X text and WinIE: a hard line break resets bidi state.
00422                 stateAtEnd.setContext(c);
00423                 stateAtEnd.setEorDir(dirCurrent);
00424                 stateAtEnd.setLastDir(dirCurrent);
00425                 stateAtEnd.setLastStrongDir(dirCurrent);
00426             }
00427         } else {
00428             dirCurrent = current.direction();
00429             if (context()->override()
00430                     && dirCurrent != RightToLeftEmbedding
00431                     && dirCurrent != LeftToRightEmbedding
00432                     && dirCurrent != RightToLeftOverride
00433                     && dirCurrent != LeftToRightOverride
00434                     && dirCurrent != PopDirectionalFormat)
00435                 dirCurrent = context()->dir();
00436             else if (dirCurrent == NonSpacingMark)
00437                 dirCurrent = m_status.last;
00438         }
00439 
00440         ASSERT(m_status.eor != OtherNeutral || eor.atEnd());
00441         switch (dirCurrent) {
00442 
00443         // embedding and overrides (X1-X9 in the Bidi specs)
00444         case RightToLeftEmbedding:
00445         case LeftToRightEmbedding:
00446         case RightToLeftOverride:
00447         case LeftToRightOverride:
00448         case PopDirectionalFormat:
00449             embed(dirCurrent);
00450             break;
00451 
00452             // strong types
00453         case LeftToRight:
00454             switch(m_status.last) {
00455                 case RightToLeft:
00456                 case RightToLeftArabic:
00457                 case EuropeanNumber:
00458                 case ArabicNumber:
00459                     if (m_status.last != EuropeanNumber || m_status.lastStrong != LeftToRight)
00460                         appendRun();
00461                     break;
00462                 case LeftToRight:
00463                     break;
00464                 case EuropeanNumberSeparator:
00465                 case EuropeanNumberTerminator:
00466                 case CommonNumberSeparator:
00467                 case BoundaryNeutral:
00468                 case BlockSeparator:
00469                 case SegmentSeparator:
00470                 case WhiteSpaceNeutral:
00471                 case OtherNeutral:
00472                     if (m_status.eor == EuropeanNumber) {
00473                         if (m_status.lastStrong != LeftToRight) {
00474                             // the numbers need to be on a higher embedding level, so let's close that run
00475                             m_direction = EuropeanNumber;
00476                             appendRun();
00477                             if (context()->dir() != LeftToRight) {
00478                                 // the neutrals take the embedding direction, which is R
00479                                 eor = last;
00480                                 m_direction = RightToLeft;
00481                                 appendRun();
00482                             }
00483                         }
00484                     } else if (m_status.eor == ArabicNumber) {
00485                         // Arabic numbers are always on a higher embedding level, so let's close that run
00486                         m_direction = ArabicNumber;
00487                         appendRun();
00488                         if (context()->dir() != LeftToRight) {
00489                             // the neutrals take the embedding direction, which is R
00490                             eor = last;
00491                             m_direction = RightToLeft;
00492                             appendRun();
00493                         }
00494                     } else if (m_status.lastStrong != LeftToRight) {
00495                         //last stuff takes embedding dir
00496                         if (context()->dir() == RightToLeft) {
00497                             eor = last; 
00498                             m_direction = RightToLeft;
00499                         }
00500                         appendRun();
00501                     }
00502                 default:
00503                     break;
00504             }
00505             eor = current;
00506             m_status.eor = LeftToRight;
00507             m_status.lastStrong = LeftToRight;
00508             m_direction = LeftToRight;
00509             break;
00510         case RightToLeftArabic:
00511         case RightToLeft:
00512             switch (m_status.last) {
00513                 case LeftToRight:
00514                 case EuropeanNumber:
00515                 case ArabicNumber:
00516                     appendRun();
00517                 case RightToLeft:
00518                 case RightToLeftArabic:
00519                     break;
00520                 case EuropeanNumberSeparator:
00521                 case EuropeanNumberTerminator:
00522                 case CommonNumberSeparator:
00523                 case BoundaryNeutral:
00524                 case BlockSeparator:
00525                 case SegmentSeparator:
00526                 case WhiteSpaceNeutral:
00527                 case OtherNeutral:
00528                     if (m_status.eor == EuropeanNumber) {
00529                         if (m_status.lastStrong == LeftToRight && context()->dir() == LeftToRight)
00530                             eor = last;
00531                         appendRun();
00532                     } else if (m_status.eor == ArabicNumber)
00533                         appendRun();
00534                     else if (m_status.lastStrong == LeftToRight) {
00535                         if (context()->dir() == LeftToRight)
00536                             eor = last;
00537                         appendRun();
00538                     }
00539                 default:
00540                     break;
00541             }
00542             eor = current;
00543             m_status.eor = RightToLeft;
00544             m_status.lastStrong = dirCurrent;
00545             m_direction = RightToLeft;
00546             break;
00547 
00548             // weak types:
00549 
00550         case EuropeanNumber:
00551             if (m_status.lastStrong != RightToLeftArabic) {
00552                 // if last strong was AL change EN to AN
00553                 switch (m_status.last) {
00554                     case EuropeanNumber:
00555                     case LeftToRight:
00556                         break;
00557                     case RightToLeft:
00558                     case RightToLeftArabic:
00559                     case ArabicNumber:
00560                         eor = last;
00561                         appendRun();
00562                         m_direction = EuropeanNumber;
00563                         break;
00564                     case EuropeanNumberSeparator:
00565                     case CommonNumberSeparator:
00566                         if (m_status.eor == EuropeanNumber)
00567                             break;
00568                     case EuropeanNumberTerminator:
00569                     case BoundaryNeutral:
00570                     case BlockSeparator:
00571                     case SegmentSeparator:
00572                     case WhiteSpaceNeutral:
00573                     case OtherNeutral:
00574                         if (m_status.eor == EuropeanNumber) {
00575                             if (m_status.lastStrong == RightToLeft) {
00576                                 // ENs on both sides behave like Rs, so the neutrals should be R.
00577                                 // Terminate the EN run.
00578                                 appendRun();
00579                                 // Make an R run.
00580                                 eor = m_status.last == EuropeanNumberTerminator ? lastBeforeET : last;
00581                                 m_direction = RightToLeft;
00582                                 appendRun();
00583                                 // Begin a new EN run.
00584                                 m_direction = EuropeanNumber;
00585                             }
00586                         } else if (m_status.eor == ArabicNumber) {
00587                             // Terminate the AN run.
00588                             appendRun();
00589                             if (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft) {
00590                                 // Make an R run.
00591                                 eor = m_status.last == EuropeanNumberTerminator ? lastBeforeET : last;
00592                                 m_direction = RightToLeft;
00593                                 appendRun();
00594                                 // Begin a new EN run.
00595                                 m_direction = EuropeanNumber;
00596                             }
00597                         } else if (m_status.lastStrong == RightToLeft) {
00598                             // Extend the R run to include the neutrals.
00599                             eor = m_status.last == EuropeanNumberTerminator ? lastBeforeET : last;
00600                             m_direction = RightToLeft;
00601                             appendRun();
00602                             // Begin a new EN run.
00603                             m_direction = EuropeanNumber;
00604                         }
00605                     default:
00606                         break;
00607                 }
00608                 eor = current;
00609                 m_status.eor = EuropeanNumber;
00610                 if (m_direction == OtherNeutral)
00611                     m_direction = LeftToRight;
00612                 break;
00613             }
00614         case ArabicNumber:
00615             dirCurrent = ArabicNumber;
00616             switch (m_status.last) {
00617                 case LeftToRight:
00618                     if (context()->dir() == LeftToRight)
00619                         appendRun();
00620                     break;
00621                 case ArabicNumber:
00622                     break;
00623                 case RightToLeft:
00624                 case RightToLeftArabic:
00625                 case EuropeanNumber:
00626                     eor = last;
00627                     appendRun();
00628                     break;
00629                 case CommonNumberSeparator:
00630                     if (m_status.eor == ArabicNumber)
00631                         break;
00632                 case EuropeanNumberSeparator:
00633                 case EuropeanNumberTerminator:
00634                 case BoundaryNeutral:
00635                 case BlockSeparator:
00636                 case SegmentSeparator:
00637                 case WhiteSpaceNeutral:
00638                 case OtherNeutral:
00639                     if (m_status.eor == ArabicNumber
00640                         || m_status.eor == EuropeanNumber && (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft)
00641                         || m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && context()->dir() == RightToLeft) {
00642                         // Terminate the run before the neutrals.
00643                         appendRun();
00644                         // Begin an R run for the neutrals.
00645                         m_direction = RightToLeft;
00646                     } else if (m_direction == OtherNeutral)
00647                         m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft;
00648                     eor = last;
00649                     appendRun();
00650                 default:
00651                     break;
00652             }
00653             eor = current;
00654             m_status.eor = ArabicNumber;
00655             if (m_direction == OtherNeutral)
00656                 m_direction = ArabicNumber;
00657             break;
00658         case EuropeanNumberSeparator:
00659         case CommonNumberSeparator:
00660             break;
00661         case EuropeanNumberTerminator:
00662             if (m_status.last == EuropeanNumber) {
00663                 dirCurrent = EuropeanNumber;
00664                 eor = current;
00665                 m_status.eor = dirCurrent;
00666             } else if (m_status.last != EuropeanNumberTerminator)
00667                 lastBeforeET = emptyRun ? eor : last;
00668             break;
00669 
00670         // boundary neutrals should be ignored
00671         case BoundaryNeutral:
00672             if (eor == last)
00673                 eor = current;
00674             break;
00675             // neutrals
00676         case BlockSeparator:
00677             // ### what do we do with newline and paragraph seperators that come to here?
00678             break;
00679         case SegmentSeparator:
00680             // ### implement rule L1
00681             break;
00682         case WhiteSpaceNeutral:
00683             break;
00684         case OtherNeutral:
00685             break;
00686         default:
00687             break;
00688         }
00689 
00690         if (pastEnd) {
00691             if (eor == current) {
00692                 if (!reachedEndOfLine) {
00693                     eor = endOfLine;
00694                     switch (m_status.eor) {
00695                         case LeftToRight:
00696                         case RightToLeft:
00697                         case ArabicNumber:
00698                             m_direction = m_status.eor;
00699                             break;
00700                         case EuropeanNumber:
00701                             m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : EuropeanNumber;
00702                             break;
00703                         default:
00704                             ASSERT(false);
00705                     }
00706                     appendRun();
00707                 }
00708                 current = end;
00709                 m_status = stateAtEnd.m_status;
00710                 sor = stateAtEnd.sor; 
00711                 eor = stateAtEnd.eor;
00712                 last = stateAtEnd.last;
00713                 reachedEndOfLine = stateAtEnd.reachedEndOfLine;
00714                 lastBeforeET = stateAtEnd.lastBeforeET;
00715                 emptyRun = stateAtEnd.emptyRun;
00716                 m_direction = OtherNeutral;
00717                 break;
00718             }
00719         }
00720 
00721         // set m_status.last as needed.
00722         switch (dirCurrent) {
00723             case EuropeanNumberTerminator:
00724                 if (m_status.last != EuropeanNumber)
00725                     m_status.last = EuropeanNumberTerminator;
00726                 break;
00727             case EuropeanNumberSeparator:
00728             case CommonNumberSeparator:
00729             case SegmentSeparator:
00730             case WhiteSpaceNeutral:
00731             case OtherNeutral:
00732                 switch(m_status.last) {
00733                     case LeftToRight:
00734                     case RightToLeft:
00735                     case RightToLeftArabic:
00736                     case EuropeanNumber:
00737                     case ArabicNumber:
00738                         m_status.last = dirCurrent;
00739                         break;
00740                     default:
00741                         m_status.last = OtherNeutral;
00742                     }
00743                 break;
00744             case NonSpacingMark:
00745             case BoundaryNeutral:
00746             case RightToLeftEmbedding:
00747             case LeftToRightEmbedding:
00748             case RightToLeftOverride:
00749             case LeftToRightOverride:
00750             case PopDirectionalFormat:
00751                 // ignore these
00752                 break;
00753             case EuropeanNumber:
00754                 // fall through
00755             default:
00756                 m_status.last = dirCurrent;
00757         }
00758 
00759         last = current;
00760 
00761         if (emptyRun && !(dirCurrent == RightToLeftEmbedding
00762                 || dirCurrent == LeftToRightEmbedding
00763                 || dirCurrent == RightToLeftOverride
00764                 || dirCurrent == LeftToRightOverride
00765                 || dirCurrent == PopDirectionalFormat)) {
00766             sor = current;
00767             emptyRun = false;
00768         }
00769 
00770         increment();
00771         if (emptyRun && (dirCurrent == RightToLeftEmbedding
00772                 || dirCurrent == LeftToRightEmbedding
00773                 || dirCurrent == RightToLeftOverride
00774                 || dirCurrent == LeftToRightOverride
00775                 || dirCurrent == PopDirectionalFormat)) {
00776             // exclude the embedding char itself from the new run so that ATSUI will never see it
00777             eor = Iterator();
00778             last = current;
00779             sor = current;
00780         }
00781 
00782         if (!pastEnd && (current == end || current.atEnd())) {
00783             if (emptyRun)
00784                 break;
00785             stateAtEnd.m_status = m_status;
00786             stateAtEnd.sor = sor; 
00787             stateAtEnd.eor = eor;
00788             stateAtEnd.last = last;
00789             stateAtEnd.reachedEndOfLine = reachedEndOfLine;
00790             stateAtEnd.lastBeforeET = lastBeforeET;
00791             stateAtEnd.emptyRun = emptyRun;
00792             endOfLine = last;
00793             pastEnd = true;
00794         }
00795     }
00796 
00797     m_logicallyLastRun = m_lastRun;
00798 
00799     // reorder line according to run structure...
00800     // do not reverse for visually ordered web sites
00801     if (!visualOrder) {
00802 
00803         // first find highest and lowest levels
00804         unsigned char levelLow = 128;
00805         unsigned char levelHigh = 0;
00806         Run* r = firstRun();
00807         while (r) {
00808             if (r->m_level > levelHigh)
00809                 levelHigh = r->m_level;
00810             if (r->m_level < levelLow)
00811                 levelLow = r->m_level;
00812             r = r->next();
00813         }
00814 
00815         // implements reordering of the line (L2 according to Bidi spec):
00816         // L2. From the highest level found in the text to the lowest odd level on each line,
00817         // reverse any contiguous sequence of characters that are at that level or higher.
00818 
00819         // reversing is only done up to the lowest odd level
00820         if (!(levelLow % 2))
00821             levelLow++;
00822 
00823         unsigned count = runCount() - 1;
00824 
00825         while (levelHigh >= levelLow) {
00826             unsigned i = 0;
00827             Run* currRun = firstRun();
00828             while (i < count) {
00829                 while (i < count && currRun && currRun->m_level < levelHigh) {
00830                     i++;
00831                     currRun = currRun->next();
00832                 }
00833                 unsigned start = i;
00834                 while (i <= count && currRun && currRun->m_level >= levelHigh) {
00835                     i++;
00836                     currRun = currRun->next();
00837                 }
00838                 unsigned end = i - 1;
00839                 reverseRuns(start, end);
00840             }
00841             levelHigh--;
00842         }
00843     }
00844     endOfLine = Iterator();
00845 }
00846 
00847 } // namespace WebCore
00848 
00849 #endif // BidiResolver_h

KHTML

Skip menu "KHTML"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal