Bidi.cs 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581
  1. // RichTextKit
  2. // Copyright © 2019-2020 Topten Software. All Rights Reserved.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. // not use this product except in compliance with the License. You may obtain
  6. // a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. // License for the specific language governing permissions and limitations
  14. // under the License.
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Linq;
  18. using System.Runtime.CompilerServices;
  19. using System.Threading;
  20. using Topten.RichTextKit.Utils;
  21. namespace Topten.RichTextKit
  22. {
  23. /// <summary>
  24. /// Implementation of Unicode Bidirection Algorithm (UAX #9)
  25. /// https://unicode.org/reports/tr9/
  26. /// </summary>
  27. /// <remarks>
  28. /// The Bidi algorithm uses a number of memory arrays for resolved
  29. /// types, level information, bracket types, x9 removal maps and
  30. /// more...
  31. ///
  32. /// This implementation of the Bidi algorithm has been designed
  33. /// to reduce memory pressure on the GC by re-using the same
  34. /// work buffers, so instances of this class should be re-used
  35. /// as much as possible.
  36. /// </remarks>
  37. class Bidi
  38. {
  39. /// <summary>
  40. /// A per-thread instance that can be re-used as often
  41. /// as necessary.
  42. /// </summary>
  43. internal static ThreadLocal<Bidi> Instance = new ThreadLocal<Bidi>(() => new Bidi());
  44. /// <summary>
  45. /// Constructs a new instance of Bidi algorithm processor
  46. /// </summary>
  47. public Bidi()
  48. {
  49. }
  50. /// <summary>
  51. /// Get the resolved levels
  52. /// </summary>
  53. public Slice<sbyte> ResolvedLevels => _resolvedLevels;
  54. /// <summary>
  55. /// Get the resolved paragraph embedding level
  56. /// </summary>
  57. public int ResolvedParagraphEmbeddingLevel => _paragraphEmbeddingLevel;
  58. /// <summary>
  59. /// Process data from a BidiData instance
  60. /// </summary>
  61. /// <param name="data"></param>
  62. public void Process(BidiData data)
  63. {
  64. Process(data.Types, data.PairedBracketTypes, data.PairedBracketValues, data.ParagraphEmbeddingLevel, data.HasBrackets, data.HasEmbeddings, data.HasIsolates, null);
  65. }
  66. /// <summary>
  67. /// Processes Bidi Data
  68. /// </summary>
  69. public void Process(
  70. Slice<Directionality> types,
  71. Slice<PairedBracketType> pairedBracketTypes,
  72. Slice<int> pairedBracketValues,
  73. sbyte paragraphEmbeddingLevel,
  74. bool? hasBrackets,
  75. bool? hasEmbeddings,
  76. bool? hasIsolates,
  77. Slice<sbyte>? outLevels
  78. )
  79. {
  80. // Reset state
  81. _isolatePairs.Clear();
  82. _workingTypesBuffer.Clear();
  83. _levelRuns.Clear();
  84. _resolvedLevelsBuffer.Clear();
  85. // Setup original types and working types
  86. _originalTypes = types;
  87. _workingTypes = _workingTypesBuffer.Add(types);
  88. // Capture paired bracket values and types
  89. _pairedBracketTypes = pairedBracketTypes;
  90. _pairedBracketValues = pairedBracketValues;
  91. // Store things we know
  92. _hasBrackets = hasBrackets ?? _pairedBracketTypes.Length == _originalTypes.Length;
  93. _hasEmbeddings = hasEmbeddings ?? true;
  94. _hasIsolates = hasIsolates ?? true;
  95. // Find all isolate pairs
  96. FindIsolatePairs();
  97. // Resolve the paragraph embedding level
  98. if (paragraphEmbeddingLevel == 2)
  99. _paragraphEmbeddingLevel = ResolveEmbeddingLevel(_originalTypes);
  100. else
  101. _paragraphEmbeddingLevel = paragraphEmbeddingLevel;
  102. // Create resolved levels buffer
  103. if (outLevels.HasValue)
  104. {
  105. if (outLevels.Value.Length != _originalTypes.Length)
  106. throw new ArgumentException("Out levels must be the same length as the input data");
  107. _resolvedLevels = outLevels.Value;
  108. }
  109. else
  110. {
  111. _resolvedLevels = _resolvedLevelsBuffer.Add(_originalTypes.Length);
  112. _resolvedLevels.Fill(_paragraphEmbeddingLevel);
  113. }
  114. // Resolve explicit embedding levels (Rules X1-X8)
  115. ResolveExplicitEmbeddingLevels();
  116. // Build the rule X9 map
  117. BuildX9RemovalMap();
  118. // Process all isolated run sequences
  119. ProcessIsolatedRunSequences();
  120. // Reset whitespace levels
  121. ResetWhitespaceLevels();
  122. // Clean up
  123. AssignLevelsToCodePointsRemovedByX9();
  124. }
  125. /// <summary>
  126. /// The original Directionality types as provided by the caller
  127. /// </summary>
  128. Slice<Directionality> _originalTypes;
  129. /// <summary>
  130. /// Paired bracket types as provided by caller
  131. /// </summary>
  132. Slice<PairedBracketType> _pairedBracketTypes;
  133. /// <summary>
  134. /// Paired bracket values as provided by caller
  135. /// </summary>
  136. Slice<int> _pairedBracketValues;
  137. /// <summary>
  138. /// Try if the incoming data is known to contain brackets
  139. /// </summary>
  140. bool _hasBrackets;
  141. /// <summary>
  142. /// True if the incoming data is known to contain embedding runs
  143. /// </summary>
  144. bool _hasEmbeddings;
  145. /// <summary>
  146. /// True if the incomding data is known to contain isolating runs
  147. /// </summary>
  148. bool _hasIsolates;
  149. /// <summary>
  150. /// Two directional mapping of isolate start/end pairs
  151. /// </summary>
  152. /// <remarks>
  153. /// The forward mapping maps the start index to the end index.
  154. /// The reverse mapping maps the end index to the start index.
  155. /// </remarks>
  156. BiDictionary<int, int> _isolatePairs = new BiDictionary<int, int>();
  157. /// <summary>
  158. /// The working Directionality types
  159. /// </summary>
  160. Slice<Directionality> _workingTypes;
  161. /// <summary>
  162. /// The buffer underlying _workingTypes
  163. /// </summary>
  164. Buffer<Directionality> _workingTypesBuffer = new Buffer<Directionality>();
  165. /// <summary>
  166. /// The resolved levels
  167. /// </summary>
  168. Slice<sbyte> _resolvedLevels;
  169. /// <summary>
  170. /// The buffer underlying _resolvedLevels
  171. /// </summary>
  172. Buffer<sbyte> _resolvedLevelsBuffer = new Buffer<sbyte>();
  173. /// <summary>
  174. /// The resolve paragraph embedding level
  175. /// </summary>
  176. sbyte _paragraphEmbeddingLevel;
  177. /// <summary>
  178. /// Status stack entry used while resolving explicit
  179. /// embedding levels
  180. /// </summary>
  181. struct Status
  182. {
  183. public sbyte EmbeddingLevel;
  184. public Directionality OverrideStatus;
  185. public bool IsolateStatus;
  186. }
  187. /// <summary>
  188. /// The status stack used during resolution of explicit
  189. /// embedding and isolating runs
  190. /// </summary>
  191. Stack<Status> _statusStack = new Stack<Status>();
  192. /// <summary>
  193. /// Mapping used to virtually remove characters for rule X9
  194. /// </summary>
  195. Buffer<int> _X9Map = new Buffer<int>();
  196. /// <summary>
  197. /// Re-usable list of level runs
  198. /// </summary>
  199. List<LevelRun> _levelRuns = new List<LevelRun>();
  200. /// <summary>
  201. /// Mapping for the current isolating sequence, built
  202. /// by joining level runs from the x9 map.
  203. /// </summary>
  204. Buffer<int> _isolatedRunMapping = new Buffer<int>();
  205. /// <summary>
  206. /// A stack of pending isolate openings used by FindIsolatePairs()
  207. /// </summary>
  208. Stack<int> _pendingIsolateOpenings = new Stack<int>();
  209. /// <summary>
  210. /// Build a list of matching isolates for a directionality slice
  211. /// Implements BD9
  212. /// </summary>
  213. void FindIsolatePairs()
  214. {
  215. // Redundant?
  216. if (!_hasIsolates)
  217. return;
  218. // Lets double check this as we go and clear the flag
  219. // if there actually aren't any isolate pairs as this might
  220. // mean we can skip some later steps
  221. _hasIsolates = false;
  222. // BD9...
  223. _pendingIsolateOpenings.Clear();
  224. for (int i = 0; i < _originalTypes.Length; i++)
  225. {
  226. var t = _originalTypes[i];
  227. if (t == Directionality.LRI || t == Directionality.RLI || t == Directionality.FSI)
  228. {
  229. _pendingIsolateOpenings.Push(i);
  230. _hasIsolates = true;
  231. }
  232. else if (t == Directionality.PDI)
  233. {
  234. if (_pendingIsolateOpenings.Count > 0)
  235. {
  236. _isolatePairs.Add(_pendingIsolateOpenings.Pop(), i);
  237. }
  238. _hasIsolates = true;
  239. }
  240. }
  241. }
  242. /// <summary>
  243. /// Resolve the explicit embedding levels from the original
  244. /// data. Implements rules X1 to X8.
  245. /// </summary>
  246. private void ResolveExplicitEmbeddingLevels()
  247. {
  248. // Redundant?
  249. if (!_hasIsolates && !_hasEmbeddings)
  250. return;
  251. // Work variables
  252. _statusStack.Clear();
  253. int overflowIsolateCount = 0;
  254. int overflowEmbeddingCount = 0;
  255. int validIsolateCount = 0;
  256. // Constants
  257. const int maxStackDepth = 125;
  258. // Rule X1 - setup initial state
  259. _statusStack.Clear();
  260. _statusStack.Push(new Status()
  261. {
  262. EmbeddingLevel = _paragraphEmbeddingLevel,
  263. OverrideStatus = Directionality.ON, // Neutral
  264. IsolateStatus = false,
  265. });
  266. // Process all characters
  267. for (int i = 0; i < _originalTypes.Length; i++)
  268. {
  269. switch (_originalTypes[i])
  270. {
  271. case Directionality.RLE:
  272. {
  273. // Rule X2
  274. var newLevel = (sbyte)((_statusStack.Peek().EmbeddingLevel + 1) | 1);
  275. if (newLevel <= maxStackDepth && overflowIsolateCount == 0 && overflowEmbeddingCount == 0)
  276. {
  277. _statusStack.Push(new Status()
  278. {
  279. EmbeddingLevel = newLevel,
  280. OverrideStatus = Directionality.ON,
  281. IsolateStatus = false,
  282. });
  283. _resolvedLevels[i] = newLevel;
  284. }
  285. else
  286. {
  287. if (overflowIsolateCount == 0)
  288. overflowEmbeddingCount++;
  289. }
  290. break;
  291. }
  292. case Directionality.LRE:
  293. {
  294. // Rule X3
  295. var newLevel = (sbyte)((_statusStack.Peek().EmbeddingLevel + 2) & ~1);
  296. if (newLevel < maxStackDepth && overflowIsolateCount == 0 && overflowEmbeddingCount == 0)
  297. {
  298. _statusStack.Push(new Status()
  299. {
  300. EmbeddingLevel = newLevel,
  301. OverrideStatus = Directionality.ON,
  302. IsolateStatus = false,
  303. });
  304. _resolvedLevels[i] = newLevel;
  305. }
  306. else
  307. {
  308. if (overflowIsolateCount == 0)
  309. overflowEmbeddingCount++;
  310. }
  311. break;
  312. }
  313. case Directionality.RLO:
  314. {
  315. // Rule X4
  316. var newLevel = (sbyte)((_statusStack.Peek().EmbeddingLevel + 1) | 1);
  317. if (newLevel <= maxStackDepth && overflowIsolateCount == 0 && overflowEmbeddingCount == 0)
  318. {
  319. _statusStack.Push(new Status()
  320. {
  321. EmbeddingLevel = newLevel,
  322. OverrideStatus = Directionality.R,
  323. IsolateStatus = false,
  324. });
  325. _resolvedLevels[i] = newLevel;
  326. }
  327. else
  328. {
  329. if (overflowIsolateCount == 0)
  330. overflowEmbeddingCount++;
  331. }
  332. break;
  333. }
  334. case Directionality.LRO:
  335. {
  336. // Rule X5
  337. var newLevel = (sbyte)((_statusStack.Peek().EmbeddingLevel + 2) & ~1);
  338. if (newLevel <= maxStackDepth && overflowIsolateCount == 0 && overflowEmbeddingCount == 0)
  339. {
  340. _statusStack.Push(new Status()
  341. {
  342. EmbeddingLevel = newLevel,
  343. OverrideStatus = Directionality.L,
  344. IsolateStatus = false,
  345. });
  346. _resolvedLevels[i] = newLevel;
  347. }
  348. else
  349. {
  350. if (overflowIsolateCount == 0)
  351. overflowEmbeddingCount++;
  352. }
  353. break;
  354. }
  355. case Directionality.RLI:
  356. case Directionality.LRI:
  357. case Directionality.FSI:
  358. {
  359. // Rule X5a, X5b and X5c
  360. var resolvedIsolate = _originalTypes[i];
  361. if (resolvedIsolate == Directionality.FSI)
  362. {
  363. if (!_isolatePairs.TryGetValue(i, out var endOfIsolate))
  364. {
  365. endOfIsolate = _originalTypes.Length;
  366. }
  367. // Rule X5c
  368. if (ResolveEmbeddingLevel(_originalTypes.SubSlice(i + 1, endOfIsolate - (i + 1))) == 1)
  369. resolvedIsolate = Directionality.RLI;
  370. else
  371. resolvedIsolate = Directionality.LRI;
  372. }
  373. // Replace RLI's level with current embedding level
  374. var tos = _statusStack.Peek();
  375. _resolvedLevels[i] = tos.EmbeddingLevel;
  376. // Apply override
  377. if (tos.OverrideStatus != Directionality.ON)
  378. {
  379. _workingTypes[i] = tos.OverrideStatus;
  380. }
  381. // Work out new level
  382. sbyte newLevel;
  383. if (resolvedIsolate == Directionality.RLI)
  384. newLevel = (sbyte)((tos.EmbeddingLevel + 1) | 1);
  385. else
  386. newLevel = (sbyte)((tos.EmbeddingLevel + 2) & ~1);
  387. // Valid?
  388. if (newLevel <= maxStackDepth && overflowIsolateCount == 0 && overflowEmbeddingCount == 0)
  389. {
  390. validIsolateCount++;
  391. _statusStack.Push(new Status()
  392. {
  393. EmbeddingLevel = newLevel,
  394. OverrideStatus = Directionality.ON,
  395. IsolateStatus = true,
  396. });
  397. }
  398. else
  399. {
  400. overflowIsolateCount++;
  401. }
  402. break;
  403. }
  404. case Directionality.BN:
  405. {
  406. // Mentioned in rule X6 - "for all types besides ..., BN, ..."
  407. // no-op
  408. break;
  409. }
  410. default:
  411. {
  412. // Rule X6
  413. var tos = _statusStack.Peek();
  414. _resolvedLevels[i] = tos.EmbeddingLevel;
  415. if (tos.OverrideStatus != Directionality.ON)
  416. {
  417. _workingTypes[i] = tos.OverrideStatus;
  418. }
  419. break;
  420. }
  421. case Directionality.PDI:
  422. {
  423. // Rule X6a
  424. if (overflowIsolateCount > 0)
  425. {
  426. overflowIsolateCount--;
  427. }
  428. else if (validIsolateCount != 0)
  429. {
  430. overflowEmbeddingCount = 0;
  431. while (!_statusStack.Peek().IsolateStatus)
  432. _statusStack.Pop();
  433. _statusStack.Pop();
  434. validIsolateCount--;
  435. }
  436. var tos = _statusStack.Peek();
  437. _resolvedLevels[i] = tos.EmbeddingLevel;
  438. if (tos.OverrideStatus != Directionality.ON)
  439. {
  440. _workingTypes[i] = tos.OverrideStatus;
  441. }
  442. break;
  443. }
  444. case Directionality.PDF:
  445. {
  446. // Rule X7
  447. if (overflowIsolateCount == 0)
  448. {
  449. if (overflowEmbeddingCount > 0)
  450. {
  451. overflowEmbeddingCount--;
  452. }
  453. else
  454. {
  455. if (!_statusStack.Peek().IsolateStatus && _statusStack.Count >= 2)
  456. {
  457. _statusStack.Pop();
  458. }
  459. }
  460. }
  461. break;
  462. }
  463. case Directionality.B:
  464. {
  465. // Rule X8
  466. _resolvedLevels[i] = _paragraphEmbeddingLevel;
  467. break;
  468. }
  469. }
  470. }
  471. }
  472. /// <summary>
  473. /// Resolve the paragraph embedding level if not explicitly passed
  474. /// by the caller. Also used by rule X5c for FSI isolating sequences.
  475. /// </summary>
  476. /// <param name="data">The data to be evaluated</param>
  477. /// <returns>The resolved embedding level</returns>
  478. public sbyte ResolveEmbeddingLevel(Slice<Directionality> data)
  479. {
  480. // P2
  481. for (var i = 0; i < data.Length; ++i)
  482. {
  483. switch (data[i])
  484. {
  485. case Directionality.L:
  486. // P3
  487. return 0;
  488. case Directionality.AL:
  489. case Directionality.R:
  490. // P3
  491. return 1;
  492. case Directionality.FSI:
  493. case Directionality.LRI:
  494. case Directionality.RLI:
  495. // Skip isolate pairs
  496. // (Because we're working with a slice, we need to adjust the indicies
  497. // we're using for the isolatePairs map)
  498. if (_isolatePairs.TryGetValue(data.Start + i, out i))
  499. {
  500. i -= data.Start;
  501. }
  502. else
  503. {
  504. i = data.Length;
  505. }
  506. break;
  507. }
  508. }
  509. // P3
  510. return 0;
  511. }
  512. /// <summary>
  513. /// Build a map to the original data positions that excludes all
  514. /// the types defined by rule X9
  515. /// </summary>
  516. void BuildX9RemovalMap()
  517. {
  518. // Reserve room for the x9 map
  519. _X9Map.Length = _originalTypes.Length;
  520. if (_hasEmbeddings || _hasIsolates)
  521. {
  522. // Build a map the removes all x9 characters
  523. var j = 0;
  524. for (int i = 0; i < _originalTypes.Length; i++)
  525. {
  526. if (!IsRemovedByX9(_originalTypes[i]))
  527. {
  528. _X9Map[j++] = i;
  529. }
  530. }
  531. // Set the final length
  532. _X9Map.Length = j;
  533. }
  534. else
  535. {
  536. for (int i = 0, count = _originalTypes.Length; i < count; i++)
  537. {
  538. _X9Map[i] = i;
  539. }
  540. }
  541. }
  542. /// <summary>
  543. /// Find the original character index for an entry in the X9 map
  544. /// </summary>
  545. /// <param name="index">Index in the x9 removal map</param>
  546. /// <returns>Index to the original data</returns>
  547. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  548. int mapX9(int index)
  549. {
  550. //return index < _X9Map.Length ? _X9Map[index] : _originalTypes.Length;
  551. return _X9Map[index];
  552. }
  553. /// <summary>
  554. /// Provides information about a level run - a continuous
  555. /// sequence of equal levels.
  556. /// </summary>
  557. struct LevelRun
  558. {
  559. public LevelRun(int start, int length, int level, Directionality sos, Directionality eos)
  560. {
  561. this.start = start;
  562. this.length = length;
  563. this.level = level;
  564. this.sos = sos;
  565. this.eos = eos;
  566. }
  567. public int start;
  568. public int length;
  569. public int level;
  570. public Directionality sos;
  571. public Directionality eos;
  572. }
  573. /// <summary>
  574. /// Add a new level run
  575. /// </summary>
  576. /// <remarks>
  577. /// This method resolves the sos and eos values for the run
  578. /// and adds the run to the list
  579. /// /// </remarks>
  580. /// <param name="start">The index of the start of the run (in x9 removed units)</param>
  581. /// <param name="length">The length of the run (in x9 removed units)</param>
  582. /// <param name="level">The level of the run</param>
  583. void AddLevelRun(int start, int length, int level)
  584. {
  585. // Get original indicies to first and last character in this run
  586. int firstCharIndex = mapX9(start);
  587. int lastCharIndex = mapX9(start + length - 1);
  588. // Work out sos
  589. int i = firstCharIndex - 1;
  590. while (i >= 0 && IsRemovedByX9(_originalTypes[i]))
  591. i--;
  592. var prevLevel = i < 0 ? _paragraphEmbeddingLevel : _resolvedLevels[i];
  593. var sos = DirectionFromLevel(Math.Max(prevLevel, level));
  594. // Work out eos
  595. var lastType = _workingTypes[lastCharIndex];
  596. int nextLevel;
  597. if (lastType == Directionality.LRI || lastType == Directionality.RLI || lastType == Directionality.FSI)
  598. {
  599. nextLevel = _paragraphEmbeddingLevel;
  600. }
  601. else
  602. {
  603. i = lastCharIndex + 1;
  604. while (i < _originalTypes.Length && IsRemovedByX9(_originalTypes[i]))
  605. i++;
  606. nextLevel = i >= _originalTypes.Length ? _paragraphEmbeddingLevel : _resolvedLevels[i];
  607. }
  608. var eos = DirectionFromLevel(Math.Max(nextLevel, level));
  609. // Add the run
  610. _levelRuns.Add(new LevelRun(start, length, level, sos, eos));
  611. }
  612. /// <summary>
  613. /// Find all runs of the same level, populating the _levelRuns
  614. /// collection
  615. /// </summary>
  616. void FindLevelRuns()
  617. {
  618. int currentLevel = -1;
  619. int runStart = 0;
  620. for (int i = 0; i < _X9Map.Length; ++i)
  621. {
  622. int level = _resolvedLevels[mapX9(i)];
  623. if (level != currentLevel)
  624. {
  625. if (currentLevel != -1)
  626. {
  627. AddLevelRun(runStart, i - runStart, currentLevel);
  628. }
  629. currentLevel = level;
  630. runStart = i;
  631. }
  632. }
  633. // Don't forget the final level run
  634. if (currentLevel != -1)
  635. {
  636. AddLevelRun(runStart, _X9Map.Length - runStart, currentLevel);
  637. }
  638. }
  639. /// <summary>
  640. /// Given a character index, find the level run that starts at that position
  641. /// </summary>
  642. /// <param name="index">The index into the original (unmapped) data</param>
  643. /// <returns>The index of the run that starts at that index</returns>
  644. int FindRunForIndex(int index)
  645. {
  646. for (int i = 0; i < _levelRuns.Count; i++)
  647. {
  648. // Passed index is for the original non-x9 filtered data, however
  649. // the level run ranges are for the x9 filtered data. Convert before
  650. // comparing
  651. if (mapX9(_levelRuns[i].start) == index)
  652. return i;
  653. }
  654. throw new InvalidOperationException("Internal error");
  655. }
  656. /// <summary>
  657. /// Determine and the process all isolated run sequences
  658. /// </summary>
  659. void ProcessIsolatedRunSequences()
  660. {
  661. // Find all runs with the same level
  662. FindLevelRuns();
  663. // Process them one at a time by first building
  664. // a mapping using slices from the x9 map for each
  665. // run section that needs to be joined together to
  666. // form an complete run. That full run mapping
  667. // will be placed in _isolatedRunMapping and then
  668. // processed by ProcessIsolatedRunSequence().
  669. while (_levelRuns.Count > 0)
  670. {
  671. // Clear the mapping
  672. _isolatedRunMapping.Clear();
  673. // Combine mappings from this run and all runs that continue on from it
  674. var runIndex = 0;
  675. Directionality eos = _levelRuns[0].eos;
  676. Directionality sos = _levelRuns[0].sos;
  677. int level = _levelRuns[0].level;
  678. while (true)
  679. {
  680. // Get the run
  681. var r = _levelRuns[runIndex];
  682. // The eos of the isolating run is the eos of the
  683. // last level run that comprises it.
  684. eos = r.eos;
  685. // Remove this run as we've now processed it
  686. _levelRuns.RemoveAt(runIndex);
  687. // Add the x9 map indicies for the run range to the mapping
  688. // for this isolated run
  689. _isolatedRunMapping.Add(_X9Map.SubSlice(r.start, r.length));
  690. // Get the last character and see if it's an isolating run with a matching
  691. // PDI and concatenate that run to this one
  692. int lastCharacterIndex = _isolatedRunMapping[_isolatedRunMapping.Length - 1];
  693. var lastType = _originalTypes[lastCharacterIndex];
  694. if ((lastType == Directionality.LRI || lastType == Directionality.RLI || lastType == Directionality.FSI) &&
  695. _isolatePairs.TryGetValue(lastCharacterIndex, out var nextRunIndex))
  696. {
  697. // Find the continuing run index
  698. runIndex = FindRunForIndex(nextRunIndex);
  699. }
  700. else
  701. {
  702. break;
  703. }
  704. }
  705. // Process this isolated run
  706. ProcessIsolatedRunSequence(sos, eos, level);
  707. }
  708. }
  709. /// <summary>
  710. /// The level of the isolating run currently being processed
  711. /// </summary>
  712. int _runLevel;
  713. /// <summary>
  714. /// The direction of the isolating run currently being processed
  715. /// </summary>
  716. Directionality _runDirection;
  717. /// <summary>
  718. /// The length of the isolating run currently being processed
  719. /// </summary>
  720. int _runLength;
  721. /// <summary>
  722. /// A mapped slice of the resolved types for the isolating run currently
  723. /// being processed
  724. /// </summary>
  725. MappedSlice<Directionality> _runResolvedTypes;
  726. /// <summary>
  727. /// A mapped slice of the original types for the isolating run currently
  728. /// being processed
  729. /// </summary>
  730. MappedSlice<Directionality> _runOriginalTypes;
  731. /// <summary>
  732. /// A mapped slice of the run levels for the isolating run currently
  733. /// being processed
  734. /// </summary>
  735. MappedSlice<sbyte> _runLevels;
  736. /// <summary>
  737. /// A mapped slice of the paired bracket types of the isolating
  738. /// run currently being processed
  739. /// </summary>
  740. MappedSlice<PairedBracketType> _runPairedBracketTypes;
  741. /// <summary>
  742. /// A mapped slice of the paired bracket values of the isolating
  743. /// run currently being processed
  744. /// </summary>
  745. MappedSlice<int> _runPairedBracketValues;
  746. /// <summary>
  747. /// Process a single isolated run sequence, where the character sequence
  748. /// mapping is currently held in _isolatedRunMapping.
  749. /// </summary>
  750. void ProcessIsolatedRunSequence(Directionality sos, Directionality eos, int runLevel)
  751. {
  752. // Create mappings onto the underlying data
  753. _runResolvedTypes = new MappedSlice<Directionality>(_workingTypes, _isolatedRunMapping.AsSlice());
  754. _runOriginalTypes = new MappedSlice<Directionality>(_originalTypes, _isolatedRunMapping.AsSlice());
  755. _runLevels = new MappedSlice<sbyte>(_resolvedLevels, _isolatedRunMapping.AsSlice());
  756. if (_hasBrackets)
  757. {
  758. _runPairedBracketTypes = new MappedSlice<PairedBracketType>(_pairedBracketTypes, _isolatedRunMapping.AsSlice());
  759. _runPairedBracketValues = new MappedSlice<int>(_pairedBracketValues, _isolatedRunMapping.AsSlice());
  760. }
  761. _runLevel = runLevel;
  762. _runDirection = DirectionFromLevel(runLevel);
  763. _runLength = _runResolvedTypes.Length;
  764. // By tracking the types of characters known to be in the current run, we can
  765. // skip some of the rules that we know won't apply. The flags will be
  766. // initialized while we're processing rule W1 below.
  767. bool hasEN = false;
  768. bool hasAL = false;
  769. bool hasES = false;
  770. bool hasCS = false;
  771. bool hasAN = false;
  772. bool hasET = false;
  773. // Rule W1
  774. // Also, set hasXX flags
  775. int i;
  776. var prevType = sos;
  777. for (i = 0; i < _runLength; i++)
  778. {
  779. var t = _runResolvedTypes[i];
  780. switch (t)
  781. {
  782. case Directionality.NSM:
  783. _runResolvedTypes[i] = prevType;
  784. break;
  785. case Directionality.LRI:
  786. case Directionality.RLI:
  787. case Directionality.FSI:
  788. case Directionality.PDI:
  789. prevType = Directionality.ON;
  790. break;
  791. case Directionality.EN:
  792. hasEN = true;
  793. prevType = t;
  794. break;
  795. case Directionality.AL:
  796. hasAL = true;
  797. prevType = t;
  798. break;
  799. case Directionality.ES:
  800. hasES = true;
  801. prevType = t;
  802. break;
  803. case Directionality.CS:
  804. hasCS = true;
  805. prevType = t;
  806. break;
  807. case Directionality.AN:
  808. hasAN = true;
  809. prevType = t;
  810. break;
  811. case Directionality.ET:
  812. hasET = true;
  813. prevType = t;
  814. break;
  815. default:
  816. prevType = t;
  817. break;
  818. }
  819. }
  820. // Rule W2
  821. if (hasEN)
  822. {
  823. for (i = 0; i < _runLength; i++)
  824. {
  825. if (_runResolvedTypes[i] == Directionality.EN)
  826. {
  827. for (int j = i - 1; j >= 0; j--)
  828. {
  829. var t = _runResolvedTypes[j];
  830. if (t == Directionality.L || t == Directionality.R || t == Directionality.AL)
  831. {
  832. if (t == Directionality.AL)
  833. {
  834. _runResolvedTypes[i] = Directionality.AN;
  835. hasAN = true;
  836. }
  837. break;
  838. }
  839. }
  840. }
  841. }
  842. }
  843. // Rule W3
  844. if (hasAL)
  845. {
  846. for (i = 0; i < _runLength; i++)
  847. {
  848. if (_runResolvedTypes[i] == Directionality.AL)
  849. {
  850. _runResolvedTypes[i] = Directionality.R;
  851. }
  852. }
  853. }
  854. // Rule W4
  855. if ((hasES || hasCS) && (hasEN || hasAN))
  856. {
  857. for (i = 1; i < _runLength - 1; ++i)
  858. {
  859. ref var rt = ref _runResolvedTypes[i];
  860. if (rt == Directionality.ES)
  861. {
  862. var prevSepType = _runResolvedTypes[i - 1];
  863. var succSepType = _runResolvedTypes[i + 1];
  864. if (prevSepType == Directionality.EN && succSepType == Directionality.EN)
  865. {
  866. // ES between EN and EN
  867. rt = Directionality.EN;
  868. }
  869. }
  870. else if (rt == Directionality.CS)
  871. {
  872. var prevSepType = _runResolvedTypes[i - 1];
  873. var succSepType = _runResolvedTypes[i + 1];
  874. if ((prevSepType == Directionality.AN && succSepType == Directionality.AN) ||
  875. (prevSepType == Directionality.EN && succSepType == Directionality.EN))
  876. {
  877. // CS between (AN and AN) or (EN and EN)
  878. rt = prevSepType;
  879. }
  880. }
  881. }
  882. }
  883. // Rule W5
  884. if (hasET && hasEN)
  885. {
  886. for (i = 0; i < _runLength; ++i)
  887. {
  888. if (_runResolvedTypes[i] == Directionality.ET)
  889. {
  890. // Locate end of sequence
  891. int seqStart = i;
  892. int seqEnd = i;
  893. while (seqEnd < _runLength && _runResolvedTypes[seqEnd] == Directionality.ET)
  894. seqEnd++;
  895. // Preceeded by, or followed by EN?
  896. if ((seqStart == 0 ? sos : _runResolvedTypes[seqStart - 1]) == Directionality.EN
  897. || (seqEnd == _runLength ? eos : _runResolvedTypes[seqEnd]) == Directionality.EN)
  898. {
  899. // Change the entire range
  900. for (int j = seqStart; i < seqEnd; ++i)
  901. {
  902. _runResolvedTypes[i] = Directionality.EN;
  903. }
  904. }
  905. // continue at end of sequence
  906. i = seqEnd;
  907. }
  908. }
  909. }
  910. // Rule W6
  911. if (hasES || hasET || hasCS)
  912. {
  913. for (i = 0; i < _runLength; ++i)
  914. {
  915. ref var t = ref _runResolvedTypes[i];
  916. if (t == Directionality.ES || t == Directionality.ET || t == Directionality.CS)
  917. {
  918. t = Directionality.ON;
  919. }
  920. }
  921. }
  922. // Rule W7.
  923. if (hasEN)
  924. {
  925. var prevStrongType = sos;
  926. for (i = 0; i < _runLength; ++i)
  927. {
  928. ref var rt = ref _runResolvedTypes[i];
  929. if (rt == Directionality.EN)
  930. {
  931. // If prev strong type was an L change this to L too
  932. if (prevStrongType == Directionality.L)
  933. {
  934. _runResolvedTypes[i] = Directionality.L;
  935. }
  936. }
  937. // Remember previous strong type (NB: AL should already be changed to R)
  938. if (rt == Directionality.L || rt == Directionality.R)
  939. {
  940. prevStrongType = rt;
  941. }
  942. }
  943. }
  944. // Rule N0 - process bracket pairs
  945. if (_hasBrackets)
  946. {
  947. int count;
  948. var pairedBrackets = LocatePairedBrackets();
  949. for (i = 0, count = pairedBrackets.Count; i < count; i++)
  950. {
  951. var pb = pairedBrackets[i];
  952. var dir = InspectPairedBracket(pb);
  953. // Case "d" - no strong types in the brackets, ignore
  954. if (dir == Directionality.ON)
  955. {
  956. continue;
  957. }
  958. // Case "b" - strong type found that matches the embedding direction
  959. if ((dir == Directionality.L || dir == Directionality.R) && dir == _runDirection)
  960. {
  961. SetPairedBracketDirection(pb, dir);
  962. continue;
  963. }
  964. // Case "c" - found opposite strong type found, look before to establish context
  965. dir = InspectBeforePairedBracket(pb, sos);
  966. if (dir == _runDirection || dir == Directionality.ON)
  967. {
  968. dir = _runDirection;
  969. }
  970. SetPairedBracketDirection(pb, dir);
  971. }
  972. }
  973. // Rules N1 and N2 - resolve neutral types
  974. for (i = 0; i < _runLength; ++i)
  975. {
  976. var t = _runResolvedTypes[i];
  977. if (IsNeutralType(t))
  978. {
  979. // Locate end of sequence
  980. int seqStart = i;
  981. int seqEnd = i;
  982. while (seqEnd < _runLength && IsNeutralType(_runResolvedTypes[seqEnd]))
  983. seqEnd++;
  984. // Work out the preceding type
  985. Directionality typeBefore;
  986. if (seqStart == 0)
  987. {
  988. typeBefore = sos;
  989. }
  990. else
  991. {
  992. typeBefore = _runResolvedTypes[seqStart - 1];
  993. if (typeBefore == Directionality.AN || typeBefore == Directionality.EN)
  994. {
  995. typeBefore = Directionality.R;
  996. }
  997. }
  998. // Work out the following type
  999. Directionality typeAfter;
  1000. if (seqEnd == _runLength)
  1001. {
  1002. typeAfter = eos;
  1003. }
  1004. else
  1005. {
  1006. typeAfter = _runResolvedTypes[seqEnd];
  1007. if (typeAfter == Directionality.AN || typeAfter == Directionality.EN)
  1008. {
  1009. typeAfter = Directionality.R;
  1010. }
  1011. }
  1012. // Work out the final resolved type
  1013. Directionality resolvedType;
  1014. if (typeBefore == typeAfter)
  1015. {
  1016. // Rule N1
  1017. resolvedType = typeBefore;
  1018. }
  1019. else
  1020. {
  1021. // Rule N2
  1022. resolvedType = _runDirection;
  1023. }
  1024. // Apply changes
  1025. for (int j = seqStart; j < seqEnd; j++)
  1026. {
  1027. _runResolvedTypes[j] = resolvedType;
  1028. }
  1029. // continue after this run
  1030. i = seqEnd;
  1031. }
  1032. }
  1033. // Rules I1 and I2 - resolve implicit types
  1034. if ((_runLevel & 0x01) == 0)
  1035. {
  1036. // Rule I1 - even
  1037. for (i = 0; i < _runLength; i++)
  1038. {
  1039. var t = _runResolvedTypes[i];
  1040. ref var l = ref _runLevels[i];
  1041. if (t == Directionality.R)
  1042. l++;
  1043. else if (t == Directionality.AN || t == Directionality.EN)
  1044. l += 2;
  1045. }
  1046. }
  1047. else
  1048. {
  1049. // Rule I2 - odd
  1050. for (i = 0; i < _runLength; i++)
  1051. {
  1052. var t = _runResolvedTypes[i];
  1053. ref var l = ref _runLevels[i];
  1054. if (t != Directionality.R)
  1055. l++;
  1056. }
  1057. }
  1058. }
  1059. /// <summary>
  1060. /// IComparer for BracketPairs
  1061. /// </summary>
  1062. class PairedBracketComparer : IComparer<BracketPair>
  1063. {
  1064. int IComparer<BracketPair>.Compare(BracketPair x, BracketPair y)
  1065. {
  1066. return x.OpeningIndex - y.OpeningIndex;
  1067. }
  1068. }
  1069. /// <summary>
  1070. /// An shared instance of the PairedBracket comparer
  1071. /// </summary>
  1072. static PairedBracketComparer _pairedBracketComparer = new PairedBracketComparer();
  1073. /// <summary>
  1074. /// Maximum pairing depth for paired brackets
  1075. /// </summary>
  1076. const int MaxPairedBracketDepth = 63;
  1077. /// <summary>
  1078. /// Re-useable list of pending opening brackets used by the
  1079. /// LocatePairedBrackets method
  1080. /// </summary>
  1081. List<int> _pendingOpeningBrackets = new List<int>();
  1082. /// <summary>
  1083. /// Resolved list of paired brackets
  1084. /// </summary>
  1085. List<BracketPair> _pairedBrackets = new List<BracketPair>();
  1086. /// <summary>
  1087. /// Locate all pair brackets in the current isolating run
  1088. /// </summary>
  1089. /// <returns>A sorted list of BracketPairs</returns>
  1090. List<BracketPair> LocatePairedBrackets()
  1091. {
  1092. // Clear work collections
  1093. _pendingOpeningBrackets.Clear();
  1094. _pairedBrackets.Clear();
  1095. // Since List.Sort is expensive on memory if called often (it internally
  1096. // allocates an ArraySorted object) and since we will rarely have many
  1097. // items in this list (most paragraphs will only have a handful of bracket
  1098. // pairs - if that), we use a simple linear lookup and insert most of the
  1099. // time. If there are more that `sortLimit` paired brackets we abort th
  1100. // linear searching/inserting and using List.Sort at the end.
  1101. const int sortLimit = 8;
  1102. // Process all characters in the run, looking for paired brackets
  1103. for (int ich = 0, length = _runLength; ich < length; ich++)
  1104. {
  1105. // Ignore non-neutral characters
  1106. if (_runResolvedTypes[ich] != Directionality.ON)
  1107. continue;
  1108. switch (_runPairedBracketTypes[ich])
  1109. {
  1110. case PairedBracketType.o:
  1111. if (_pendingOpeningBrackets.Count == MaxPairedBracketDepth)
  1112. goto exit;
  1113. _pendingOpeningBrackets.Insert(0, ich);
  1114. break;
  1115. case PairedBracketType.c:
  1116. // see if there is a match
  1117. for (int i = 0; i < _pendingOpeningBrackets.Count; i++)
  1118. {
  1119. if (_runPairedBracketValues[ich] == _runPairedBracketValues[_pendingOpeningBrackets[i]])
  1120. {
  1121. // Add this paired bracket set
  1122. var opener = _pendingOpeningBrackets[i];
  1123. if (_pairedBrackets.Count < sortLimit)
  1124. {
  1125. int ppi = 0;
  1126. while (ppi < _pairedBrackets.Count && _pairedBrackets[ppi].OpeningIndex < opener)
  1127. {
  1128. ppi++;
  1129. }
  1130. _pairedBrackets.Insert(ppi, new BracketPair(opener, ich));
  1131. }
  1132. else
  1133. {
  1134. _pairedBrackets.Add(new BracketPair(opener, ich));
  1135. }
  1136. // remove up to and including matched opener
  1137. _pendingOpeningBrackets.RemoveRange(0, i + 1);
  1138. break;
  1139. }
  1140. }
  1141. break;
  1142. }
  1143. }
  1144. exit:
  1145. // Is a sort pending?
  1146. if (_pairedBrackets.Count > sortLimit)
  1147. _pairedBrackets.Sort(_pairedBracketComparer);
  1148. return _pairedBrackets;
  1149. }
  1150. /// <summary>
  1151. /// Inspect a paired bracket set and determine its strong direction
  1152. /// </summary>
  1153. /// <param name="pb">The paired bracket to be inpected</param>
  1154. /// <returns>The direction of the bracket set content</returns>
  1155. Directionality InspectPairedBracket(BracketPair pb)
  1156. {
  1157. var dirEmbed = DirectionFromLevel(_runLevel);
  1158. var dirOpposite = Directionality.ON;
  1159. for (int ich = pb.OpeningIndex + 1; ich < pb.ClosingIndex; ich++)
  1160. {
  1161. var dir = GetStrongTypeN0(_runResolvedTypes[ich]);
  1162. if (dir == Directionality.ON)
  1163. continue;
  1164. if (dir == dirEmbed)
  1165. return dir;
  1166. dirOpposite = dir;
  1167. }
  1168. return dirOpposite;
  1169. }
  1170. /// <summary>
  1171. /// Look for a strong type before a paired bracket
  1172. /// </summary>
  1173. /// <param name="pb">The paired bracket set to be inspected</param>
  1174. /// <param name="sos">The sos in case nothing found before the bracket</param>
  1175. /// <returns>The strong direction before the brackets</returns>
  1176. Directionality InspectBeforePairedBracket(BracketPair pb, Directionality sos)
  1177. {
  1178. for (int ich = pb.OpeningIndex - 1; ich >= 0; --ich)
  1179. {
  1180. var dir = GetStrongTypeN0(_runResolvedTypes[ich]);
  1181. if (dir != Directionality.ON)
  1182. return dir;
  1183. }
  1184. return sos;
  1185. }
  1186. /// <summary>
  1187. /// Sets the direction of a bracket pair, including setting the direction of
  1188. /// NSM's inside the brackets and following.
  1189. /// </summary>
  1190. /// <param name="pb">The paired brackets</param>
  1191. /// <param name="dir">The resolved direction for the bracket pair</param>
  1192. void SetPairedBracketDirection(BracketPair pb, Directionality dir)
  1193. {
  1194. // Set the direction of the brackets
  1195. _runResolvedTypes[pb.OpeningIndex] = dir;
  1196. _runResolvedTypes[pb.ClosingIndex] = dir;
  1197. // Set the directionality of NSM's inside the brackets
  1198. for (int i = pb.OpeningIndex + 1; i < pb.ClosingIndex; i++)
  1199. {
  1200. if (_runOriginalTypes[i] == Directionality.NSM)
  1201. _runOriginalTypes[i] = dir;
  1202. else
  1203. break;
  1204. }
  1205. // Set the directionality of NSM's following the brackets
  1206. for (int i = pb.ClosingIndex + 1; i < _runLength; i++)
  1207. {
  1208. if (_runOriginalTypes[i] == Directionality.NSM)
  1209. _runResolvedTypes[i] = dir;
  1210. else
  1211. break;
  1212. }
  1213. }
  1214. /// <summary>
  1215. /// Hold the start and end index of a pair of brackets
  1216. /// </summary>
  1217. readonly struct BracketPair
  1218. {
  1219. /// <summary>
  1220. /// Index of the opening bracket
  1221. /// </summary>
  1222. public readonly int OpeningIndex;
  1223. /// <summary>
  1224. /// Index of the closing bracket
  1225. /// </summary>
  1226. public readonly int ClosingIndex;
  1227. /// <summary>
  1228. /// Constructs a new paired bracket
  1229. /// </summary>
  1230. /// <param name="openingIndex">Index of the opening bracket</param>
  1231. /// <param name="closingIndex">Index of the closing bracket</param>
  1232. public BracketPair(int openingIndex, int closingIndex)
  1233. {
  1234. this.OpeningIndex = openingIndex;
  1235. this.ClosingIndex = closingIndex;
  1236. }
  1237. }
  1238. /// <summary>
  1239. /// Resets whitespace levels. Implements rule L1
  1240. /// </summary>
  1241. void ResetWhitespaceLevels()
  1242. {
  1243. for (int i = 0; i < _resolvedLevels.Length; i++)
  1244. {
  1245. var t = _originalTypes[i];
  1246. if (t == Directionality.B || t == Directionality.S)
  1247. {
  1248. // Rule L1, clauses one and two.
  1249. _resolvedLevels[i] = _paragraphEmbeddingLevel;
  1250. // Rule L1, clause three.
  1251. for (int j = i - 1; j >= 0; --j)
  1252. {
  1253. if (IsWhitespace(_originalTypes[j]))
  1254. { // including format
  1255. // codes
  1256. _resolvedLevels[j] = _paragraphEmbeddingLevel;
  1257. }
  1258. else
  1259. {
  1260. break;
  1261. }
  1262. }
  1263. }
  1264. }
  1265. // Rule L1, clause four.
  1266. for (int j = _resolvedLevels.Length - 1; j >= 0; j--)
  1267. {
  1268. if (IsWhitespace(_originalTypes[j]))
  1269. { // including format codes
  1270. _resolvedLevels[j] = _paragraphEmbeddingLevel;
  1271. }
  1272. else
  1273. {
  1274. break;
  1275. }
  1276. }
  1277. }
  1278. /// <summary>
  1279. /// Assign levels to any characters that would be have been
  1280. /// removed by rule X9. The idea is to keep level runs together
  1281. /// that would otherwise be broken by an interfering isolate/embedding
  1282. /// control character.
  1283. /// </summary>
  1284. void AssignLevelsToCodePointsRemovedByX9()
  1285. {
  1286. // Redundant?
  1287. if (!_hasIsolates && !_hasEmbeddings)
  1288. return;
  1289. // No-op?
  1290. if (_workingTypes.Length == 0)
  1291. return;
  1292. // Fix up first character
  1293. if (_resolvedLevels[0] < 0)
  1294. _resolvedLevels[0] = _paragraphEmbeddingLevel;
  1295. if (IsRemovedByX9(_originalTypes[0]))
  1296. _workingTypes[0] = _originalTypes[0];
  1297. for (int i = 1, length = _workingTypes.Length; i < length; i++)
  1298. {
  1299. var t = _originalTypes[i];
  1300. if (IsRemovedByX9(t))
  1301. {
  1302. _workingTypes[i] = t;
  1303. _resolvedLevels[i] = _resolvedLevels[i - 1];
  1304. }
  1305. }
  1306. }
  1307. /// <summary>
  1308. /// Check if a directionality type represents whitepsace
  1309. /// </summary>
  1310. /// <param name="biditype"></param>
  1311. /// <returns></returns>
  1312. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1313. static bool IsWhitespace(Directionality biditype)
  1314. {
  1315. switch (biditype)
  1316. {
  1317. case Directionality.LRE:
  1318. case Directionality.RLE:
  1319. case Directionality.LRO:
  1320. case Directionality.RLO:
  1321. case Directionality.PDF:
  1322. case Directionality.LRI:
  1323. case Directionality.RLI:
  1324. case Directionality.FSI:
  1325. case Directionality.PDI:
  1326. case Directionality.BN:
  1327. case Directionality.WS:
  1328. return true;
  1329. default:
  1330. return false;
  1331. }
  1332. }
  1333. /// <summary>
  1334. /// Convert a level to a direction where odd is RTL and
  1335. /// even is LTR
  1336. /// </summary>
  1337. /// <param name="level">The level to convert</param>
  1338. /// <returns>A directionality</returns>
  1339. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1340. static Directionality DirectionFromLevel(int level)
  1341. {
  1342. return ((level & 0x1) == 0) ? Directionality.L : Directionality.R;
  1343. }
  1344. /// <summary>
  1345. /// Helper to check if a directionality is removed by rule X9
  1346. /// </summary>
  1347. /// <param name="biditype">The bidi type to check</param>
  1348. /// <returns>True if rule X9 would remove this character; otherwise false</returns>
  1349. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1350. public static bool IsRemovedByX9(Directionality biditype)
  1351. {
  1352. switch (biditype)
  1353. {
  1354. case Directionality.LRE:
  1355. case Directionality.RLE:
  1356. case Directionality.LRO:
  1357. case Directionality.RLO:
  1358. case Directionality.PDF:
  1359. case Directionality.BN:
  1360. return true;
  1361. default:
  1362. return false;
  1363. }
  1364. }
  1365. /// <summary>
  1366. /// Check if a a directionality is neutral for rules N1 and N2
  1367. /// </summary>
  1368. /// <param name="dir"></param>
  1369. /// <returns></returns>
  1370. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1371. bool IsNeutralType(Directionality dir)
  1372. {
  1373. switch (dir)
  1374. {
  1375. case Directionality.B:
  1376. case Directionality.S:
  1377. case Directionality.WS:
  1378. case Directionality.ON:
  1379. case Directionality.RLI:
  1380. case Directionality.LRI:
  1381. case Directionality.FSI:
  1382. case Directionality.PDI:
  1383. return true;
  1384. }
  1385. return false;
  1386. }
  1387. /// <summary>
  1388. /// Maps a direction to a strong type for rule N0
  1389. /// </summary>
  1390. /// <param name="dir">The direction to map</param>
  1391. /// <returns>A strong direction - R, L or ON</returns>
  1392. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1393. Directionality GetStrongTypeN0(Directionality dir)
  1394. {
  1395. switch (dir)
  1396. {
  1397. case Directionality.EN:
  1398. case Directionality.AN:
  1399. case Directionality.AL:
  1400. case Directionality.R:
  1401. return Directionality.R;
  1402. case Directionality.L:
  1403. return Directionality.L;
  1404. default:
  1405. return Directionality.ON;
  1406. }
  1407. }
  1408. }
  1409. }