1 : /* GRAPHITE2 LICENSING
2 :
3 : Copyright 2010, SIL International
4 : All rights reserved.
5 :
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.
10 :
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.
15 :
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.
21 :
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 :
31 : using namespace graphite2;
32 :
33 : enum DirCode { // Hungarian: dirc
34 : Unk = -1,
35 : N = 0, // other neutrals (default) - ON
36 : L = 1, // left-to-right, strong - L
37 : R = 2, // right-to-left, strong - R
38 : AL = 3, // Arabic letter, right-to-left, strong, AR
39 : EN = 4, // European number, left-to-right, weak - EN
40 : ES = 5, // European separator, left-to-right, weak - ES
41 : ET = 6, // European number terminator, left-to-right, weak - ET
42 : AN = 7, // Arabic number, left-to-right, weak - AN
43 : CS = 8, // Common number separator, left-to-right, weak - CS
44 : WS = 9, // white space, neutral - WS
45 : BN = 10, // boundary neutral - BN
46 :
47 : LRO = 11, // LTR override
48 : RLO = 12, // RTL override
49 : LRE = 13, // LTR embedding
50 : RLE = 14, // RTL embedding
51 : PDF = 15, // pop directional format
52 : NSM = 16, // non-space mark
53 :
54 : ON = N
55 : };
56 :
57 : enum DirMask {
58 : Nmask = 1,
59 : Lmask = 2,
60 : Rmask = 4,
61 : ALmask = 8,
62 : ENmask = 0x10,
63 : ESmask = 0x20,
64 : ETmask = 0x40,
65 : ANmask = 0x80,
66 : CSmask = 0x100,
67 : WSmask = 0x200,
68 : BNmask = 0x400,
69 : LROmask = 0x800,
70 : RLOmask = 0x1000,
71 : LREmask = 0x2000,
72 : RLEmask = 0x4000,
73 : PDFmask = 0x8000,
74 : NSMmask = 0x10000
75 : };
76 :
77 : unsigned int bidi_class_map[] = { 0, 1, 2, 5, 4, 8, 9, 3, 7, 0, 0, 0, 0, 0, 0, 0, 6 };
78 : // Algorithms based on Unicode reference standard code. Thanks Asmus Freitag.
79 : #define MAX_LEVEL 61
80 :
81 0 : Slot *resolveExplicit(int level, int dir, Slot *s, int nNest = 0)
82 : {
83 0 : int nLastValid = nNest;
84 0 : Slot *res = NULL;
85 0 : for ( ; s && !res; s = s->next())
86 : {
87 0 : int cls = s->getBidiClass();
88 0 : switch(cls)
89 : {
90 : case LRO:
91 : case LRE:
92 0 : nNest++;
93 0 : if (level & 1)
94 0 : s->setBidiLevel(level + 1);
95 : else
96 0 : s->setBidiLevel(level + 2);
97 0 : if (s->getBidiLevel() > MAX_LEVEL)
98 0 : s->setBidiLevel(level);
99 : else
100 : {
101 0 : s = resolveExplicit(s->getBidiLevel(), (cls == LRE ? N : L), s->next(), nNest);
102 0 : nNest--;
103 0 : if (s) continue; else break;
104 : }
105 0 : cls = BN;
106 0 : s->setBidiClass(cls);
107 0 : break;
108 :
109 : case RLO:
110 : case RLE:
111 0 : nNest++;
112 0 : if (level & 1)
113 0 : s->setBidiLevel(level + 2);
114 : else
115 0 : s->setBidiLevel(level + 1);
116 0 : if (s->getBidiLevel() > MAX_LEVEL)
117 0 : s->setBidiLevel(level);
118 : else
119 : {
120 0 : s = resolveExplicit(s->getBidiLevel(), (cls == RLE ? N : R), s->next(), nNest);
121 0 : nNest--;
122 0 : if (s) continue; else break;
123 : }
124 0 : cls = BN;
125 0 : s->setBidiClass(cls);
126 0 : break;
127 :
128 : case PDF:
129 0 : cls = BN;
130 0 : s->setBidiClass(cls);
131 0 : if (nNest)
132 : {
133 0 : if (nLastValid < nNest)
134 0 : --nNest;
135 : else
136 0 : res = s;
137 : }
138 0 : break;
139 : }
140 :
141 0 : if (dir != N)
142 0 : cls = dir;
143 0 : if (s)
144 : {
145 0 : s->setBidiLevel(level);
146 0 : if (s->getBidiClass() != BN)
147 0 : s->setBidiClass(cls);
148 : }
149 : else
150 0 : break;
151 : }
152 0 : return res;
153 : }
154 :
155 : // === RESOLVE WEAK TYPES ================================================
156 :
157 : enum bidi_state // possible states
158 : {
159 : xa, // arabic letter
160 : xr, // right leter
161 : xl, // left letter
162 :
163 : ao, // arabic lett. foll by ON
164 : ro, // right lett. foll by ON
165 : lo, // left lett. foll by ON
166 :
167 : rt, // ET following R
168 : lt, // ET following L
169 :
170 : cn, // EN, AN following AL
171 : ra, // arabic number foll R
172 : re, // european number foll R
173 : la, // arabic number foll L
174 : le, // european number foll L
175 :
176 : ac, // CS following cn
177 : rc, // CS following ra
178 : rs, // CS,ES following re
179 : lc, // CS following la
180 : ls, // CS,ES following le
181 :
182 : ret, // ET following re
183 : let, // ET following le
184 : } ;
185 :
186 : enum bidi_state_mask
187 : {
188 : xamask = 1,
189 : xrmask = 2,
190 : xlmask = 4,
191 : aomask = 8,
192 : romask = 0x10,
193 : lomask = 0x20,
194 : rtmask = 0x40,
195 : ltmask = 0x80,
196 : cnmask = 0x100,
197 : ramask = 0x200,
198 : remask = 0x400,
199 : lamask = 0x800,
200 : lemask = 0x1000,
201 : acmask = 0x2000,
202 : rcmask = 0x4000,
203 : rsmask = 0x8000,
204 : lcmask = 0x10000,
205 : lsmask = 0x20000,
206 : retmask = 0x40000,
207 : letmask = 0x80000
208 : };
209 :
210 : const bidi_state stateWeak[][10] =
211 : {
212 : // N, L, R, AN, EN, AL,NSM, CS, ES, ET,
213 : { /*xa*/ ao, xl, xr, cn, cn, xa, xa, ao, ao, ao, /* arabic letter */ },
214 : { /*xr*/ ro, xl, xr, ra, re, xa, xr, ro, ro, rt, /* right letter */ },
215 : { /*xl*/ lo, xl, xr, la, le, xa, xl, lo, lo, lt, /* left letter */ },
216 :
217 : { /*ao*/ ao, xl, xr, cn, cn, xa, ao, ao, ao, ao, /* arabic lett. foll by ON*/ },
218 : { /*ro*/ ro, xl, xr, ra, re, xa, ro, ro, ro, rt, /* right lett. foll by ON */ },
219 : { /*lo*/ lo, xl, xr, la, le, xa, lo, lo, lo, lt, /* left lett. foll by ON */ },
220 :
221 : { /*rt*/ ro, xl, xr, ra, re, xa, rt, ro, ro, rt, /* ET following R */ },
222 : { /*lt*/ lo, xl, xr, la, le, xa, lt, lo, lo, lt, /* ET following L */ },
223 :
224 : { /*cn*/ ao, xl, xr, cn, cn, xa, cn, ac, ao, ao, /* EN, AN following AL */ },
225 : { /*ra*/ ro, xl, xr, ra, re, xa, ra, rc, ro, rt, /* arabic number foll R */ },
226 : { /*re*/ ro, xl, xr, ra, re, xa, re, rs, rs,ret, /* european number foll R */ },
227 : { /*la*/ lo, xl, xr, la, le, xa, la, lc, lo, lt, /* arabic number foll L */ },
228 : { /*le*/ lo, xl, xr, la, le, xa, le, ls, ls,let, /* european number foll L */ },
229 :
230 : { /*ac*/ ao, xl, xr, cn, cn, xa, ao, ao, ao, ao, /* CS following cn */ },
231 : { /*rc*/ ro, xl, xr, ra, re, xa, ro, ro, ro, rt, /* CS following ra */ },
232 : { /*rs*/ ro, xl, xr, ra, re, xa, ro, ro, ro, rt, /* CS,ES following re */ },
233 : { /*lc*/ lo, xl, xr, la, le, xa, lo, lo, lo, lt, /* CS following la */ },
234 : { /*ls*/ lo, xl, xr, la, le, xa, lo, lo, lo, lt, /* CS,ES following le */ },
235 :
236 : { /*ret*/ ro, xl, xr, ra, re, xa,ret, ro, ro,ret, /* ET following re */ },
237 : { /*let*/ lo, xl, xr, la, le, xa,let, lo, lo,let, /* ET following le */ },
238 :
239 :
240 : };
241 :
242 : enum bidi_action // possible actions
243 : {
244 : // primitives
245 : IX = 0x100, // increment
246 : XX = 0xF, // no-op
247 :
248 : // actions
249 : xxx = (XX << 4) + XX, // no-op
250 : xIx = IX + xxx, // increment run
251 : xxN = (XX << 4) + ON, // set current to N
252 : xxE = (XX << 4) + EN, // set current to EN
253 : xxA = (XX << 4) + AN, // set current to AN
254 : xxR = (XX << 4) + R, // set current to R
255 : xxL = (XX << 4) + L, // set current to L
256 : Nxx = (ON << 4) + 0xF, // set run to neutral
257 : Axx = (AN << 4) + 0xF, // set run to AN
258 : ExE = (EN << 4) + EN, // set run to EN, set current to EN
259 : NIx = (ON << 4) + 0xF + IX, // set run to N, increment
260 : NxN = (ON << 4) + ON, // set run to N, set current to N
261 : NxR = (ON << 4) + R, // set run to N, set current to R
262 : NxE = (ON << 4) + EN, // set run to N, set current to EN
263 :
264 : AxA = (AN << 4) + AN, // set run to AN, set current to AN
265 : NxL = (ON << 4) + L, // set run to N, set current to L
266 : LxL = (L << 4) + L, // set run to L, set current to L
267 : };
268 :
269 :
270 : const bidi_action actionWeak[][10] =
271 : {
272 : // N,.. L, R, AN, EN, AL, NSM, CS,..ES, ET,
273 : { /*xa*/ xxx, xxx, xxx, xxx, xxA, xxR, xxR, xxN, xxN, xxN, /* arabic letter */ },
274 : { /*xr*/ xxx, xxx, xxx, xxx, xxE, xxR, xxR, xxN, xxN, xIx, /* right leter */ },
275 : { /*xl*/ xxx, xxx, xxx, xxx, xxL, xxR, xxL, xxN, xxN, xIx, /* left letter */ },
276 :
277 : { /*ao*/ xxx, xxx, xxx, xxx, xxA, xxR, xxN, xxN, xxN, xxN, /* arabic lett. foll by ON */ },
278 : { /*ro*/ xxx, xxx, xxx, xxx, xxE, xxR, xxN, xxN, xxN, xIx, /* right lett. foll by ON */ },
279 : { /*lo*/ xxx, xxx, xxx, xxx, xxL, xxR, xxN, xxN, xxN, xIx, /* left lett. foll by ON */ },
280 :
281 : { /*rt*/ Nxx, Nxx, Nxx, Nxx, ExE, NxR, xIx, NxN, NxN, xIx, /* ET following R */ },
282 : { /*lt*/ Nxx, Nxx, Nxx, Nxx, LxL, NxR, xIx, NxN, NxN, xIx, /* ET following L */ },
283 :
284 : { /*cn*/ xxx, xxx, xxx, xxx, xxA, xxR, xxA, xIx, xxN, xxN, /* EN, AN following AL */ },
285 : { /*ra*/ xxx, xxx, xxx, xxx, xxE, xxR, xxA, xIx, xxN, xIx, /* arabic number foll R */ },
286 : { /*re*/ xxx, xxx, xxx, xxx, xxE, xxR, xxE, xIx, xIx, xxE, /* european number foll R */ },
287 : { /*la*/ xxx, xxx, xxx, xxx, xxL, xxR, xxA, xIx, xxN, xIx, /* arabic number foll L */ },
288 : { /*le*/ xxx, xxx, xxx, xxx, xxL, xxR, xxL, xIx, xIx, xxL, /* european number foll L */ },
289 :
290 : { /*ac*/ Nxx, Nxx, Nxx, Axx, AxA, NxR, NxN, NxN, NxN, NxN, /* CS following cn */ },
291 : { /*rc*/ Nxx, Nxx, Nxx, Axx, NxE, NxR, NxN, NxN, NxN, NIx, /* CS following ra */ },
292 : { /*rs*/ Nxx, Nxx, Nxx, Nxx, ExE, NxR, NxN, NxN, NxN, NIx, /* CS,ES following re */ },
293 : { /*lc*/ Nxx, Nxx, Nxx, Axx, NxL, NxR, NxN, NxN, NxN, NIx, /* CS following la */ },
294 : { /*ls*/ Nxx, Nxx, Nxx, Nxx, LxL, NxR, NxN, NxN, NxN, NIx, /* CS,ES following le */ },
295 :
296 : { /*ret*/xxx, xxx, xxx, xxx, xxE, xxR, xxE, xxN, xxN, xxE, /* ET following re */ },
297 : { /*let*/xxx, xxx, xxx, xxx, xxL, xxR, xxL, xxN, xxN, xxL, /* ET following le */ },
298 : };
299 :
300 0 : inline uint8 GetDeferredType(bidi_action a) { return (a >> 4) & 0xF; }
301 0 : inline uint8 GetResolvedType(bidi_action a) { return a & 0xF; }
302 0 : inline DirCode EmbeddingDirection(int l) { return l & 1 ? R : L; }
303 : inline bool IsDeferredState(bidi_state a) { return (1 << a) & (rtmask | ltmask | acmask | rcmask | rsmask | lcmask | lsmask); }
304 : inline bool IsModifiedClass(DirCode a) { return (1 << a) & (ALmask | NSMmask | ESmask | CSmask | ETmask | ENmask); }
305 :
306 0 : void SetDeferredRunClass(Slot *s, Slot *sRun, int nval)
307 : {
308 0 : if (!sRun || s == sRun) return;
309 0 : for (Slot *p = s->prev(); p != sRun; p = p->prev())
310 0 : p->setBidiClass(nval);
311 : }
312 :
313 0 : void resolveWeak(int baseLevel, Slot *s)
314 : {
315 0 : int state = (baseLevel & 1) ? xr : xl;
316 : int cls;
317 0 : int level = baseLevel;
318 0 : Slot *sRun = NULL;
319 0 : Slot *sLast = s;
320 :
321 0 : for ( ; s; s = s->next())
322 : {
323 0 : sLast = s;
324 0 : cls = s->getBidiClass();
325 0 : if (cls == BN)
326 : {
327 0 : s->setBidiLevel(level);
328 0 : if (!s->next() && level != baseLevel)
329 0 : s->setBidiClass(EmbeddingDirection(level));
330 0 : else if (s->next() && level != s->next()->getBidiLevel() && s->next()->getBidiClass() != BN)
331 : {
332 0 : int newLevel = s->next()->getBidiLevel();
333 0 : if (level > newLevel)
334 0 : newLevel = level;
335 0 : s->setBidiLevel(newLevel);
336 0 : s->setBidiClass(EmbeddingDirection(newLevel));
337 0 : level = s->next()->getBidiLevel();
338 : }
339 : else
340 0 : continue;
341 : }
342 :
343 0 : bidi_action action = actionWeak[state][bidi_class_map[cls]];
344 0 : int clsRun = GetDeferredType(action);
345 0 : if (clsRun != XX)
346 : {
347 0 : SetDeferredRunClass(s, sRun, clsRun);
348 0 : sRun = NULL;
349 : }
350 0 : int clsNew = GetResolvedType(action);
351 0 : if (clsNew != XX)
352 0 : s->setBidiClass(clsNew);
353 0 : if (!sRun && (IX & action))
354 0 : sRun = s->prev();
355 0 : state = stateWeak[state][bidi_class_map[cls]];
356 : }
357 :
358 0 : cls = EmbeddingDirection(level);
359 0 : int clsRun = GetDeferredType(actionWeak[state][bidi_class_map[cls]]);
360 0 : if (clsRun != XX)
361 0 : SetDeferredRunClass(sLast, sRun, clsRun);
362 0 : }
363 :
364 : // Neutrals
365 : enum neutral_action
366 : {
367 : // action to resolve previous input
368 : nL = L, // resolve EN to L
369 : En = 3 << 4, // resolve neutrals run to embedding level direction
370 : Rn = R << 4, // resolve neutrals run to strong right
371 : Ln = L << 4, // resolved neutrals run to strong left
372 : In = (1<<8), // increment count of deferred neutrals
373 : LnL = (1<<4)+L, // set run and EN to L
374 : };
375 :
376 0 : int GetDeferredNeutrals(int action, int level)
377 : {
378 0 : action = (action >> 4) & 0xF;
379 0 : if (action == (En >> 4))
380 0 : return EmbeddingDirection(level);
381 : else
382 0 : return action;
383 : }
384 :
385 0 : int GetResolvedNeutrals(int action)
386 : {
387 0 : action = action & 0xF;
388 0 : if (action == In)
389 0 : return 0;
390 : else
391 0 : return action;
392 : }
393 :
394 : // state values
395 : enum neutral_state
396 : {
397 : // new temporary class
398 : r, // R and characters resolved to R
399 : l, // L and characters resolved to L
400 : rn, // N preceded by right
401 : ln, // N preceded by left
402 : a, // AN preceded by left (the abbrev 'la' is used up above)
403 : na, // N preceeded by a
404 : } ;
405 :
406 : const uint8 neutral_class_map[] = { 0, 1, 2, 0, 4, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0 };
407 :
408 : const int actionNeutrals[][5] =
409 : {
410 : // N, L, R, AN, EN, = cls
411 : // state =
412 : { In, 0, 0, 0, 0, }, // r right
413 : { In, 0, 0, 0, L, }, // l left
414 :
415 : { In, En, Rn, Rn, Rn, }, // rn N preceded by right
416 : { In, Ln, En, En, LnL, }, // ln N preceded by left
417 :
418 : { In, 0, 0, 0, L, }, // a AN preceded by left
419 : { In, En, Rn, Rn, En, }, // na N preceded by a
420 : } ;
421 :
422 : const int stateNeutrals[][5] =
423 : {
424 : // N, L, R, AN, EN = cls
425 : // state =
426 : { rn, l, r, r, r, }, // r right
427 : { ln, l, r, a, l, }, // l left
428 :
429 : { rn, l, r, r, r, }, // rn N preceded by right
430 : { ln, l, r, a, l, }, // ln N preceded by left
431 :
432 : { na, l, r, a, l, }, // a AN preceded by left
433 : { na, l, r, a, l, }, // na N preceded by la
434 : } ;
435 :
436 0 : void resolveNeutrals(int baseLevel, Slot *s)
437 : {
438 0 : int state = baseLevel ? r : l;
439 : int cls;
440 0 : Slot *sRun = NULL;
441 0 : Slot *sLast = s;
442 0 : int level = baseLevel;
443 :
444 0 : for ( ; s; s = s->next())
445 : {
446 0 : sLast = s;
447 0 : cls = s->getBidiClass();
448 0 : if (cls == BN)
449 : {
450 0 : if (sRun)
451 0 : sRun = sRun->prev();
452 0 : continue;
453 : }
454 :
455 0 : int action = actionNeutrals[state][neutral_class_map[cls]];
456 0 : int clsRun = GetDeferredNeutrals(action, level);
457 0 : if (clsRun != N)
458 : {
459 0 : SetDeferredRunClass(s, sRun, clsRun);
460 0 : sRun = NULL;
461 : }
462 0 : int clsNew = GetResolvedNeutrals(action);
463 0 : if (clsNew != N)
464 0 : s->setBidiClass(clsNew);
465 0 : state = stateNeutrals[state][neutral_class_map[cls]];
466 0 : level = s->getBidiLevel();
467 : }
468 0 : cls = EmbeddingDirection(level);
469 0 : int clsRun = GetDeferredNeutrals(actionNeutrals[state][neutral_class_map[cls]], level);
470 0 : if (clsRun != N)
471 0 : SetDeferredRunClass(sLast, sRun, clsRun);
472 0 : }
473 :
474 : const int addLevel[][4] =
475 : {
476 : // L, R, AN, EN = cls
477 : // level =
478 : /* even */ { 0, 1, 2, 2, }, // EVEN
479 : /* odd */ { 1, 0, 1, 1, }, // ODD
480 :
481 : };
482 :
483 0 : void resolveImplicit(Slot *s, Segment *seg, uint8 aMirror)
484 : {
485 0 : bool rtl = seg->dir() & 1;
486 0 : for ( ; s; s = s->next())
487 : {
488 0 : int cls = s->getBidiClass();
489 0 : if (cls == BN)
490 0 : continue;
491 0 : else if (cls == AN)
492 0 : cls = AL;
493 0 : if (cls < 5 && cls > 0)
494 : {
495 0 : int level = s->getBidiLevel();
496 0 : level += addLevel[level & 1][cls - 1];
497 0 : s->setBidiLevel(level);
498 0 : if (aMirror)
499 : {
500 0 : int hasChar = seg->glyphAttr(s->gid(), aMirror + 1);
501 0 : if ( ((level & 1) && (!(seg->dir() & 4) || !hasChar))
502 0 : || ((rtl ^ (level & 1)) && (seg->dir() & 4) && hasChar) )
503 : {
504 0 : unsigned short g = seg->glyphAttr(s->gid(), aMirror);
505 0 : if (g) s->setGlyph(seg, g);
506 : }
507 : }
508 : }
509 : }
510 0 : }
511 :
512 0 : void resolveWhitespace(int baseLevel, Segment *seg, uint8 aBidi, Slot *s)
513 : {
514 0 : for ( ; s; s = s->prev())
515 : {
516 0 : int cls = seg->glyphAttr(s->gid(), aBidi);
517 0 : if (cls == WS)
518 0 : s->setBidiLevel(baseLevel);
519 : else
520 0 : break;
521 : }
522 0 : }
523 :
524 :
525 : inline
526 0 : Slot * join(int level, Slot * a, Slot * b)
527 : {
528 0 : if (!a) return b;
529 0 : if (level & 1) { Slot * const t = a; a = b; b = t; }
530 0 : Slot * const t = b->prev();
531 0 : a->prev()->next(b); b->prev(a->prev()); // splice middle
532 0 : t->next(a); a->prev(t); // splice ends
533 0 : return a;
534 : }
535 :
536 :
537 0 : Slot * span(Slot * & cs, const bool rtl)
538 : {
539 0 : Slot * r = cs, * re = cs; cs = cs->next();
540 0 : if (rtl)
541 : {
542 0 : Slot * t = r->next(); r->next(r->prev()); r->prev(t);
543 0 : for (int l = r->getBidiLevel(); cs && l == cs->getBidiLevel(); cs = cs->prev())
544 : {
545 0 : re = cs;
546 0 : t = cs->next(); cs->next(cs->prev()); cs->prev(t);
547 : }
548 0 : r->next(re);
549 0 : re->prev(r);
550 0 : r = re;
551 : }
552 : else
553 : {
554 0 : for (int l = r->getBidiLevel(); cs && l == cs->getBidiLevel(); cs = cs->next())
555 0 : re = cs;
556 0 : r->prev(re);
557 0 : re->next(r);
558 : }
559 0 : if (cs) cs->prev(0);
560 0 : return r;
561 : }
562 :
563 :
564 0 : Slot *resolveOrder(Slot * & cs, const bool reordered, const int level)
565 : {
566 0 : Slot * r = 0;
567 : int ls;
568 0 : while (cs && level <= (ls = cs->getBidiLevel() - reordered))
569 : {
570 : r = join(level, r, level >= ls
571 0 : ? span(cs, level & 1)
572 0 : : resolveOrder(cs, reordered, level+1));
573 : }
574 0 : return r;
575 : }
576 :
|