gfx/graphite2/src/Bidi.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* GRAPHITE2 LICENSING
michael@0 2
michael@0 3 Copyright 2011, SIL International
michael@0 4 All rights reserved.
michael@0 5
michael@0 6 This library is free software; you can redistribute it and/or modify
michael@0 7 it under the terms of the GNU Lesser General Public License as published
michael@0 8 by the Free Software Foundation; either version 2.1 of License, or
michael@0 9 (at your option) any later version.
michael@0 10
michael@0 11 This program is distributed in the hope that it will be useful,
michael@0 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
michael@0 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
michael@0 14 Lesser General Public License for more details.
michael@0 15
michael@0 16 You should also have received a copy of the GNU Lesser General Public
michael@0 17 License along with this library in the file named "LICENSE".
michael@0 18 If not, write to the Free Software Foundation, 51 Franklin Street,
michael@0 19 suite 500, Boston, MA 02110-1335, USA or visit their web page on the
michael@0 20 internet at http://www.fsf.org/licenses/lgpl.html.
michael@0 21
michael@0 22 Alternatively, the contents of this file may be used under the terms of the
michael@0 23 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
michael@0 24 License, as published by the Free Software Foundation, either version 2
michael@0 25 of the License or (at your option) any later version.
michael@0 26 */
michael@0 27 #include "inc/Main.h"
michael@0 28 #include "inc/Slot.h"
michael@0 29 #include "inc/Segment.h"
michael@0 30 #include "inc/Bidi.h"
michael@0 31
michael@0 32 using namespace graphite2;
michael@0 33
michael@0 34 enum DirCode { // Hungarian: dirc
michael@0 35 Unk = -1,
michael@0 36 N = 0, // other neutrals (default) - ON
michael@0 37 L = 1, // left-to-right, strong - L
michael@0 38 R = 2, // right-to-left, strong - R
michael@0 39 AL = 3, // Arabic letter, right-to-left, strong, AR
michael@0 40 EN = 4, // European number, left-to-right, weak - EN
michael@0 41 EUS = 5, // European separator, left-to-right, weak - ES
michael@0 42 ET = 6, // European number terminator, left-to-right, weak - ET
michael@0 43 AN = 7, // Arabic number, left-to-right, weak - AN
michael@0 44 CUS = 8, // Common number separator, left-to-right, weak - CS
michael@0 45 WS = 9, // white space, neutral - WS
michael@0 46 BN = 10, // boundary neutral - BN
michael@0 47
michael@0 48 LRO = 11, // LTR override
michael@0 49 RLO = 12, // RTL override
michael@0 50 LRE = 13, // LTR embedding
michael@0 51 RLE = 14, // RTL embedding
michael@0 52 PDF = 15, // pop directional format
michael@0 53 NSM = 16, // non-space mark
michael@0 54 LRI = 17, // LRI isolate
michael@0 55 RLI = 18, // RLI isolate
michael@0 56 FSI = 19, // FSI isolate
michael@0 57 PDI = 20, // pop isolate
michael@0 58 OPP = 21, // opening paired parenthesis
michael@0 59 CPP = 22, // closing paired parenthesis
michael@0 60
michael@0 61 ON = N
michael@0 62 };
michael@0 63
michael@0 64 enum DirMask {
michael@0 65 WSflag = (1 << 7), // keep track of WS for eos handling
michael@0 66 WSMask = ~(1 << 7)
michael@0 67 };
michael@0 68
michael@0 69 inline uint8 BaseClass(Slot *s) { return s->getBidiClass() & WSMask; }
michael@0 70
michael@0 71 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 };
michael@0 72 // Algorithms based on Unicode reference standard code. Thanks Asmus Freitag.
michael@0 73
michael@0 74 void resolveWeak(Slot *start, int sos, int eos);
michael@0 75 void resolveNeutrals(Slot *s, int baseLevel, int sos, int eos);
michael@0 76 void processParens(Slot *s, Segment *seg, uint8 aMirror, int level, BracketPairStack &stack);
michael@0 77
michael@0 78 inline int calc_base_level(Slot *s)
michael@0 79 {
michael@0 80 int count = 0;
michael@0 81 for ( ; s; s = s->next())
michael@0 82 {
michael@0 83 int cls = s->getBidiClass();
michael@0 84 if (count)
michael@0 85 {
michael@0 86 switch(cls)
michael@0 87 {
michael@0 88 case LRI :
michael@0 89 case RLI :
michael@0 90 case FSI :
michael@0 91 ++count;
michael@0 92 break;
michael@0 93 case PDI :
michael@0 94 --count;
michael@0 95 }
michael@0 96 }
michael@0 97 else
michael@0 98 {
michael@0 99 switch(cls)
michael@0 100 {
michael@0 101 case L :
michael@0 102 return 0;
michael@0 103 case R :
michael@0 104 case AL :
michael@0 105 return 1;
michael@0 106 case LRI :
michael@0 107 case RLI :
michael@0 108 case FSI :
michael@0 109 ++count;
michael@0 110 }
michael@0 111 }
michael@0 112 }
michael@0 113 return 0;
michael@0 114 }
michael@0 115
michael@0 116 // inline or not?
michael@0 117 void do_resolves(Slot *start, int level, int sos, int eos, int &bmask, Segment *seg, uint8 aMirror, BracketPairStack &stack)
michael@0 118 {
michael@0 119 if (bmask & 0x1F1178)
michael@0 120 resolveWeak(start, sos, eos);
michael@0 121 if (bmask & 0x200000)
michael@0 122 processParens(start, seg, aMirror, level, stack);
michael@0 123 if (bmask & 0x7E0361)
michael@0 124 resolveNeutrals(start, level, sos, eos);
michael@0 125 bmask = 0;
michael@0 126 }
michael@0 127
michael@0 128 enum maxs
michael@0 129 {
michael@0 130 MAX_LEVEL = 125,
michael@0 131 };
michael@0 132
michael@0 133 // returns where we are up to in processing
michael@0 134 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)
michael@0 135 {
michael@0 136 int bmask = 0;
michael@0 137 Slot *s = start;
michael@0 138 Slot *slast = start;
michael@0 139 Slot *scurr = 0;
michael@0 140 Slot *stemp;
michael@0 141 int lnextLevel = nextLevel;
michael@0 142 int newLevel;
michael@0 143 int empty = 1;
michael@0 144 for ( ; s; s = s ? s->next() : s)
michael@0 145 {
michael@0 146 int cls = s->getBidiClass();
michael@0 147 bmask |= (1 << cls);
michael@0 148 s->setBidiLevel(level);
michael@0 149 // we keep s->prev() pointing backwards for PDI repeating
michael@0 150
michael@0 151 switch (cls)
michael@0 152 {
michael@0 153 case BN :
michael@0 154 if (slast == s) slast = s->next(); // ignore if at front of text
michael@0 155 continue;
michael@0 156 case LRE :
michael@0 157 case LRO :
michael@0 158 case RLE :
michael@0 159 case RLO :
michael@0 160 switch (cls)
michael@0 161 {
michael@0 162 case LRE :
michael@0 163 case LRO :
michael@0 164 newLevel = level + (level & 1 ? 1 : 2);
michael@0 165 break;
michael@0 166 case RLE :
michael@0 167 case RLO :
michael@0 168 newLevel = level + (level & 1 ? 2 : 1);
michael@0 169 break;
michael@0 170 }
michael@0 171 s->setBidiClass(BN);
michael@0 172 if (isolerr || newLevel > MAX_LEVEL || embederr)
michael@0 173 {
michael@0 174 if (!isolerr) ++embederr;
michael@0 175 break;
michael@0 176 }
michael@0 177 stemp = scurr;
michael@0 178 if (scurr)
michael@0 179 scurr->prev(0); // don't include control in string
michael@0 180 lnextLevel = newLevel;
michael@0 181 scurr = s;
michael@0 182 s->setBidiLevel(newLevel); // to make it vanish
michael@0 183 // recurse for the new subsequence. A sequence only contains text at the same level
michael@0 184 s = process_bidi(s->next(), newLevel, level, lnextLevel, cls < LRE, 0, cisol, isolerr, embederr, 0, seg, aMirror, bstack);
michael@0 185 // s points at PDF or end of sequence
michael@0 186 // try to keep extending the run and not process it until we have to
michael@0 187 if (lnextLevel != level || !s) // if the subsequence really had something in it, or we are at the end of the run
michael@0 188 {
michael@0 189 if (slast != scurr) // process the run now, don't try to extend it
michael@0 190 {
michael@0 191 // process text preceeding embedding
michael@0 192 do_resolves(slast, level, (prelevel > level ? prelevel : level) & 1, lnextLevel & 1, bmask, seg, aMirror, bstack);
michael@0 193 empty = 0;
michael@0 194 nextLevel = level;
michael@0 195 }
michael@0 196 else if (lnextLevel != level) // the subsequence had something
michael@0 197 {
michael@0 198 empty = 0; // so we aren't empty either
michael@0 199 nextLevel = lnextLevel; // but since we really are empty, pass back our level from the subsequence
michael@0 200 }
michael@0 201 if (s) // if still more to process
michael@0 202 {
michael@0 203 prelevel = lnextLevel; // future text starts out with sos of the higher subsequence
michael@0 204 lnextLevel = level; // and eos is our level
michael@0 205 }
michael@0 206 slast = s ? s->next() : s;
michael@0 207 }
michael@0 208 else if (stemp)
michael@0 209 stemp->prev(s);
michael@0 210 break;
michael@0 211
michael@0 212 case PDF :
michael@0 213 s->setBidiClass(BN);
michael@0 214 s->prev(0); // unstitch us since we skip final stitching code when we return
michael@0 215 if (isol || isolerr || init) // boundary error conditions
michael@0 216 break;
michael@0 217 if (embederr)
michael@0 218 {
michael@0 219 --embederr;
michael@0 220 break;
michael@0 221 }
michael@0 222 if (slast != s)
michael@0 223 {
michael@0 224 scurr->prev(0); // if slast, then scurr. Terminate before here
michael@0 225 do_resolves(slast, level, level & 1, level & 1, bmask, seg, aMirror, bstack);
michael@0 226 empty = 0;
michael@0 227 }
michael@0 228 if (empty)
michael@0 229 {
michael@0 230 nextLevel = prelevel; // no contents? set our level to that of parent
michael@0 231 s->setBidiLevel(prelevel);
michael@0 232 }
michael@0 233 return s;
michael@0 234
michael@0 235 case FSI :
michael@0 236 case LRI :
michael@0 237 case RLI :
michael@0 238 switch (cls)
michael@0 239 {
michael@0 240 case FSI :
michael@0 241 if (calc_base_level(s->next()))
michael@0 242 newLevel = level + (level & 1 ? 2 : 1);
michael@0 243 else
michael@0 244 newLevel = level + (level & 1 ? 1 : 2);
michael@0 245 break;
michael@0 246 case LRI :
michael@0 247 newLevel = level + (level & 1 ? 1 : 2);
michael@0 248 break;
michael@0 249 case RLI :
michael@0 250 newLevel = level + (level & 1 ? 2 : 1);
michael@0 251 break;
michael@0 252 }
michael@0 253 if (newLevel > MAX_LEVEL || isolerr)
michael@0 254 {
michael@0 255 ++isolerr;
michael@0 256 s->setBidiClass(ON | WSflag);
michael@0 257 break;
michael@0 258 }
michael@0 259 ++cisol;
michael@0 260 if (scurr) scurr->prev(s);
michael@0 261 scurr = s; // include FSI
michael@0 262 lnextLevel = newLevel;
michael@0 263 // recurse for the new sub sequence
michael@0 264 s = process_bidi(s->next(), newLevel, newLevel, lnextLevel, 0, 1, cisol, isolerr, embederr, 0, seg, aMirror, bstack);
michael@0 265 // s points at PDI
michael@0 266 if (s)
michael@0 267 {
michael@0 268 bmask |= 1 << BaseClass(s); // include the PDI in the mask
michael@0 269 s->setBidiLevel(level); // reset its level to our level
michael@0 270 }
michael@0 271 lnextLevel = level;
michael@0 272 break;
michael@0 273
michael@0 274 case PDI :
michael@0 275 if (isolerr)
michael@0 276 {
michael@0 277 --isolerr;
michael@0 278 s->setBidiClass(ON | WSflag);
michael@0 279 break;
michael@0 280 }
michael@0 281 if (init || !cisol)
michael@0 282 {
michael@0 283 s->setBidiClass(ON | WSflag);
michael@0 284 break;
michael@0 285 }
michael@0 286 embederr = 0;
michael@0 287 if (!isol) // we are in an embedded subsequence, we have to return through all those
michael@0 288 {
michael@0 289 if (empty) // if empty, reset the level to tell embedded parent
michael@0 290 nextLevel = prelevel;
michael@0 291 return s->prev(); // keep working up the stack pointing at this PDI until we get to an isolate entry
michael@0 292 }
michael@0 293 else // we are terminating an isolate sequence
michael@0 294 {
michael@0 295 if (slast != s) // process any remaining content in this subseqence
michael@0 296 {
michael@0 297 scurr->prev(0);
michael@0 298 do_resolves(slast, level, prelevel & 1, level & 1, bmask, seg, aMirror, bstack);
michael@0 299 }
michael@0 300 --cisol; // pop the isol sequence from the stack
michael@0 301 return s;
michael@0 302 }
michael@0 303
michael@0 304 default :
michael@0 305 if (dirover)
michael@0 306 s->setBidiClass((level & 1 ? R : L) | (WSflag * (cls == WS)));
michael@0 307 }
michael@0 308 if (s) s->prev(0); // unstitch us
michael@0 309 if (scurr) // stitch in text for processing
michael@0 310 scurr->prev(s);
michael@0 311 scurr = s; // add us to text to process
michael@0 312 }
michael@0 313 if (slast != s)
michael@0 314 {
michael@0 315 do_resolves(slast, level, (level > prelevel ? level : prelevel) & 1, lnextLevel & 1, bmask, seg, aMirror, bstack);
michael@0 316 empty = 0;
michael@0 317 }
michael@0 318 if (empty || isol)
michael@0 319 nextLevel = prelevel;
michael@0 320 return s;
michael@0 321 }
michael@0 322
michael@0 323 // === RESOLVE WEAK TYPES ================================================
michael@0 324
michael@0 325 enum bidi_state // possible states
michael@0 326 {
michael@0 327 xa, // arabic letter
michael@0 328 xr, // right leter
michael@0 329 xl, // left letter
michael@0 330
michael@0 331 ao, // arabic lett. foll by ON
michael@0 332 ro, // right lett. foll by ON
michael@0 333 lo, // left lett. foll by ON
michael@0 334
michael@0 335 rt, // ET following R
michael@0 336 lt, // ET following L
michael@0 337
michael@0 338 cn, // EN, AN following AL
michael@0 339 ra, // arabic number foll R
michael@0 340 re, // european number foll R
michael@0 341 la, // arabic number foll L
michael@0 342 le, // european number foll L
michael@0 343
michael@0 344 ac, // CS following cn
michael@0 345 rc, // CS following ra
michael@0 346 rs, // CS,ES following re
michael@0 347 lc, // CS following la
michael@0 348 ls, // CS,ES following le
michael@0 349
michael@0 350 ret, // ET following re
michael@0 351 let, // ET following le
michael@0 352 } ;
michael@0 353
michael@0 354 const bidi_state stateWeak[][10] =
michael@0 355 {
michael@0 356 // N, L, R, AN, EN, AL,NSM, CS, ES, ET,
michael@0 357 { /*xa*/ ao, xl, xr, cn, cn, xa, xa, ao, ao, ao, /* arabic letter */ },
michael@0 358 { /*xr*/ ro, xl, xr, ra, re, xa, xr, ro, ro, rt, /* right letter */ },
michael@0 359 { /*xl*/ lo, xl, xr, la, le, xa, xl, lo, lo, lt, /* left letter */ },
michael@0 360
michael@0 361 { /*ao*/ ao, xl, xr, cn, cn, xa, ao, ao, ao, ao, /* arabic lett. foll by ON*/ },
michael@0 362 { /*ro*/ ro, xl, xr, ra, re, xa, ro, ro, ro, rt, /* right lett. foll by ON */ },
michael@0 363 { /*lo*/ lo, xl, xr, la, le, xa, lo, lo, lo, lt, /* left lett. foll by ON */ },
michael@0 364
michael@0 365 { /*rt*/ ro, xl, xr, ra, re, xa, rt, ro, ro, rt, /* ET following R */ },
michael@0 366 { /*lt*/ lo, xl, xr, la, le, xa, lt, lo, lo, lt, /* ET following L */ },
michael@0 367
michael@0 368 { /*cn*/ ao, xl, xr, cn, cn, xa, cn, ac, ao, ao, /* EN, AN following AL */ },
michael@0 369 { /*ra*/ ro, xl, xr, ra, re, xa, ra, rc, ro, rt, /* arabic number foll R */ },
michael@0 370 { /*re*/ ro, xl, xr, ra, re, xa, re, rs, rs,ret, /* european number foll R */ },
michael@0 371 { /*la*/ lo, xl, xr, la, le, xa, la, lc, lo, lt, /* arabic number foll L */ },
michael@0 372 { /*le*/ lo, xl, xr, la, le, xa, le, ls, ls,let, /* european number foll L */ },
michael@0 373
michael@0 374 { /*ac*/ ao, xl, xr, cn, cn, xa, ao, ao, ao, ao, /* CS following cn */ },
michael@0 375 { /*rc*/ ro, xl, xr, ra, re, xa, ro, ro, ro, rt, /* CS following ra */ },
michael@0 376 { /*rs*/ ro, xl, xr, ra, re, xa, ro, ro, ro, rt, /* CS,ES following re */ },
michael@0 377 { /*lc*/ lo, xl, xr, la, le, xa, lo, lo, lo, lt, /* CS following la */ },
michael@0 378 { /*ls*/ lo, xl, xr, la, le, xa, lo, lo, lo, lt, /* CS,ES following le */ },
michael@0 379
michael@0 380 { /*ret*/ ro, xl, xr, ra, re, xa,ret, ro, ro,ret, /* ET following re */ },
michael@0 381 { /*let*/ lo, xl, xr, la, le, xa,let, lo, lo,let, /* ET following le */ },
michael@0 382
michael@0 383
michael@0 384 };
michael@0 385
michael@0 386 enum bidi_action // possible actions
michael@0 387 {
michael@0 388 // primitives
michael@0 389 IX = 0x100, // increment
michael@0 390 XX = 0xF, // no-op
michael@0 391
michael@0 392 // actions
michael@0 393 xxx = (XX << 4) + XX, // no-op
michael@0 394 xIx = IX + xxx, // increment run
michael@0 395 xxN = (XX << 4) + ON, // set current to N
michael@0 396 xxE = (XX << 4) + EN, // set current to EN
michael@0 397 xxA = (XX << 4) + AN, // set current to AN
michael@0 398 xxR = (XX << 4) + R, // set current to R
michael@0 399 xxL = (XX << 4) + L, // set current to L
michael@0 400 Nxx = (ON << 4) + 0xF, // set run to neutral
michael@0 401 Axx = (AN << 4) + 0xF, // set run to AN
michael@0 402 ExE = (EN << 4) + EN, // set run to EN, set current to EN
michael@0 403 NIx = (ON << 4) + 0xF + IX, // set run to N, increment
michael@0 404 NxN = (ON << 4) + ON, // set run to N, set current to N
michael@0 405 NxR = (ON << 4) + R, // set run to N, set current to R
michael@0 406 NxE = (ON << 4) + EN, // set run to N, set current to EN
michael@0 407
michael@0 408 AxA = (AN << 4) + AN, // set run to AN, set current to AN
michael@0 409 NxL = (ON << 4) + L, // set run to N, set current to L
michael@0 410 LxL = (L << 4) + L, // set run to L, set current to L
michael@0 411 };
michael@0 412
michael@0 413
michael@0 414 const bidi_action actionWeak[][10] =
michael@0 415 {
michael@0 416 // N,.. L, R, AN, EN, AL, NSM, CS,..ES, ET,
michael@0 417 { /*xa*/ xxx, xxx, xxx, xxx, xxA, xxR, xxR, xxN, xxN, xxN, /* arabic letter */ },
michael@0 418 { /*xr*/ xxx, xxx, xxx, xxx, xxE, xxR, xxR, xxN, xxN, xIx, /* right leter */ },
michael@0 419 { /*xl*/ xxx, xxx, xxx, xxx, xxL, xxR, xxL, xxN, xxN, xIx, /* left letter */ },
michael@0 420
michael@0 421 { /*ao*/ xxx, xxx, xxx, xxx, xxA, xxR, xxN, xxN, xxN, xxN, /* arabic lett. foll by ON */ },
michael@0 422 { /*ro*/ xxx, xxx, xxx, xxx, xxE, xxR, xxN, xxN, xxN, xIx, /* right lett. foll by ON */ },
michael@0 423 { /*lo*/ xxx, xxx, xxx, xxx, xxL, xxR, xxN, xxN, xxN, xIx, /* left lett. foll by ON */ },
michael@0 424
michael@0 425 { /*rt*/ Nxx, Nxx, Nxx, Nxx, ExE, NxR, xIx, NxN, NxN, xIx, /* ET following R */ },
michael@0 426 { /*lt*/ Nxx, Nxx, Nxx, Nxx, LxL, NxR, xIx, NxN, NxN, xIx, /* ET following L */ },
michael@0 427
michael@0 428 { /*cn*/ xxx, xxx, xxx, xxx, xxA, xxR, xxA, xIx, xxN, xxN, /* EN, AN following AL */ },
michael@0 429 { /*ra*/ xxx, xxx, xxx, xxx, xxE, xxR, xxA, xIx, xxN, xIx, /* arabic number foll R */ },
michael@0 430 { /*re*/ xxx, xxx, xxx, xxx, xxE, xxR, xxE, xIx, xIx, xxE, /* european number foll R */ },
michael@0 431 { /*la*/ xxx, xxx, xxx, xxx, xxL, xxR, xxA, xIx, xxN, xIx, /* arabic number foll L */ },
michael@0 432 { /*le*/ xxx, xxx, xxx, xxx, xxL, xxR, xxL, xIx, xIx, xxL, /* european number foll L */ },
michael@0 433
michael@0 434 { /*ac*/ Nxx, Nxx, Nxx, Axx, AxA, NxR, NxN, NxN, NxN, NxN, /* CS following cn */ },
michael@0 435 { /*rc*/ Nxx, Nxx, Nxx, Axx, NxE, NxR, NxN, NxN, NxN, NIx, /* CS following ra */ },
michael@0 436 { /*rs*/ Nxx, Nxx, Nxx, Nxx, ExE, NxR, NxN, NxN, NxN, NIx, /* CS,ES following re */ },
michael@0 437 { /*lc*/ Nxx, Nxx, Nxx, Axx, NxL, NxR, NxN, NxN, NxN, NIx, /* CS following la */ },
michael@0 438 { /*ls*/ Nxx, Nxx, Nxx, Nxx, LxL, NxR, NxN, NxN, NxN, NIx, /* CS,ES following le */ },
michael@0 439
michael@0 440 { /*ret*/xxx, xxx, xxx, xxx, xxE, xxR, xxE, xxN, xxN, xxE, /* ET following re */ },
michael@0 441 { /*let*/xxx, xxx, xxx, xxx, xxL, xxR, xxL, xxN, xxN, xxL, /* ET following le */ },
michael@0 442 };
michael@0 443
michael@0 444 inline uint8 GetDeferredType(bidi_action a) { return (a >> 4) & 0xF; }
michael@0 445 inline uint8 GetResolvedType(bidi_action a) { return a & 0xF; }
michael@0 446 inline DirCode EmbeddingDirection(int l) { return l & 1 ? R : L; }
michael@0 447
michael@0 448 // Neutrals
michael@0 449 enum neutral_action
michael@0 450 {
michael@0 451 // action to resolve previous input
michael@0 452 nL = L, // resolve EN to L
michael@0 453 En = 3 << 4, // resolve neutrals run to embedding level direction
michael@0 454 Rn = R << 4, // resolve neutrals run to strong right
michael@0 455 Ln = L << 4, // resolved neutrals run to strong left
michael@0 456 In = (1<<8), // increment count of deferred neutrals
michael@0 457 LnL = (1<<4)+L, // set run and EN to L
michael@0 458 };
michael@0 459
michael@0 460 // ->prev() here means ->next()
michael@0 461 void SetDeferredRunClass(Slot *s, Slot *sRun, int nval)
michael@0 462 {
michael@0 463 if (!sRun || s == sRun) return;
michael@0 464 for (Slot *p = sRun; p != s; p = p->prev())
michael@0 465 if (p->getBidiClass() == WS) p->setBidiClass(nval | WSflag);
michael@0 466 else if (BaseClass(p) != BN) p->setBidiClass(nval | (p->getBidiClass() & WSflag));
michael@0 467 }
michael@0 468
michael@0 469 void SetThisDeferredRunClass(Slot *s, Slot *sRun, int nval)
michael@0 470 {
michael@0 471 if (!sRun) return;
michael@0 472 for (Slot *p = sRun, *e = s->prev(); p != e; p = p->prev())
michael@0 473 if (p->getBidiClass() == WS) p->setBidiClass(nval | WSflag);
michael@0 474 else if (BaseClass(p) != BN) p->setBidiClass(nval | (p->getBidiClass() & WSflag));
michael@0 475 }
michael@0 476
michael@0 477 void resolveWeak(Slot *start, int sos, int eos)
michael@0 478 {
michael@0 479 int state = (sos & 1) ? xr : xl;
michael@0 480 int cls;
michael@0 481 Slot *s = start;
michael@0 482 Slot *sRun = NULL;
michael@0 483 Slot *sLast = s;
michael@0 484
michael@0 485 for ( ; s; s = s->prev())
michael@0 486 {
michael@0 487 sLast = s;
michael@0 488 cls = BaseClass(s);
michael@0 489 switch (cls)
michael@0 490 {
michael@0 491 case BN :
michael@0 492 if (s == start) start = s->prev(); // skip initial BNs for NSM resolving
michael@0 493 continue;
michael@0 494 case LRI :
michael@0 495 case RLI :
michael@0 496 case FSI :
michael@0 497 case PDI :
michael@0 498 {
michael@0 499 Slot *snext = s->prev();
michael@0 500 if (snext && snext->getBidiClass() == NSM)
michael@0 501 snext->setBidiClass(ON);
michael@0 502 s->setBidiClass(ON | WSflag);
michael@0 503 }
michael@0 504 break;
michael@0 505
michael@0 506 case NSM :
michael@0 507 if (s == start)
michael@0 508 {
michael@0 509 cls = EmbeddingDirection(sos);
michael@0 510 s->setBidiClass(cls);
michael@0 511 }
michael@0 512 break;
michael@0 513 }
michael@0 514
michael@0 515 bidi_action action = actionWeak[state][bidi_class_map[cls]];
michael@0 516 int clsRun = GetDeferredType(action);
michael@0 517 if (clsRun != XX)
michael@0 518 {
michael@0 519 SetDeferredRunClass(s, sRun, clsRun);
michael@0 520 sRun = NULL;
michael@0 521 }
michael@0 522 int clsNew = GetResolvedType(action);
michael@0 523 if (clsNew != XX)
michael@0 524 s->setBidiClass(clsNew);
michael@0 525 if (!sRun && (IX & action))
michael@0 526 sRun = s;
michael@0 527 state = stateWeak[state][bidi_class_map[cls]];
michael@0 528 }
michael@0 529
michael@0 530 cls = EmbeddingDirection(eos);
michael@0 531 int clsRun = GetDeferredType(actionWeak[state][bidi_class_map[cls]]);
michael@0 532 if (clsRun != XX)
michael@0 533 SetThisDeferredRunClass(sLast, sRun, clsRun);
michael@0 534 }
michael@0 535
michael@0 536 void processParens(Slot *s, Segment *seg, uint8 aMirror, int level, BracketPairStack &stack)
michael@0 537 {
michael@0 538 uint8 mask = 0;
michael@0 539 int8 lastDir = -1;
michael@0 540 BracketPair *p;
michael@0 541 for ( ; s; s = s->prev()) // walk the sequence
michael@0 542 {
michael@0 543 uint16 ogid = seg->glyphAttr(s->gid(), aMirror);
michael@0 544 int cls = BaseClass(s);
michael@0 545
michael@0 546 switch(cls)
michael@0 547 {
michael@0 548 case OPP :
michael@0 549 stack.orin(mask);
michael@0 550 stack.push(ogid, s, lastDir, lastDir != CPP);
michael@0 551 mask = 0;
michael@0 552 lastDir = OPP;
michael@0 553 break;
michael@0 554 case CPP :
michael@0 555 stack.orin(mask);
michael@0 556 p = stack.scan(s->gid());
michael@0 557 if (!p) break;
michael@0 558 mask = 0;
michael@0 559 stack.close(p, s);
michael@0 560 lastDir = CPP;
michael@0 561 break;
michael@0 562 case L :
michael@0 563 lastDir = L;
michael@0 564 mask |= 1;
michael@0 565 break;
michael@0 566 case R :
michael@0 567 case AL :
michael@0 568 case AN :
michael@0 569 case EN :
michael@0 570 lastDir = R;
michael@0 571 mask |= 2;
michael@0 572 }
michael@0 573 }
michael@0 574 for (p = stack.start(); p; p =p->next()) // walk the stack
michael@0 575 {
michael@0 576 if (p->close() && p->mask())
michael@0 577 {
michael@0 578 int dir = (level & 1) + 1;
michael@0 579 if (p->mask() & dir)
michael@0 580 { }
michael@0 581 else if (p->mask() & (1 << (~level & 1))) // if inside has strong other embedding
michael@0 582 {
michael@0 583 int ldir = p->before();
michael@0 584 if ((p->before() == OPP || p->before() == CPP) && p->prev())
michael@0 585 {
michael@0 586 for (BracketPair *q = p->prev(); q; q = q->prev())
michael@0 587 {
michael@0 588 ldir = q->open()->getBidiClass();
michael@0 589 if (ldir < 3) break;
michael@0 590 ldir = q->before();
michael@0 591 if (ldir < 3) break;
michael@0 592 }
michael@0 593 if (ldir > 2) ldir = 0;
michael@0 594 }
michael@0 595 if (ldir > 0 && (ldir - 1) != (level & 1)) // is dir given opp. to level dir (ldir == R or L)
michael@0 596 dir = (~level & 1) + 1;
michael@0 597 }
michael@0 598 p->open()->setBidiClass(dir);
michael@0 599 p->close()->setBidiClass(dir);
michael@0 600 }
michael@0 601 }
michael@0 602 stack.clear();
michael@0 603 }
michael@0 604
michael@0 605 int GetDeferredNeutrals(int action, int level)
michael@0 606 {
michael@0 607 action = (action >> 4) & 0xF;
michael@0 608 if (action == (En >> 4))
michael@0 609 return EmbeddingDirection(level);
michael@0 610 else
michael@0 611 return action;
michael@0 612 }
michael@0 613
michael@0 614 int GetResolvedNeutrals(int action)
michael@0 615 {
michael@0 616 return action & 0xF;
michael@0 617 }
michael@0 618
michael@0 619 // state values
michael@0 620 enum neutral_state
michael@0 621 {
michael@0 622 // new temporary class
michael@0 623 r, // R and characters resolved to R
michael@0 624 l, // L and characters resolved to L
michael@0 625 rn, // N preceded by right
michael@0 626 ln, // N preceded by left
michael@0 627 a, // AN preceded by left (the abbrev 'la' is used up above)
michael@0 628 na, // N preceeded by a
michael@0 629 } ;
michael@0 630
michael@0 631 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 };
michael@0 632
michael@0 633 const int actionNeutrals[][5] =
michael@0 634 {
michael@0 635 // cls= N, L, R, AN, EN, state =
michael@0 636 { In, 0, 0, 0, 0, }, // r right
michael@0 637 { In, 0, 0, 0, L, }, // l left
michael@0 638
michael@0 639 { In, En, Rn, Rn, Rn, }, // rn N preceded by right
michael@0 640 { In, Ln, En, En, LnL, }, // ln N preceded by left
michael@0 641
michael@0 642 { In, 0, 0, 0, L, }, // a AN preceded by left
michael@0 643 { In, En, Rn, Rn, En, }, // na N preceded by a
michael@0 644 } ;
michael@0 645
michael@0 646 const int stateNeutrals[][5] =
michael@0 647 {
michael@0 648 // cls= N, L, R, AN, EN state =
michael@0 649 { rn, l, r, r, r, }, // r right
michael@0 650 { ln, l, r, a, l, }, // l left
michael@0 651
michael@0 652 { rn, l, r, r, r, }, // rn N preceded by right
michael@0 653 { ln, l, r, a, l, }, // ln N preceded by left
michael@0 654
michael@0 655 { na, l, r, a, l, }, // a AN preceded by left
michael@0 656 { na, l, r, a, l, }, // na N preceded by la
michael@0 657 } ;
michael@0 658
michael@0 659 void resolveNeutrals(Slot *s, int baseLevel, int sos, int eos)
michael@0 660 {
michael@0 661 int state = (sos & 1) ? r : l;
michael@0 662 int cls;
michael@0 663 Slot *sRun = NULL;
michael@0 664 Slot *sLast = s;
michael@0 665 int level = baseLevel;
michael@0 666
michael@0 667 for ( ; s; s = s->prev())
michael@0 668 {
michael@0 669 sLast = s;
michael@0 670 cls = BaseClass(s);
michael@0 671 switch (cls)
michael@0 672 {
michael@0 673 case BN :
michael@0 674 continue;
michael@0 675 case LRI :
michael@0 676 case RLI :
michael@0 677 case FSI :
michael@0 678 s->setBidiClass(BN | WSflag);
michael@0 679 continue;
michael@0 680
michael@0 681 default :
michael@0 682 int action = actionNeutrals[state][neutral_class_map[cls]];
michael@0 683 int clsRun = GetDeferredNeutrals(action, level);
michael@0 684 if (clsRun != N)
michael@0 685 {
michael@0 686 SetDeferredRunClass(s, sRun, clsRun);
michael@0 687 sRun = NULL;
michael@0 688 }
michael@0 689 int clsNew = GetResolvedNeutrals(action);
michael@0 690 if (clsNew != N)
michael@0 691 s->setBidiClass(clsNew);
michael@0 692 if (!sRun && (action & In))
michael@0 693 sRun = s;
michael@0 694 state = stateNeutrals[state][neutral_class_map[cls]];
michael@0 695 }
michael@0 696 }
michael@0 697 cls = EmbeddingDirection(eos);
michael@0 698 int clsRun = GetDeferredNeutrals(actionNeutrals[state][neutral_class_map[cls]], level);
michael@0 699 if (clsRun != N)
michael@0 700 SetThisDeferredRunClass(sLast, sRun, clsRun);
michael@0 701 }
michael@0 702
michael@0 703 const int addLevel[][4] =
michael@0 704 {
michael@0 705 // cls = L, R, AN, EN level =
michael@0 706 /* even */ { 0, 1, 2, 2, }, // EVEN
michael@0 707 /* odd */ { 1, 0, 1, 1, }, // ODD
michael@0 708
michael@0 709 };
michael@0 710
michael@0 711 void resolveImplicit(Slot *s, Segment *seg, uint8 aMirror)
michael@0 712 {
michael@0 713 bool rtl = seg->dir() & 1;
michael@0 714 int level = rtl;
michael@0 715 Slot *slast = 0;
michael@0 716 for ( ; s; s = s->next())
michael@0 717 {
michael@0 718 int cls = BaseClass(s);
michael@0 719 s->prev(slast); // restitch the prev() side of the doubly linked list
michael@0 720 slast = s;
michael@0 721 if (cls == AN)
michael@0 722 cls = AL; // use AL value as the index for AN, no property change
michael@0 723 if (cls < 5 && cls > 0)
michael@0 724 {
michael@0 725 level = s->getBidiLevel();
michael@0 726 level += addLevel[level & 1][cls - 1];
michael@0 727 s->setBidiLevel(level);
michael@0 728 }
michael@0 729 if (aMirror)
michael@0 730 {
michael@0 731 int hasChar = seg->glyphAttr(s->gid(), aMirror + 1);
michael@0 732 if ( ((level & 1) && (!(seg->dir() & 4) || !hasChar))
michael@0 733 || ((rtl ^ (level & 1)) && (seg->dir() & 4) && hasChar) )
michael@0 734 {
michael@0 735 unsigned short g = seg->glyphAttr(s->gid(), aMirror);
michael@0 736 if (g) s->setGlyph(seg, g);
michael@0 737 }
michael@0 738 }
michael@0 739 }
michael@0 740 }
michael@0 741
michael@0 742 void resolveWhitespace(int baseLevel, Slot *s)
michael@0 743 {
michael@0 744 for ( ; s; s = s->prev())
michael@0 745 {
michael@0 746 int8 cls = s->getBidiClass();
michael@0 747 if (cls == WS || cls & WSflag)
michael@0 748 s->setBidiLevel(baseLevel);
michael@0 749 else if (cls != BN)
michael@0 750 break;
michael@0 751 }
michael@0 752 }
michael@0 753
michael@0 754
michael@0 755 /*
michael@0 756 Stitch two spans together to make another span (with ends tied together).
michael@0 757 If the level is odd then swap the order of the two spans
michael@0 758 */
michael@0 759 inline
michael@0 760 Slot * join(int level, Slot * a, Slot * b)
michael@0 761 {
michael@0 762 if (!a) return b;
michael@0 763 if (level & 1) { Slot * const t = a; a = b; b = t; }
michael@0 764 Slot * const t = b->prev();
michael@0 765 a->prev()->next(b); b->prev(a->prev()); // splice middle
michael@0 766 t->next(a); a->prev(t); // splice ends
michael@0 767 return a;
michael@0 768 }
michael@0 769
michael@0 770 /*
michael@0 771 Given the first slot in a run of slots with the same bidi level, turn the run
michael@0 772 into it's own little doubly linked list ring (a span) with the two ends joined together.
michael@0 773 If the run is rtl then reverse its direction.
michael@0 774 Returns the first slot after the span
michael@0 775 */
michael@0 776 Slot * span(Slot * & cs, const bool rtl)
michael@0 777 {
michael@0 778 Slot * r = cs, * re = cs; cs = cs->next();
michael@0 779 if (rtl)
michael@0 780 {
michael@0 781 Slot * t = r->next(); r->next(r->prev()); r->prev(t);
michael@0 782 for (int l = r->getBidiLevel(); cs && (l == cs->getBidiLevel() || cs->getBidiClass() == BN); cs = cs->prev())
michael@0 783 {
michael@0 784 re = cs;
michael@0 785 t = cs->next(); cs->next(cs->prev()); cs->prev(t);
michael@0 786 }
michael@0 787 r->next(re);
michael@0 788 re->prev(r);
michael@0 789 r = re;
michael@0 790 }
michael@0 791 else
michael@0 792 {
michael@0 793 for (int l = r->getBidiLevel(); cs && (l == cs->getBidiLevel() || cs->getBidiClass() == BN); cs = cs->next())
michael@0 794 re = cs;
michael@0 795 r->prev(re);
michael@0 796 re->next(r);
michael@0 797 }
michael@0 798 if (cs) cs->prev(0);
michael@0 799 return r;
michael@0 800 }
michael@0 801
michael@0 802 inline int getlevel(const Slot *cs, const int level)
michael@0 803 {
michael@0 804 while (cs && cs->getBidiClass() == BN)
michael@0 805 { cs = cs->next(); }
michael@0 806 if (cs)
michael@0 807 return cs->getBidiLevel();
michael@0 808 else
michael@0 809 return level;
michael@0 810 }
michael@0 811
michael@0 812 Slot *resolveOrder(Slot * & cs, const bool reordered, const int level)
michael@0 813 {
michael@0 814 Slot * r = 0;
michael@0 815 int ls;
michael@0 816 while (cs && level <= (ls = getlevel(cs, level) - reordered))
michael@0 817 {
michael@0 818 r = join(level, r, level < ls
michael@0 819 ? resolveOrder(/* updates */cs, reordered, level+1) // find span of heighest level
michael@0 820 : span(/* updates */cs, level & 1));
michael@0 821 }
michael@0 822 return r;
michael@0 823 }

mercurial