diff options
Diffstat (limited to 'gfx/graphite2/src/Bidi.cpp')
-rw-r--r-- | gfx/graphite2/src/Bidi.cpp | 826 |
1 files changed, 826 insertions, 0 deletions
diff --git a/gfx/graphite2/src/Bidi.cpp b/gfx/graphite2/src/Bidi.cpp new file mode 100644 index 000000000..48ec2ebfc --- /dev/null +++ b/gfx/graphite2/src/Bidi.cpp @@ -0,0 +1,826 @@ +/* GRAPHITE2 LICENSING + + Copyright 2011, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. + +Alternatively, the contents of this file may be used under the terms of the +Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public +License, as published by the Free Software Foundation, either version 2 +of the License or (at your option) any later version. +*/ +#include "inc/Main.h" +#include "inc/Slot.h" +#include "inc/Segment.h" +#include "inc/Bidi.h" + +using namespace graphite2; + +enum DirCode { // Hungarian: dirc + Unk = -1, + N = 0, // other neutrals (default) - ON + L = 1, // left-to-right, strong - L + R = 2, // right-to-left, strong - R + AL = 3, // Arabic letter, right-to-left, strong, AR + EN = 4, // European number, left-to-right, weak - EN + EUS = 5, // European separator, left-to-right, weak - ES + ET = 6, // European number terminator, left-to-right, weak - ET + AN = 7, // Arabic number, left-to-right, weak - AN + CUS = 8, // Common number separator, left-to-right, weak - CS + WS = 9, // white space, neutral - WS + BN = 10, // boundary neutral - BN + + LRO = 11, // LTR override + RLO = 12, // RTL override + LRE = 13, // LTR embedding + RLE = 14, // RTL embedding + PDF = 15, // pop directional format + NSM = 16, // non-space mark + LRI = 17, // LRI isolate + RLI = 18, // RLI isolate + FSI = 19, // FSI isolate + PDI = 20, // pop isolate + OPP = 21, // opening paired parenthesis + CPP = 22, // closing paired parenthesis + + ON = N +}; + +enum DirMask { + WSflag = int8(1 << 7), // keep track of WS for eos handling + WSMask = int8(~(1 << 7)) +}; + +inline uint8 BaseClass(Slot *s) { return s->getBidiClass() & WSMask; } + +unsigned int bidi_class_map[] = { 0, 1, 2, 5, 4, 8, 9, 3, 7, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0 }; +// Algorithms based on Unicode reference standard code. Thanks Asmus Freitag. + +void resolveWeak(Slot *start, int sos, int eos); +void resolveNeutrals(Slot *s, int baseLevel, int sos, int eos); +void processParens(Slot *s, Segment *seg, uint8 aMirror, int level, BracketPairStack &stack); + +inline int calc_base_level(Slot *s) +{ + int count = 0; + for ( ; s; s = s->next()) + { + int cls = s->getBidiClass(); + if (count) + { + switch(cls) + { + case LRI : + case RLI : + case FSI : + ++count; + break; + case PDI : + --count; + } + } + else + { + switch(cls) + { + case L : + return 0; + case R : + case AL : + return 1; + case LRI : + case RLI : + case FSI : + ++count; + } + } + } + return 0; +} + +// inline or not? +void do_resolves(Slot *start, int level, int sos, int eos, int &bmask, Segment *seg, uint8 aMirror, BracketPairStack &stack) +{ + if (bmask & 0x1F1178) + resolveWeak(start, sos, eos); + if (bmask & 0x200000) + processParens(start, seg, aMirror, level, stack); + if (bmask & 0x7E0361) + resolveNeutrals(start, level, sos, eos); + bmask = 0; +} + +enum maxs +{ + MAX_LEVEL = 125, +}; + +// returns where we are up to in processing +Slot *process_bidi(Slot *start, int level, int prelevel, int &nextLevel, int dirover, int isol, int &cisol, int &isolerr, int &embederr, int init, Segment *seg, uint8 aMirror, BracketPairStack &bstack) +{ + int bmask = 0; + Slot *s = start; + Slot *slast = start; + Slot *scurr = 0; + Slot *stemp; + int lnextLevel = nextLevel; + int newLevel; + int empty = 1; + for ( ; s; s = s ? s->next() : s) + { + int cls = s->getBidiClass(); + bmask |= (1 << cls); + s->setBidiLevel(level); + // we keep s->prev() pointing backwards for PDI repeating + + switch (cls) + { + case BN : + if (slast == s) slast = s->next(); // ignore if at front of text + continue; + case LRE : + case LRO : + case RLE : + case RLO : + switch (cls) + { + case LRE : + case LRO : + newLevel = level + (level & 1 ? 1 : 2); + break; + case RLE : + case RLO : + newLevel = level + (level & 1 ? 2 : 1); + break; + } + s->setBidiClass(BN); + if (isolerr || newLevel > MAX_LEVEL || embederr) + { + if (!isolerr) ++embederr; + break; + } + stemp = scurr; + if (scurr) + scurr->prev(0); // don't include control in string + lnextLevel = newLevel; + scurr = s; + s->setBidiLevel(newLevel); // to make it vanish + // recurse for the new subsequence. A sequence only contains text at the same level + s = process_bidi(s->next(), newLevel, level, lnextLevel, cls < LRE, 0, cisol, isolerr, embederr, 0, seg, aMirror, bstack); + // s points at PDF or end of sequence + // try to keep extending the run and not process it until we have to + if (lnextLevel != level || !s) // if the subsequence really had something in it, or we are at the end of the run + { + if (slast != scurr) // process the run now, don't try to extend it + { + // process text preceeding embedding + do_resolves(slast, level, (prelevel > level ? prelevel : level) & 1, lnextLevel & 1, bmask, seg, aMirror, bstack); + empty = 0; + nextLevel = level; + } + else if (lnextLevel != level) // the subsequence had something + { + empty = 0; // so we aren't empty either + nextLevel = lnextLevel; // but since we really are empty, pass back our level from the subsequence + } + if (s) // if still more to process + { + prelevel = lnextLevel; // future text starts out with sos of the higher subsequence + lnextLevel = level; // and eos is our level + } + slast = s ? s->next() : s; + } + else if (stemp) + stemp->prev(s); + break; + + case PDF : + s->setBidiClass(BN); + s->prev(0); // unstitch us since we skip final stitching code when we return + if (isol || isolerr || init) // boundary error conditions + break; + if (embederr) + { + --embederr; + break; + } + if (slast != s) + { + scurr->prev(0); // if slast, then scurr. Terminate before here + do_resolves(slast, level, level & 1, level & 1, bmask, seg, aMirror, bstack); + empty = 0; + } + if (empty) + { + nextLevel = prelevel; // no contents? set our level to that of parent + s->setBidiLevel(prelevel); + } + return s; + + case FSI : + case LRI : + case RLI : + switch (cls) + { + case FSI : + if (calc_base_level(s->next())) + newLevel = level + (level & 1 ? 2 : 1); + else + newLevel = level + (level & 1 ? 1 : 2); + break; + case LRI : + newLevel = level + (level & 1 ? 1 : 2); + break; + case RLI : + newLevel = level + (level & 1 ? 2 : 1); + break; + } + if (newLevel > MAX_LEVEL || isolerr) + { + ++isolerr; + s->setBidiClass(ON | WSflag); + break; + } + ++cisol; + if (scurr) scurr->prev(s); + scurr = s; // include FSI + lnextLevel = newLevel; + // recurse for the new sub sequence + s = process_bidi(s->next(), newLevel, newLevel, lnextLevel, 0, 1, cisol, isolerr, embederr, 0, seg, aMirror, bstack); + // s points at PDI + if (s) + { + bmask |= 1 << BaseClass(s); // include the PDI in the mask + s->setBidiLevel(level); // reset its level to our level + } + lnextLevel = level; + break; + + case PDI : + if (isolerr) + { + --isolerr; + s->setBidiClass(ON | WSflag); + break; + } + if (init || !cisol) + { + s->setBidiClass(ON | WSflag); + break; + } + embederr = 0; + if (!isol) // we are in an embedded subsequence, we have to return through all those + { + if (empty) // if empty, reset the level to tell embedded parent + nextLevel = prelevel; + return s->prev(); // keep working up the stack pointing at this PDI until we get to an isolate entry + } + else // we are terminating an isolate sequence + { + if (slast != s) // process any remaining content in this subseqence + { + scurr->prev(0); + do_resolves(slast, level, prelevel & 1, level & 1, bmask, seg, aMirror, bstack); + } + --cisol; // pop the isol sequence from the stack + return s; + } + + default : + if (dirover) + s->setBidiClass((level & 1 ? R : L) | (WSflag * (cls == WS))); + } + if (s) s->prev(0); // unstitch us + if (scurr) // stitch in text for processing + scurr->prev(s); + scurr = s; // add us to text to process + } + if (slast != s) + { + do_resolves(slast, level, (level > prelevel ? level : prelevel) & 1, lnextLevel & 1, bmask, seg, aMirror, bstack); + empty = 0; + } + if (empty || isol) + nextLevel = prelevel; + return s; +} + +// === RESOLVE WEAK TYPES ================================================ + +enum bidi_state // possible states +{ + xa, // arabic letter + xr, // right leter + xl, // left letter + + ao, // arabic lett. foll by ON + ro, // right lett. foll by ON + lo, // left lett. foll by ON + + rt, // ET following R + lt, // ET following L + + cn, // EN, AN following AL + ra, // arabic number foll R + re, // european number foll R + la, // arabic number foll L + le, // european number foll L + + ac, // CS following cn + rc, // CS following ra + rs, // CS,ES following re + lc, // CS following la + ls, // CS,ES following le + + ret, // ET following re + let, // ET following le +} ; + +const bidi_state stateWeak[][10] = +{ + // N, L, R, AN, EN, AL,NSM, CS, ES, ET, +{ /*xa*/ ao, xl, xr, cn, cn, xa, xa, ao, ao, ao, /* arabic letter */ }, +{ /*xr*/ ro, xl, xr, ra, re, xa, xr, ro, ro, rt, /* right letter */ }, +{ /*xl*/ lo, xl, xr, la, le, xa, xl, lo, lo, lt, /* left letter */ }, + +{ /*ao*/ ao, xl, xr, cn, cn, xa, ao, ao, ao, ao, /* arabic lett. foll by ON*/ }, +{ /*ro*/ ro, xl, xr, ra, re, xa, ro, ro, ro, rt, /* right lett. foll by ON */ }, +{ /*lo*/ lo, xl, xr, la, le, xa, lo, lo, lo, lt, /* left lett. foll by ON */ }, + +{ /*rt*/ ro, xl, xr, ra, re, xa, rt, ro, ro, rt, /* ET following R */ }, +{ /*lt*/ lo, xl, xr, la, le, xa, lt, lo, lo, lt, /* ET following L */ }, + +{ /*cn*/ ao, xl, xr, cn, cn, xa, cn, ac, ao, ao, /* EN, AN following AL */ }, +{ /*ra*/ ro, xl, xr, ra, re, xa, ra, rc, ro, rt, /* arabic number foll R */ }, +{ /*re*/ ro, xl, xr, ra, re, xa, re, rs, rs,ret, /* european number foll R */ }, +{ /*la*/ lo, xl, xr, la, le, xa, la, lc, lo, lt, /* arabic number foll L */ }, +{ /*le*/ lo, xl, xr, la, le, xa, le, ls, ls,let, /* european number foll L */ }, + +{ /*ac*/ ao, xl, xr, cn, cn, xa, ao, ao, ao, ao, /* CS following cn */ }, +{ /*rc*/ ro, xl, xr, ra, re, xa, ro, ro, ro, rt, /* CS following ra */ }, +{ /*rs*/ ro, xl, xr, ra, re, xa, ro, ro, ro, rt, /* CS,ES following re */ }, +{ /*lc*/ lo, xl, xr, la, le, xa, lo, lo, lo, lt, /* CS following la */ }, +{ /*ls*/ lo, xl, xr, la, le, xa, lo, lo, lo, lt, /* CS,ES following le */ }, + +{ /*ret*/ ro, xl, xr, ra, re, xa,ret, ro, ro,ret, /* ET following re */ }, +{ /*let*/ lo, xl, xr, la, le, xa,let, lo, lo,let, /* ET following le */ }, + + +}; + +enum bidi_action // possible actions +{ + // primitives + IX = 0x100, // increment + XX = 0xF, // no-op + + // actions + xxx = (XX << 4) + XX, // no-op + xIx = IX + xxx, // increment run + xxN = (XX << 4) + ON, // set current to N + xxE = (XX << 4) + EN, // set current to EN + xxA = (XX << 4) + AN, // set current to AN + xxR = (XX << 4) + R, // set current to R + xxL = (XX << 4) + L, // set current to L + Nxx = (ON << 4) + 0xF, // set run to neutral + Axx = (AN << 4) + 0xF, // set run to AN + ExE = (EN << 4) + EN, // set run to EN, set current to EN + NIx = (ON << 4) + 0xF + IX, // set run to N, increment + NxN = (ON << 4) + ON, // set run to N, set current to N + NxR = (ON << 4) + R, // set run to N, set current to R + NxE = (ON << 4) + EN, // set run to N, set current to EN + + AxA = (AN << 4) + AN, // set run to AN, set current to AN + NxL = (ON << 4) + L, // set run to N, set current to L + LxL = (L << 4) + L, // set run to L, set current to L +}; + + +const bidi_action actionWeak[][10] = +{ + // N,.. L, R, AN, EN, AL, NSM, CS,..ES, ET, +{ /*xa*/ xxx, xxx, xxx, xxx, xxA, xxR, xxR, xxN, xxN, xxN, /* arabic letter */ }, +{ /*xr*/ xxx, xxx, xxx, xxx, xxE, xxR, xxR, xxN, xxN, xIx, /* right leter */ }, +{ /*xl*/ xxx, xxx, xxx, xxx, xxL, xxR, xxL, xxN, xxN, xIx, /* left letter */ }, + +{ /*ao*/ xxx, xxx, xxx, xxx, xxA, xxR, xxN, xxN, xxN, xxN, /* arabic lett. foll by ON */ }, +{ /*ro*/ xxx, xxx, xxx, xxx, xxE, xxR, xxN, xxN, xxN, xIx, /* right lett. foll by ON */ }, +{ /*lo*/ xxx, xxx, xxx, xxx, xxL, xxR, xxN, xxN, xxN, xIx, /* left lett. foll by ON */ }, + +{ /*rt*/ Nxx, Nxx, Nxx, Nxx, ExE, NxR, xIx, NxN, NxN, xIx, /* ET following R */ }, +{ /*lt*/ Nxx, Nxx, Nxx, Nxx, LxL, NxR, xIx, NxN, NxN, xIx, /* ET following L */ }, + +{ /*cn*/ xxx, xxx, xxx, xxx, xxA, xxR, xxA, xIx, xxN, xxN, /* EN, AN following AL */ }, +{ /*ra*/ xxx, xxx, xxx, xxx, xxE, xxR, xxA, xIx, xxN, xIx, /* arabic number foll R */ }, +{ /*re*/ xxx, xxx, xxx, xxx, xxE, xxR, xxE, xIx, xIx, xxE, /* european number foll R */ }, +{ /*la*/ xxx, xxx, xxx, xxx, xxL, xxR, xxA, xIx, xxN, xIx, /* arabic number foll L */ }, +{ /*le*/ xxx, xxx, xxx, xxx, xxL, xxR, xxL, xIx, xIx, xxL, /* european number foll L */ }, + +{ /*ac*/ Nxx, Nxx, Nxx, Axx, AxA, NxR, NxN, NxN, NxN, NxN, /* CS following cn */ }, +{ /*rc*/ Nxx, Nxx, Nxx, Axx, NxE, NxR, NxN, NxN, NxN, NIx, /* CS following ra */ }, +{ /*rs*/ Nxx, Nxx, Nxx, Nxx, ExE, NxR, NxN, NxN, NxN, NIx, /* CS,ES following re */ }, +{ /*lc*/ Nxx, Nxx, Nxx, Axx, NxL, NxR, NxN, NxN, NxN, NIx, /* CS following la */ }, +{ /*ls*/ Nxx, Nxx, Nxx, Nxx, LxL, NxR, NxN, NxN, NxN, NIx, /* CS,ES following le */ }, + +{ /*ret*/xxx, xxx, xxx, xxx, xxE, xxR, xxE, xxN, xxN, xxE, /* ET following re */ }, +{ /*let*/xxx, xxx, xxx, xxx, xxL, xxR, xxL, xxN, xxN, xxL, /* ET following le */ }, +}; + +inline uint8 GetDeferredType(bidi_action a) { return (a >> 4) & 0xF; } +inline uint8 GetResolvedType(bidi_action a) { return a & 0xF; } +inline DirCode EmbeddingDirection(int l) { return l & 1 ? R : L; } + +// Neutrals +enum neutral_action +{ + // action to resolve previous input + nL = L, // resolve EN to L + En = 3 << 4, // resolve neutrals run to embedding level direction + Rn = R << 4, // resolve neutrals run to strong right + Ln = L << 4, // resolved neutrals run to strong left + In = (1<<8), // increment count of deferred neutrals + LnL = (1<<4)+L, // set run and EN to L +}; + +// ->prev() here means ->next() +void SetDeferredRunClass(Slot *s, Slot *sRun, int nval) +{ + if (!sRun || s == sRun) return; + for (Slot *p = sRun; p != s; p = p->prev()) + if (p->getBidiClass() == WS) p->setBidiClass(nval | WSflag); + else if (BaseClass(p) != BN) p->setBidiClass(nval | (p->getBidiClass() & WSflag)); +} + +void SetThisDeferredRunClass(Slot *s, Slot *sRun, int nval) +{ + if (!sRun) return; + for (Slot *p = sRun, *e = s->prev(); p != e; p = p->prev()) + if (p->getBidiClass() == WS) p->setBidiClass(nval | WSflag); + else if (BaseClass(p) != BN) p->setBidiClass(nval | (p->getBidiClass() & WSflag)); +} + +void resolveWeak(Slot *start, int sos, int eos) +{ + int state = (sos & 1) ? xr : xl; + int cls; + Slot *s = start; + Slot *sRun = NULL; + Slot *sLast = s; + + for ( ; s; s = s->prev()) + { + sLast = s; + cls = BaseClass(s); + switch (cls) + { + case BN : + if (s == start) start = s->prev(); // skip initial BNs for NSM resolving + continue; + case LRI : + case RLI : + case FSI : + case PDI : + { + Slot *snext = s->prev(); + if (snext && snext->getBidiClass() == NSM) + snext->setBidiClass(ON); + s->setBidiClass(ON | WSflag); + } + break; + + case NSM : + if (s == start) + { + cls = EmbeddingDirection(sos); + s->setBidiClass(cls); + } + break; + } + + bidi_action action = actionWeak[state][bidi_class_map[cls]]; + int clsRun = GetDeferredType(action); + if (clsRun != XX) + { + SetDeferredRunClass(s, sRun, clsRun); + sRun = NULL; + } + int clsNew = GetResolvedType(action); + if (clsNew != XX) + s->setBidiClass(clsNew); + if (!sRun && (IX & action)) + sRun = s; + state = stateWeak[state][bidi_class_map[cls]]; + } + + cls = EmbeddingDirection(eos); + int clsRun = GetDeferredType(actionWeak[state][bidi_class_map[cls]]); + if (clsRun != XX) + SetThisDeferredRunClass(sLast, sRun, clsRun); +} + +void processParens(Slot *s, Segment *seg, uint8 aMirror, int level, BracketPairStack &stack) +{ + uint8 mask = 0; + int8 lastDir = -1; + BracketPair *p; + for ( ; s; s = s->prev()) // walk the sequence + { + uint16 ogid = seg->glyphAttr(s->gid(), aMirror) || s->gid(); + int cls = BaseClass(s); + + switch(cls) + { + case OPP : + stack.orin(mask); + stack.push(ogid, s, lastDir, lastDir != CPP); + mask = 0; + lastDir = OPP; + break; + case CPP : + stack.orin(mask); + p = stack.scan(s->gid()); + if (!p) break; + mask = 0; + stack.close(p, s); + lastDir = CPP; + break; + case L : + lastDir = L; + mask |= 1; + break; + case R : + case AL : + case AN : + case EN : + lastDir = R; + mask |= 2; + } + } + if (stack.size()) + { + for (p = stack.start(); p; p =p->next()) // walk the stack + { + if (p->close() && p->mask()) + { + int dir = (level & 1) + 1; + if (p->mask() & dir) + { } + else if (p->mask() & (1 << (~level & 1))) // if inside has strong other embedding + { + int ldir = p->before(); + if ((p->before() == OPP || p->before() == CPP) && p->prev()) + { + for (BracketPair *q = p->prev(); q; q = q->prev()) + { + ldir = q->open()->getBidiClass(); + if (ldir < 3) break; + ldir = q->before(); + if (ldir < 3) break; + } + if (ldir > 2) ldir = 0; + } + if (ldir > 0 && (ldir - 1) != (level & 1)) // is dir given opp. to level dir (ldir == R or L) + dir = (~level & 1) + 1; + } + p->open()->setBidiClass(dir); + p->close()->setBidiClass(dir); + } + } + stack.clear(); + } +} + +int GetDeferredNeutrals(int action, int level) +{ + action = (action >> 4) & 0xF; + if (action == (En >> 4)) + return EmbeddingDirection(level); + else + return action; +} + +int GetResolvedNeutrals(int action) +{ + return action & 0xF; +} + +// state values +enum neutral_state +{ + // new temporary class + r, // R and characters resolved to R + l, // L and characters resolved to L + rn, // N preceded by right + ln, // N preceded by left + a, // AN preceded by left (the abbrev 'la' is used up above) + na, // N preceeded by a +} ; + +const uint8 neutral_class_map[] = { 0, 1, 2, 0, 4, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +const int actionNeutrals[][5] = +{ +// cls= N, L, R, AN, EN, state = +{ In, 0, 0, 0, 0, }, // r right +{ In, 0, 0, 0, L, }, // l left + +{ In, En, Rn, Rn, Rn, }, // rn N preceded by right +{ In, Ln, En, En, LnL, }, // ln N preceded by left + +{ In, 0, 0, 0, L, }, // a AN preceded by left +{ In, En, Rn, Rn, En, }, // na N preceded by a +} ; + +const int stateNeutrals[][5] = +{ +// cls= N, L, R, AN, EN state = +{ rn, l, r, r, r, }, // r right +{ ln, l, r, a, l, }, // l left + +{ rn, l, r, r, r, }, // rn N preceded by right +{ ln, l, r, a, l, }, // ln N preceded by left + +{ na, l, r, a, l, }, // a AN preceded by left +{ na, l, r, a, l, }, // na N preceded by la +} ; + +void resolveNeutrals(Slot *s, int baseLevel, int sos, int eos) +{ + int state = (sos & 1) ? r : l; + int cls; + Slot *sRun = NULL; + Slot *sLast = s; + int level = baseLevel; + + for ( ; s; s = s->prev()) + { + sLast = s; + cls = BaseClass(s); + switch (cls) + { + case BN : + continue; + case LRI : + case RLI : + case FSI : + s->setBidiClass(BN | WSflag); + continue; + + default : + int action = actionNeutrals[state][neutral_class_map[cls]]; + int clsRun = GetDeferredNeutrals(action, level); + if (clsRun != N) + { + SetDeferredRunClass(s, sRun, clsRun); + sRun = NULL; + } + int clsNew = GetResolvedNeutrals(action); + if (clsNew != N) + s->setBidiClass(clsNew); + if (!sRun && (action & In)) + sRun = s; + state = stateNeutrals[state][neutral_class_map[cls]]; + } + } + cls = EmbeddingDirection(eos); + int clsRun = GetDeferredNeutrals(actionNeutrals[state][neutral_class_map[cls]], level); + if (clsRun != N) + SetThisDeferredRunClass(sLast, sRun, clsRun); +} + +const int addLevel[][4] = +{ + // cls = L, R, AN, EN level = +/* even */ { 0, 1, 2, 2, }, // EVEN +/* odd */ { 1, 0, 1, 1, }, // ODD + +}; + +void resolveImplicit(Slot *s, Segment *seg, uint8 aMirror) +{ + bool rtl = seg->dir() & 1; + int level = rtl; + Slot *slast = 0; + for ( ; s; s = s->next()) + { + int cls = BaseClass(s); + s->prev(slast); // restitch the prev() side of the doubly linked list + slast = s; + if (cls == AN) + cls = AL; // use AL value as the index for AN, no property change + if (cls < 5 && cls > 0) + { + level = s->getBidiLevel(); + level += addLevel[level & 1][cls - 1]; + s->setBidiLevel(level); + } + if (aMirror) + { + int hasChar = seg->glyphAttr(s->gid(), aMirror + 1); + if ( ((level & 1) && (!(seg->dir() & 4) || !hasChar)) + || ((rtl ^ (level & 1)) && (seg->dir() & 4) && hasChar) ) + { + unsigned short g = seg->glyphAttr(s->gid(), aMirror); + if (g) s->setGlyph(seg, g); + } + } + } +} + +void resolveWhitespace(int baseLevel, Slot *s) +{ + for ( ; s; s = s->prev()) + { + int8 cls = s->getBidiClass(); + if (cls == WS || (cls & WSflag)) + s->setBidiLevel(baseLevel); + else if (cls != BN) + break; + } +} + + +/* +Stitch two spans together to make another span (with ends tied together). +If the level is odd then swap the order of the two spans +*/ +inline +Slot * join(int level, Slot * a, Slot * b) +{ + if (!a) return b; + if (level & 1) { Slot * const t = a; a = b; b = t; } + Slot * const t = b->prev(); + a->prev()->next(b); b->prev(a->prev()); // splice middle + t->next(a); a->prev(t); // splice ends + return a; +} + +/* +Given the first slot in a run of slots with the same bidi level, turn the run +into it's own little doubly linked list ring (a span) with the two ends joined together. +If the run is rtl then reverse its direction. +Returns the first slot after the span +*/ +Slot * span(Slot * & cs, const bool rtl) +{ + Slot * r = cs, * re = cs; cs = cs->next(); + if (rtl) + { + Slot * t = r->next(); r->next(r->prev()); r->prev(t); + for (int l = r->getBidiLevel(); cs && (l == cs->getBidiLevel() || cs->getBidiClass() == BN); cs = cs->prev()) + { + re = cs; + t = cs->next(); cs->next(cs->prev()); cs->prev(t); + } + r->next(re); + re->prev(r); + r = re; + } + else + { + for (int l = r->getBidiLevel(); cs && (l == cs->getBidiLevel() || cs->getBidiClass() == BN); cs = cs->next()) + re = cs; + r->prev(re); + re->next(r); + } + if (cs) cs->prev(0); + return r; +} + +inline int getlevel(const Slot *cs, const int level) +{ + while (cs && cs->getBidiClass() == BN) + { cs = cs->next(); } + if (cs) + return cs->getBidiLevel(); + else + return level; +} + +Slot *resolveOrder(Slot * & cs, const bool reordered, const int level) +{ + Slot * r = 0; + int ls; + while (cs && level <= (ls = getlevel(cs, level) - reordered)) + { + r = join(level, r, level < ls + ? resolveOrder(/* updates */cs, reordered, level+1) // find span of heighest level + : span(/* updates */cs, level & 1)); + } + return r; +} |