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