Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
BiDiLayoutEngine.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <algorithm>
7
9#include "Stroika/Foundation/Containers/Support/ReserveTweaks.h"
10
11#include "BiDiLayoutEngine.h"
12
13#if qTryToUseUNISCRIBEForTextRuns
14#include <Usp10.h>
15#endif
16
17#if qUseFriBidi
18#include "fribidi.h"
19#endif
20
21#if qUseICUBidi
22#include "unicode/ubidi.h"
23#endif
24
25/*
26 * Hack to build BiDiLevelProc for SPR#1236
27 */
28#ifndef qBuildMemoizedBiDiLevelProc
29#define qBuildMemoizedBiDiLevelProc 0
30#endif
31
32#if qBuildMemoizedBiDiLevelProc
33#include <fstream>
34#endif
35
36using std::byte;
37
38using namespace Stroika::Foundation;
39
40using namespace Stroika::Frameworks;
41using namespace Stroika::Frameworks::Led;
42
43/*
44 * This hack reverses the direction results returned by this class for basic text parsing/layout. This is
45 * useful to debug/test - cuz then I can see ENGLISH text (words) displayed RTL (right to left) - which I can
46 * better identify and see if they are working properly.
47 *
48 * Note that this ONLY reverses directions WITHIN a run - not the order of the runs. Thats still good enough
49 * to debug most display issues.
50 */
51#ifndef qDebugHack_ReverseDirections
52#define qDebugHack_ReverseDirections 0
53#endif
54
55/*
56 * This hack treats all english aplhabetic upper case characters as RTL characters, and lower case characters
57 * as LTR characters. This hack COULD be improved to actually PRESERVE the original characters in the virtual
58 * output, but that hasn't been done yet.
59 */
60#ifndef qDebugHack_UpperCaseCharsTratedAsRTL
61#define qDebugHack_UpperCaseCharsTratedAsRTL 0
62#endif
63
64/*
65 * Code to test how FriBidi does compared with UNISCRIBE for laying out Arabic text. for plain English text
66 * these are the same. But - even simple Arabic files seem to produce different results between FriBidi and
67 * UNISCRIBE. For example - an Arabic string which ends in a period - with FriBidi - it all ends up as one
68 * LTR run. But - with UNISCRIBE - you get two runs - and the period comes out on the right.
69 */
70#ifndef qTestUNISCRIBEResultsEqualFriBidi
71#if qStroika_Foundation_Debug_AssertionsChecked && qTryToUseUNISCRIBEForTextRuns && qUseFriBidi
72// #define qTestUNISCRIBEResultsEqualFriBidi 0
73#define qTestUNISCRIBEResultsEqualFriBidi 1
74#endif
75#endif
76
77#if qTryToUseUNISCRIBEForTextRuns
78enum InitialUNISCRIBEDir {
79 eLTRChar,
80 eRTLChar,
81 eNeutralChar
82};
83static InitialUNISCRIBEDir myGetInitialUNISCRIBEDir (wchar_t c);
84#endif
85
86/*
87 * Use this to test my myGetInitialUNISCRIBEDir function produce the right results. (SPR#1236).
88 */
89#ifndef qTestMyWriteMemoizedUniscribeDirFunction
90#define qTestMyWriteMemoizedUniscribeDirFunction (qUseFriBidi && qTryToUseUNISCRIBEForTextRuns)
91#endif
92
93#if qTestMyWriteMemoizedUniscribeDirFunction || qBuildMemoizedBiDiLevelProc
94namespace {
95 InitialUNISCRIBEDir GetInitialUNISCRIBEDir (wchar_t c)
96 {
97 FriBidiCharType ct = fribidi_get_type (c);
98 if (FRIBIDI_IS_LETTER (ct)) {
99 if (FRIBIDI_DIR_TO_LEVEL (ct) & 1) {
100 return eRTLChar;
101 }
102 else {
103 return eLTRChar;
104 }
105 }
106 else {
107 return eNeutralChar;
108 }
109 }
110};
111#endif
112
113#if qBuildMemoizedBiDiLevelProc
114namespace {
115 template <class FUNCTION>
116 void WriteMemoizedUniscribeDirProc (FUNCTION function, const string& origFunctionName, const string& functionName)
117 {
118 ofstream outStream ("IsXXXProc.txt");
119
120 outStream << "/*\n";
121 outStream << " ********************************************************************************\n";
122 outStream << " ********** " << functionName << " (AUTOGENERATED memoize of " << origFunctionName << " - " << __DATE__ << ") ***********\n";
123 outStream << " ********************************************************************************\n";
124 outStream << " */\n";
125 outStream << "// Hack for SPR#1236\n";
126 outStream << "InitialUNISCRIBEDir " << functionName << " (wchar_t c)\n";
127 outStream << "{\n";
128
129 bool firstTime = true;
130 InitialUNISCRIBEDir lastIST = eNeutralChar;
131 size_t firstRangeIdxTrue = 0;
132 for (size_t i = 0; i < 256 * 256; ++i) {
133 InitialUNISCRIBEDir isT = function (static_cast<wchar_t> (i));
134 if (i == 0) {
135 lastIST = isT;
136 }
137 if (isT != lastIST or i >= 256 * 256 - 1) {
138 if (lastIST != eNeutralChar) {
139 // then emit the range...
140 outStream << "\tif (" << firstRangeIdxTrue << " <= c && c <= " << i - 1 << ") {\n";
141 switch (lastIST) {
142 case eNeutralChar:
143 outStream << "\t\treturn eNeutralChar;\n";
144 break;
145 case eRTLChar:
146 outStream << "\t\treturn eRTLChar;\n";
147 break;
148 case eLTRChar:
149 outStream << "\t\treturn eLTRChar;\n";
150 break;
151 }
152 outStream << "\t}\n";
153 }
154 lastIST = isT;
155 firstRangeIdxTrue = i;
156 }
157 }
158 outStream << "\treturn eNeutralChar;\n";
159 outStream << "}\n";
160 outStream << "\n";
161 }
162
163 struct DoRunIt {
164 DoRunIt ()
165 {
166 WriteMemoizedUniscribeDirProc (GetInitialUNISCRIBEDir, "GetInitialUNISCRIBEDir", "myGetInitialUNISCRIBEDir");
167 }
168 } gRunIt;
169};
170#endif
171
172#if qTestMyWriteMemoizedUniscribeDirFunction
173class MyIWriteMemoizedUniscribeDirFunction {
174public:
175 MyIWriteMemoizedUniscribeDirFunction ()
176 {
177 for (wchar_t c = 0; c < 0xffff; ++c) {
178 Assert (GetInitialUNISCRIBEDir (c) == myGetInitialUNISCRIBEDir (c));
179 }
180 }
181} aMyIWriteMemoizedUniscribeDirFunction;
182#endif
183
184#if qStroika_Foundation_Common_Platform_Windows && qTryToUseUNISCRIBEForTextRuns
185namespace {
186
187 /*
188 * Use LoadLibrary/GetProcAddress instead of direct call to avoid having to link with
189 * Usp10.lib. This avoidance allows us to run on systems that don't it installed.
190 */
191 struct UniscribeDLL {
192 UniscribeDLL ()
193 : fDLL (::LoadLibrary (_T ("Usp10.dll")))
194 , fScriptItemize (nullptr)
195 , fScriptLayout (nullptr)
196 {
197 if (fDLL != nullptr) {
198 fScriptItemize = (HRESULT (WINAPI*) (const WCHAR*, int, int, const SCRIPT_CONTROL*, const SCRIPT_STATE*, SCRIPT_ITEM*,
199 int*)) (::GetProcAddress (fDLL, "ScriptItemize"));
200 fScriptLayout = (HRESULT (WINAPI*) (int, const BYTE*, int*, int*)) (::GetProcAddress (fDLL, "ScriptLayout"));
201 }
202 }
203
204 ~UniscribeDLL ()
205 {
206 if (fDLL != nullptr) {
207 Verify (::FreeLibrary (fDLL));
208 }
209 }
210
211 nonvirtual bool IsAvail () const
212 {
213 return fDLL != nullptr;
214 }
215
216 HRESULT WINAPI ScriptItemize (const WCHAR* pwcInChars, int cInChars, int cMaxItems, const SCRIPT_CONTROL* psControl,
217 const SCRIPT_STATE* psState, SCRIPT_ITEM* pItems, int* pcItems)
218 {
219 if (fScriptItemize == nullptr) {
220 return E_FAIL;
221 }
222 return (*fScriptItemize) (pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, pcItems);
223 }
224
225 HRESULT WINAPI ScriptLayout (int cRuns, const BYTE* pbLevel, int* piVisualToLogical, int* piLogicalToVisual)
226 {
227 if (fScriptLayout == nullptr) {
228 return E_FAIL;
229 }
230 return (*fScriptLayout) (cRuns, pbLevel, piVisualToLogical, piLogicalToVisual);
231 }
232
233 HINSTANCE fDLL;
234 HRESULT (WINAPI* fScriptItemize)
235 (const WCHAR*, int, int, const SCRIPT_CONTROL*, const SCRIPT_STATE*, SCRIPT_ITEM*, int*);
236 HRESULT (WINAPI* fScriptLayout)
237 (int, const BYTE*, int*, int*);
238 };
239 static UniscribeDLL sUniscribeDLL;
240};
241#endif
242
243/*
244 ********************************************************************************
245 ******************************** TextLayoutBlock *******************************
246 ********************************************************************************
247 */
248/*
249@METHOD: TextLayoutBlock::GetCharacterDirection
250@DESCRIPTION:
251*/
252TextDirection TextLayoutBlock::GetCharacterDirection (size_t realCharOffset) const
253{
254 vector<ScriptRunElt> runs = GetScriptRuns ();
255 for (auto i = runs.begin (); i != runs.end (); ++i) {
256 if ((*i).fRealStart <= realCharOffset and realCharOffset < (*i).fRealEnd) {
257 return (*i).fDirection;
258 }
259 }
260 Assert (false);
261 return eLeftToRight;
262}
263
264/*
265@METHOD: TextLayoutBlock::MapRealOffsetToVirtual
266@DESCRIPTION:
267*/
268size_t TextLayoutBlock::MapRealOffsetToVirtual (size_t /*i*/) const
269{
270 Assert (false); //NYI
271 return 0;
272}
273
274/*
275@METHOD: TextLayoutBlock::MapVirtualOffsetToReal
276@DESCRIPTION:
277*/
278size_t TextLayoutBlock::MapVirtualOffsetToReal (size_t i) const
279{
280 Require (i < GetTextLength ());
281 vector<ScriptRunElt> runs = GetScriptRuns ();
282 for (auto runIt = runs.begin (); runIt != runs.end (); ++runIt) {
283 const ScriptRunElt& se = *runIt;
284 if (se.fVirtualStart <= i and i < se.fVirtualEnd) {
285 if (se.fDirection == eLeftToRight) {
286 size_t result = (i - se.fVirtualStart) + se.fRealStart;
287 Ensure (result < GetTextLength ());
288 return (result);
289 }
290 else {
291 size_t segLen = se.fRealEnd - se.fRealStart;
292 size_t result = (segLen - (i - se.fVirtualStart)) - 1 + se.fRealStart;
293 Ensure (result < GetTextLength ());
294 return (result);
295 }
296 }
297 }
298 Assert (false); // BAD INDEX
299 return 0;
300}
301
302void TextLayoutBlock::CopyOutRealText (const ScriptRunElt& scriptRunElt, Led_tChar* buf) const
303{
304 const Led_tChar* s = nullptr;
305 const Led_tChar* e = nullptr;
306 PeekAtRealText (scriptRunElt, &s, &e);
307 copy (s, e, buf);
308}
309
310void TextLayoutBlock::CopyOutVirtualText (const ScriptRunElt& scriptRunElt, Led_tChar* buf) const
311{
312 const Led_tChar* s = nullptr;
313 const Led_tChar* e = nullptr;
314 PeekAtVirtualText (scriptRunElt, &s, &e);
315 copy (s, e, buf);
316}
317
318void TextLayoutBlock::PeekAtRealText (const ScriptRunElt& scriptRunElt, const Led_tChar** startText, const Led_tChar** endText) const
319{
320 RequireNotNull (startText);
321 RequireNotNull (endText);
322 PeekAtRealText_ (startText, endText);
323 *startText += scriptRunElt.fRealStart;
324 size_t len = scriptRunElt.fRealEnd - scriptRunElt.fRealStart;
325 Assert (*endText - *startText >= static_cast<ptrdiff_t> (len)); // make sure we are SHRINKING the text and not making it point past its end
326 *endText = *startText + len;
327}
328
329Led_tString TextLayoutBlock::GetRealText () const
330{
331 const Led_tChar* s = nullptr;
332 const Led_tChar* e = nullptr;
333 PeekAtRealText_ (&s, &e);
334 return Led_tString{s, e};
335}
336
337Led_tString TextLayoutBlock::GetRealText (const ScriptRunElt& scriptRunElt) const
338{
339 const Led_tChar* s = nullptr;
340 const Led_tChar* e = nullptr;
341 PeekAtRealText (scriptRunElt, &s, &e);
342 return Led_tString{s, e};
343}
344
345void TextLayoutBlock::PeekAtVirtualText (const ScriptRunElt& scriptRunElt, const Led_tChar** startText, const Led_tChar** endText) const
346{
347 RequireNotNull (startText);
348 RequireNotNull (endText);
349 PeekAtVirtualText_ (startText, endText);
350 *startText += scriptRunElt.fVirtualStart;
351 size_t len = scriptRunElt.fVirtualEnd - scriptRunElt.fVirtualStart;
352 Assert (*endText - *startText >= static_cast<ptrdiff_t> (len)); // make sure we are SHRINKING the text and not making it point past its end
353 *endText = *startText + len;
354}
355
356Led_tString TextLayoutBlock::GetVirtualText () const
357{
358 const Led_tChar* s = nullptr;
359 const Led_tChar* e = nullptr;
360 PeekAtVirtualText_ (&s, &e);
361 return Led_tString{s, e};
362}
363
364Led_tString TextLayoutBlock::GetVirtualText (const ScriptRunElt& scriptRunElt) const
365{
366 const Led_tChar* s = nullptr;
367 const Led_tChar* e = nullptr;
368 PeekAtVirtualText (scriptRunElt, &s, &e);
369 return Led_tString{s, e};
370}
371
372#if qStroika_Foundation_Debug_AssertionsChecked
373void TextLayoutBlock::Invariant_ () const
374{
375 size_t len = GetTextLength ();
376
377 vector<ScriptRunElt> scriptRuns = GetScriptRuns ();
378
379 // Make sure each scriptrun elt is non-empty
380 {
381 for (auto j = scriptRuns.begin (); j != scriptRuns.end (); ++j) {
382 Assert ((*j).fRealStart < len);
383 Assert ((*j).fRealEnd <= len);
384 Assert ((*j).fVirtualStart < len);
385 Assert ((*j).fVirtualEnd <= len);
386 Assert ((*j).fRealStart < (*j).fRealEnd); // no empty elements
387 }
388 }
389
390 /*
391 * Validate the fScriptRuns list. Make sure it completely covers the real and virtual range. Make
392 * sure each element has exactly LENGTH spaces used up for the real text and the virtual text.
393 * That - by itself - assures also that there are no overlapping elements.
394 */
395 {
396 for (size_t i = 0; i < len; ++i) {
397 // Assure pos 'i' found in source and virtual
398 bool foundReal = false;
399 bool foundVirtual = false;
400 size_t nRealFound = 0;
401 size_t nVirtualFound = 0;
402 for (auto j = scriptRuns.begin (); j != scriptRuns.end (); ++j) {
403 if ((*j).fRealStart <= i and i < (*j).fRealEnd) {
404 Assert (not foundReal);
405 foundReal = true;
406 }
407 Assert ((*j).fVirtualStart < (*j).fVirtualEnd); // no empty elements
408 if ((*j).fVirtualStart <= i and i < (*j).fVirtualEnd) {
409 Assert (not foundVirtual);
410 foundVirtual = true;
411 }
412 nRealFound += (*j).fRealEnd - (*j).fRealStart;
413 nVirtualFound += (*j).fVirtualEnd - (*j).fVirtualStart;
414 }
415 Assert (foundReal);
416 Assert (foundVirtual);
417 Assert (nRealFound == len);
418 Assert (nVirtualFound == len);
419 }
420 }
421}
422#endif
423
424bool TextLayoutBlock::operator== (const TextLayoutBlock& rhs) const
425{
426 if (this->GetTextLength () != rhs.GetTextLength ()) {
427 return false;
428 }
429 if (this->GetRealText () != rhs.GetRealText ()) {
430 return false;
431 }
432 if (this->GetVirtualText () != rhs.GetVirtualText ()) {
433 return false;
434 }
435 vector<TextLayoutBlock::ScriptRunElt> lhsSR = this->GetScriptRuns ();
436 vector<TextLayoutBlock::ScriptRunElt> rhsSR = rhs.GetScriptRuns ();
437 if (lhsSR.size () != rhsSR.size ()) {
438 return false;
439 }
440 for (size_t i = 0; i < lhsSR.size (); ++i) {
441 if (lhsSR[i] != rhsSR[i]) {
442 return false;
443 }
444 }
445 return true;
446}
447
448/*
449 ********************************************************************************
450 ****************************** TextLayoutBlock_Basic ***************************
451 ********************************************************************************
452 */
453TextLayoutBlock_Basic::TextLayoutBlock_Basic (const Led_tChar* realText, const Led_tChar* realTextEnd)
454 : fTextLength{0}
455 , fRealText{0}
456 , fVirtualText{0}
457 , fScriptRuns{}
458{
459 Construct (realText, realTextEnd, nullptr);
460}
461
462TextLayoutBlock_Basic::TextLayoutBlock_Basic (const Led_tChar* realText, const Led_tChar* realTextEnd, TextDirection initialDirection)
463 : fTextLength{0}
464 , fRealText{0}
465 , fVirtualText{0}
466 , fScriptRuns{}
467{
468 Construct (realText, realTextEnd, &initialDirection);
469}
470
471void TextLayoutBlock_Basic::Construct (const Led_tChar* realText, const Led_tChar* realTextEnd, [[maybe_unused]] const TextDirection* initialDirection)
472{
473 size_t textLength = realTextEnd - realText;
474 fTextLength = textLength;
475 fRealText.GrowToSize (textLength);
476 fVirtualText.GrowToSize (textLength);
477 copy (realText, realText + textLength, static_cast<Led_tChar*> (fRealText));
478
479#if qDebugHack_UpperCaseCharsTratedAsRTL
480 for (size_t i = 0; i < textLength; ++i) {
481 if ('A' <= fRealText[i] and fRealText[i] <= 'Z') {
482 fRealText[i] = 0xfe7d; // random arabic character
483 }
484 }
485#endif
486
487 /*
488 * Try a number of different ways to generate the virtual text/maps etc.
489 */
490#if qTestUNISCRIBEResultsEqualFriBidi
491 (void)Construct_UNISCRIBE (initialDirection);
492 Memory::StackBuffer<Led_tChar> savedVirtualText{fTextLength};
493 copy (static_cast<const Led_tChar*> (fVirtualText), static_cast<const Led_tChar*> (fVirtualText) + fTextLength,
494 static_cast<Led_tChar*> (savedVirtualText));
495 vector<ScriptRunElt> savedScriptRuns = fScriptRuns;
496 fScriptRuns = vector<ScriptRunElt> ();
497 Construct_FriBidi (initialDirection);
498 {
499 for (size_t i = 0; i < fTextLength; ++i) {
500 Assert (savedVirtualText[i] == fVirtualText[i]);
501 }
502 }
503 {
504 Assert (savedScriptRuns.size () == fScriptRuns.size ());
505 for (size_t i = 0; i < savedScriptRuns.size (); ++i) {
506 Assert (savedScriptRuns[i] == fScriptRuns[i]);
507 }
508 }
509#else
510#if qUseFriBidi
511 Construct_FriBidi (initialDirection);
512 if (1) {
513 }
514 else
515#elif qTryToUseUNISCRIBEForTextRuns
516 if (Construct_UNISCRIBE (initialDirection)) {
517 }
518 else
519#elif qUseICUBidi
520 Construct_ICU (initialDirection);
521 if (1) {
522 }
523 else
524#endif
525 {
526 Construct_Default ();
527 }
528#endif
529
530 if constexpr (qDebugHack_ReverseDirections) {
531 for (auto i = fScriptRuns.begin (); i != fScriptRuns.end (); ++i) {
532 ScriptRunElt& se = *i;
533 TextDirection newDir = (se.fDirection == eLeftToRight) ? eRightToLeft : eLeftToRight;
534 se.fDirection = newDir;
535 // now reverse the virtual text in the run...
536 size_t runLen = se.fVirtualEnd - se.fVirtualStart;
537 Memory::StackBuffer<Led_tChar> reverseBuf{runLen};
538 for (size_t j = se.fVirtualStart; j < se.fVirtualEnd; ++j) {
539 reverseBuf[runLen - 1 - (j - se.fVirtualStart)] = fVirtualText[j];
540 }
541 copy (static_cast<Led_tChar*> (reverseBuf), static_cast<Led_tChar*> (reverseBuf) + runLen,
542 static_cast<Led_tChar*> (fVirtualText) + se.fVirtualStart);
543 }
544 }
545
546 Invariant ();
547}
548
549#if qTryToUseUNISCRIBEForTextRuns
550bool TextLayoutBlock_Basic::Construct_UNISCRIBE (const TextDirection* initialDirection)
551{
552 if (sUniscribeDLL.IsAvail () and fTextLength > 0) {
553 /*
554 * See SPR#1224 for why we pass along zeroed scriptControl/scriptState.
555 */
556 Memory::StackBuffer<SCRIPT_ITEM> scriptItems{fTextLength + 1};
557 int nScriptItems = 0;
558 SCRIPT_CONTROL scriptControl;
559 SCRIPT_STATE scriptState;
560 memset (&scriptControl, 0, sizeof (scriptControl));
561 memset (&scriptState, 0, sizeof (scriptState));
562
563 // Guess inital uBidiLevel to simulate the _ON feature of fribidi
564 {
565 InitialUNISCRIBEDir dir = eNeutralChar;
566 if (initialDirection == nullptr) {
567 size_t maxToCheck = min (fTextLength, size_t (100)); // don't bother checking more than 100 characters, as this CAN be slow
568 // in degenerate cases (see SPR#1295). Not such a big deal to get this right
569 // in those crazy cases.
570 for (size_t i = 0; i < maxToCheck; ++i) {
571 dir = myGetInitialUNISCRIBEDir (fRealText[i]);
572 if (dir != eNeutralChar) {
573 break;
574 }
575 }
576 }
577 else {
578 dir = (*initialDirection == eLeftToRight) ? eLTRChar : eRTLChar;
579 }
580 if (dir == eRTLChar) {
581 scriptState.uBidiLevel = 1;
582 }
583 }
584 Verify (sUniscribeDLL.ScriptItemize (static_cast<const Led_tChar*> (fRealText), fTextLength, fTextLength + 1, &scriptControl,
585 &scriptState, scriptItems, &nScriptItems) == S_OK);
586 Assert (nScriptItems >= 1);
587
588 Memory::StackBuffer<BYTE> bidiLevels{nScriptItems};
589 for (size_t i = 0; i < static_cast<size_t> (nScriptItems); ++i) {
590 bidiLevels[i] = scriptItems[i].a.s.uBidiLevel;
591 }
592
593 Memory::StackBuffer<int> visualToLogical{nScriptItems};
594 Memory::StackBuffer<int> logicalToVisual{nScriptItems};
595
596 Verify (sUniscribeDLL.ScriptLayout (nScriptItems, bidiLevels, visualToLogical, logicalToVisual) == S_OK);
597
598 /*
599 * Create an array (indexed by virtual segment number) of that segments display start. The first obviously
600 * starts at zero offset. The subsequent ones are offset from their predecessors by the width of that predecessor
601 * (a slightly tricky computation - noting that we must map back to logical coords to use the scriptItems array
602 * and note that the UNISCRIBE API gives you the length of a cell by taking diff between it and following cell start).
603 */
604 Memory::StackBuffer<size_t> visualSegStarts{nScriptItems};
605 {
606 Assert (nScriptItems > 0);
607 visualSegStarts[0] = 0;
608 for (size_t visIndex = 1; visIndex < static_cast<size_t> (nScriptItems); ++visIndex) {
609 size_t prevLogIdx = visualToLogical[visIndex - 1];
610 size_t prevItemWidth = scriptItems[prevLogIdx + 1].iCharPos - scriptItems[prevLogIdx].iCharPos;
611 visualSegStarts[visIndex] = visualSegStarts[visIndex - 1] + prevItemWidth;
612 }
613 }
614
615 {
616 const Led_tChar* rT = static_cast<Led_tChar*> (fRealText);
617 Led_tChar* vT = static_cast<Led_tChar*> (fVirtualText);
618
619 for (size_t i = 0; i < static_cast<size_t> (nScriptItems); ++i) {
620 TextDirection curDir = (bidiLevels[i] & 1) ? eRightToLeft : eLeftToRight;
621 // Construct a very simple one-char wide run elt with appropriate direction info and
622 // to find it in src text or virtual text
623 ScriptRunElt s;
624 s.fDirection = curDir;
625 s.fRealStart = scriptItems[i].iCharPos;
626 s.fRealEnd = scriptItems[i + 1].iCharPos;
627
628#if qStroika_Foundation_Debug_AssertionsChecked
629 {
630 for (size_t j = 0; j < static_cast<size_t> (nScriptItems); ++j) {
631 Assert (logicalToVisual[visualToLogical[i]] == i);
632 }
633 }
634 size_t referenceVirtualStart = 0;
635 {
636 referenceVirtualStart = 0;
637 for (int visIndex = logicalToVisual[i] - 1; visIndex >= 0; --visIndex) {
638 Assert (visIndex < nScriptItems);
639 size_t itsLogIdx = visualToLogical[visIndex];
640 referenceVirtualStart += (scriptItems[itsLogIdx + 1].iCharPos - scriptItems[itsLogIdx].iCharPos);
641 }
642 }
643 Assert (referenceVirtualStart == visualSegStarts[logicalToVisual[i]]);
644#endif
645
646 // Now precompute visual segment starts arrays and just use logicalToVisual to find the appropriate
647 // visual segment (SPR#1566) - speed tweek - to precompute array, and just use here -- LGP 2003-11-30.
648 s.fVirtualStart = visualSegStarts[logicalToVisual[i]];
649 s.fVirtualEnd = s.fVirtualStart + (s.fRealEnd - s.fRealStart);
650 Assert (s.fVirtualEnd - s.fVirtualStart == s.fRealEnd - s.fRealStart);
651
652 // Check that each run element satisfies these contraints:
653 // o Same direction
654 // o Source text abuts (on left or right)
655 // o Virtual text abuts (on left or right)
656 //
657 // If the element satisfies these contraints - then merge it with the current last element,
658 // and otherwise, construct a new element.
659 //
660 // NB: This merging behavir isn't really needed. It will work fine without it. ScriptItemize
661 // appears to generate lots of run elements with the same direction and that are adjacent.
662 // I'm not sure why this is useful - at least how I work right now - so we just merge them
663 // together.
664 //
665 const bool kDoMerging = true;
666 if (kDoMerging) {
667 if (fScriptRuns.empty ()) {
668 ScriptRunElt e = s;
669 e.fRealEnd = e.fRealStart;
670 e.fVirtualEnd = e.fVirtualStart;
671 fScriptRuns.push_back (e);
672 }
673 ScriptRunElt& lastElt = fScriptRuns.back ();
674
675 if (lastElt.fDirection == s.fDirection and (lastElt.fRealEnd == s.fRealStart or lastElt.fRealStart == s.fRealEnd) and
676 (lastElt.fVirtualEnd == s.fVirtualStart or lastElt.fVirtualStart == s.fVirtualEnd)) {
677 lastElt.fRealStart = min (lastElt.fRealStart, s.fRealStart);
678 lastElt.fRealEnd = max (lastElt.fRealEnd, s.fRealEnd);
679 lastElt.fVirtualStart = min (lastElt.fVirtualStart, s.fVirtualStart);
680 lastElt.fVirtualEnd = max (lastElt.fVirtualEnd, s.fVirtualEnd);
681 }
682 else {
683 fScriptRuns.push_back (s);
684 }
685 }
686 else {
687 fScriptRuns.push_back (s);
688 }
689
690 if (curDir == eLeftToRight) {
691 copy (rT + s.fRealStart, rT + s.fRealEnd, vT + s.fVirtualStart);
692 }
693 else {
694 reverse_copy (rT + s.fRealStart, rT + s.fRealEnd, vT + s.fVirtualStart);
695 /*
696 * Walk through the RTL run and see if we need to mirror any characters.
697 */
698 for (auto cur = vT + s.fVirtualStart; cur < vT + s.fVirtualStart + (s.fRealEnd - s.fRealStart); ++cur) {
699 Led_tChar mirrorChar = '\0';
700 if (CharacterProperties::IsMirrored (*cur, &mirrorChar)) {
701 *cur = mirrorChar;
702 }
703 }
704 }
705 }
706 }
707
708 return true;
709 }
710 return false;
711}
712#endif
713
714#if qUseFriBidi
715void TextLayoutBlock_Basic::Construct_FriBidi (const TextDirection* initialDirection)
716{
717 Assert (fTextLength <= FRIBIDI_MAX_STRING_LENGTH);
718
719 FriBidiCharType baseDir = FRIBIDI_TYPE_ON; // http://imagic.weizmann.ac.il/~dov/freesw/FriBidi/ docs indicate FRIBIDI_TYPE_N is a good default value - 2002-11-27
720 // but fribidi_types.h header says FRIBIDI_TYPE_N is obsolete name - use FRIBIDI_TYPE_ON instead
721 if (initialDirection != nullptr) {
722 baseDir = (*initialDirection == eLeftToRight) ? FRIBIDI_TYPE_L : FRIBIDI_TYPE_R;
723 }
724 Memory::StackBuffer<FriBidiChar> srcText{fTextLength};
725 Memory::StackBuffer<FriBidiChar> vText{fTextLength + 1}; // fribidi_log2vis NUL-terminates the string
726 copy (static_cast<Led_tChar*> (fRealText), fRealText + fTextLength, static_cast<FriBidiChar*> (srcText));
727 Memory::StackBuffer<FriBidiStrIndex> posLtoVList{fTextLength};
728 Memory::StackBuffer<FriBidiLevel> bidiLevels{fTextLength * 2}; // no docs on size - but looking at code it appears it can be up to this big...
729 // LGP 2002-12-04
730
731 bool result = ::fribidi_log2vis (srcText, fTextLength, &baseDir, vText, posLtoVList, nullptr, bidiLevels);
732 Assert (result);
733
734 {
735 for (size_t i = 0; i < fTextLength; ++i) {
736 TextDirection curDir = (bidiLevels[i] & 1) ? eRightToLeft : eLeftToRight;
737 // Construct a very simple one-char wide run elt with appropriate direction info and
738 // to find it in src text or virtual text
739 ScriptRunElt thisCharElt;
740 thisCharElt.fDirection = curDir;
741 thisCharElt.fRealStart = i;
742 thisCharElt.fRealEnd = thisCharElt.fRealStart + 1;
743 thisCharElt.fVirtualStart = posLtoVList[i];
744 thisCharElt.fVirtualEnd = thisCharElt.fVirtualStart + 1;
745
746 if (i == 0) {
747 ScriptRunElt s = thisCharElt;
748 s.fRealEnd = s.fRealStart;
749 s.fVirtualEnd = s.fVirtualStart;
750 fScriptRuns.push_back (s);
751 }
752 ScriptRunElt& lastElt = fScriptRuns[fScriptRuns.size () - 1];
753
754 // Check that each run element satisfies these contraints:
755 // o Same direction
756 // o Source text abuts (on left or right)
757 // o Virtual text abuts (on left or right)
758 //
759 // If the element satisfies these contraints - then merge it with the current last element,
760 // and otherwise, construct a new element.
761 //
762 if (lastElt.fDirection == thisCharElt.fDirection and
763 (lastElt.fRealEnd == thisCharElt.fRealStart or lastElt.fRealStart == thisCharElt.fRealEnd) and
764 (lastElt.fVirtualEnd == thisCharElt.fVirtualStart or lastElt.fVirtualStart == thisCharElt.fVirtualEnd)) {
765 lastElt.fRealStart = min (lastElt.fRealStart, thisCharElt.fRealStart);
766 lastElt.fRealEnd = max (lastElt.fRealEnd, thisCharElt.fRealEnd);
767 lastElt.fVirtualStart = min (lastElt.fVirtualStart, thisCharElt.fVirtualStart);
768 lastElt.fVirtualEnd = max (lastElt.fVirtualEnd, thisCharElt.fVirtualEnd);
769 }
770 else {
771 fScriptRuns.push_back (thisCharElt);
772 }
773 }
774 }
775
776 copy (static_cast<FriBidiChar*> (vText), vText + fTextLength, static_cast<Led_tChar*> (fVirtualText));
777}
778#endif
779
780#if qUseICUBidi
781void TextLayoutBlock_Basic::Construct_ICU (const TextDirection* initialDirection)
782{
783 //TMPHACK...to start testing...
784 UErrorCode err = U_ZERO_ERROR;
785 UBiDi* para = ubidi_openSized (textLength, 0, &err);
786 ubidi_close (para);
787#error "NOT YET IMPLEMENTED (maybe won't be)"
788}
789#endif
790
791void TextLayoutBlock_Basic::Construct_Default ()
792{
793 // Only do something for UNICODE/wide-chars - otherwise - just copy the chars by value...
794 copy (static_cast<Led_tChar*> (fRealText), static_cast<Led_tChar*> (fRealText) + fTextLength, static_cast<Led_tChar*> (fVirtualText));
795 if (fTextLength != 0) {
796 ScriptRunElt thisCharElt;
797 thisCharElt.fDirection = eLeftToRight;
798 thisCharElt.fRealStart = 0;
799 thisCharElt.fRealEnd = fTextLength;
800 thisCharElt.fVirtualStart = 0;
801 thisCharElt.fVirtualEnd = fTextLength;
802 fScriptRuns.push_back (thisCharElt);
803 }
804}
805
806void TextLayoutBlock_Basic::PeekAtRealText_ (const Led_tChar** startText, const Led_tChar** endText) const
807{
808 RequireNotNull (startText);
809 RequireNotNull (endText);
810 *startText = fRealText.data ();
811 *endText = fRealText.data () + fTextLength;
812}
813
814void TextLayoutBlock_Basic::PeekAtVirtualText_ (const Led_tChar** startText, const Led_tChar** endText) const
815{
816 RequireNotNull (startText);
817 RequireNotNull (endText);
818 *startText = fVirtualText.data ();
819 *endText = fVirtualText.data () + fTextLength;
820}
821
822vector<TextLayoutBlock::ScriptRunElt> TextLayoutBlock_Basic::GetScriptRuns () const
823{
824 return fScriptRuns;
825}
826
827/*
828 ********************************************************************************
829 ***************************** TextLayoutBlock_Copy *****************************
830 ********************************************************************************
831 */
832TextLayoutBlock_Copy::TextLayoutBlock_Copy (const TextLayoutBlock& from)
833{
834 /*
835 * Compute size - and then copy all the data into a continguous block of RAM.
836 */
837 size_t strLength = from.GetTextLength ();
838 vector<ScriptRunElt> scriptRuns = from.GetScriptRuns ();
839
840 // compute needed size
841 size_t neededSize = sizeof (BlockRep);
842 neededSize += sizeof (Led_tChar) * strLength; // fRealText
843 neededSize += sizeof (Led_tChar) * strLength; // fVirtualText
844 neededSize += sizeof (ScriptRunElt) * scriptRuns.size (); // fScriptRuns
845 auto deleter = [] (BlockRep* br) { delete[] (reinterpret_cast<byte*> (br)); };
846 fRep = shared_ptr<BlockRep>{reinterpret_cast<BlockRep*> (new byte[neededSize]), deleter};
847
848 fRep->fTextLength = strLength;
849
850 fRep->fRealText = reinterpret_cast<Led_tChar*> (fRep.get () + 1);
851 {
852 const Led_tChar* s = nullptr;
853 const Led_tChar* e = nullptr;
854 from.PeekAtRealText_ (&s, &e);
855 copy (s, e, const_cast<Led_tChar*> (fRep->fRealText));
856 }
857
858 fRep->fVirtualText = fRep->fRealText + strLength;
859 {
860 const Led_tChar* s = nullptr;
861 const Led_tChar* e = nullptr;
862 from.PeekAtVirtualText_ (&s, &e);
863 copy (s, e, const_cast<Led_tChar*> (fRep->fVirtualText));
864 }
865
866 fRep->fScriptRuns = reinterpret_cast<const ScriptRunElt*> (fRep->fVirtualText + strLength);
867 fRep->fScriptRunsEnd = fRep->fScriptRuns + scriptRuns.size ();
868 copy (scriptRuns.begin (), scriptRuns.end (), const_cast<ScriptRunElt*> (fRep->fScriptRuns));
869}
870
871TextLayoutBlock_Copy::TextLayoutBlock_Copy (const TextLayoutBlock_Copy& from)
872 : inherited (from)
873 , fRep (from.fRep)
874{
875}
876
877void TextLayoutBlock_Copy::PeekAtRealText_ (const Led_tChar** startText, const Led_tChar** endText) const
878{
879 RequireNotNull (startText);
880 RequireNotNull (endText);
881 *startText = fRep->fRealText;
882 *endText = fRep->fRealText + fRep->fTextLength;
883}
884
885void TextLayoutBlock_Copy::PeekAtVirtualText_ (const Led_tChar** startText, const Led_tChar** endText) const
886{
887 RequireNotNull (startText);
888 RequireNotNull (endText);
889 *startText = fRep->fVirtualText;
890 *endText = fRep->fVirtualText + fRep->fTextLength;
891}
892
893vector<TextLayoutBlock_Copy::ScriptRunElt> TextLayoutBlock_Copy::GetScriptRuns () const
894{
895 return vector<ScriptRunElt> (fRep->fScriptRuns, fRep->fScriptRunsEnd);
896}
897
898/*
899 ********************************************************************************
900 ************************ TextLayoutBlock_Copy::BlockRep ************************
901 ********************************************************************************
902 */
903void TextLayoutBlock_Copy::BlockRep::operator delete (void* p)
904{
905 // Because we allocate using new byte[] in TextLayoutBlock_Copy::CTOR (see SPR#1596)
906 delete[] (reinterpret_cast<char*> (p));
907}
908
909/*
910 ********************************************************************************
911 *********************** TextLayoutBlock_VirtualSubset **************************
912 ********************************************************************************
913 */
914TextLayoutBlock_VirtualSubset::TextLayoutBlock_VirtualSubset (const TextLayoutBlock& subsetOf, size_t start, size_t end)
915 : fSubsetOf{subsetOf}
916 , fStart{start}
917 , fEnd{end}
918 , fRealText{end - start}
919{
920 vector<ScriptRunElt> origRuns = fSubsetOf.GetScriptRuns ();
921 size_t offsetSoFar = 0;
922 const Led_tChar* fullRealText = fSubsetOf.PeekAtRealText ();
923 for (auto i = origRuns.begin (); i != origRuns.end (); ++i) {
924 size_t offsetStart = (*i).fVirtualStart;
925 size_t offsetEnd = (*i).fVirtualEnd;
926
927 size_t runRelStart = max (offsetStart, static_cast<size_t> (fStart));
928 size_t runRelEnd = min (offsetEnd, static_cast<size_t> (fEnd));
929 if (runRelStart < runRelEnd) {
930 // then this is a run with some stuff in it
931 ScriptRunElt s = *i;
932 Assert (runRelStart >= fStart);
933 Assert (runRelEnd >= fStart);
934 s.fVirtualStart = runRelStart - fStart;
935 s.fVirtualEnd = runRelEnd - fStart;
936
937 /*
938 * Since the API of TextLayoutBlock assumes a contiguous piece of RAM for the REAL and Virtual text, and since
939 * the source text is no longer continguous (if we used the full buffer then its length wouldn't match the length
940 * of our virtual buffer), we must make a copy of the real text and use different adjusted offsets. This 'real' text then
941 * isn't so real after all. It might not even be in an order that would have produced the given virtual text. BUT - if one
942 * makes calls to get back the original real text for any given range of Virual text - you WILL at least get back the
943 * right text.
944 */
945 Assert (runRelEnd >= runRelStart);
946 size_t runEltLen = runRelEnd - runRelStart;
947 copy (fullRealText + s.fRealStart, fullRealText + s.fRealStart + runEltLen, fRealText.data () + offsetSoFar);
948 s.fRealStart = offsetSoFar;
949 offsetSoFar += runEltLen;
950 s.fRealEnd = offsetSoFar;
951 Containers::Support::ReserveTweaks::Reserve4Add1 (fScriptRuns);
952 fScriptRuns.push_back (s);
953 }
954 }
955
956 Invariant ();
957}
958
959void TextLayoutBlock_VirtualSubset::PeekAtRealText_ (const Led_tChar** startText, const Led_tChar** endText) const
960{
961 RequireNotNull (startText);
962 RequireNotNull (endText);
963 *startText = fRealText.data ();
964 *endText = *startText + (fEnd - fStart);
965}
966
967void TextLayoutBlock_VirtualSubset::PeekAtVirtualText_ (const Led_tChar** startText, const Led_tChar** endText) const
968{
969 RequireNotNull (startText);
970 RequireNotNull (endText);
971 fSubsetOf.PeekAtVirtualText_ (startText, endText);
972 *startText += fStart;
973 Assert (fStart <= fEnd);
974 Assert (*endText - *startText >= static_cast<int> (fEnd - fStart)); // make sure orig string >= shortened string
975 *endText = *startText + (fEnd - fStart);
976}
977
978vector<TextLayoutBlock::ScriptRunElt> TextLayoutBlock_VirtualSubset::GetScriptRuns () const
979{
980 return fScriptRuns;
981}
982
983#if qTryToUseUNISCRIBEForTextRuns
984/*
985 ********************************************************************************
986 ********** myGetInitialUNISCRIBEDir (AUTOGENERATED memoize of GetInitialUNISCRIBEDir - Jan 12 2003) ***********
987 ********************************************************************************
988 */
989// Hack for SPR#1236
990InitialUNISCRIBEDir myGetInitialUNISCRIBEDir (wchar_t c)
991{
992 if (65 <= c && c <= 90) {
993 return eLTRChar;
994 }
995 if (97 <= c && c <= 122) {
996 return eLTRChar;
997 }
998 if (170 <= c && c <= 170) {
999 return eLTRChar;
1000 }
1001 if (181 <= c && c <= 181) {
1002 return eLTRChar;
1003 }
1004 if (186 <= c && c <= 186) {
1005 return eLTRChar;
1006 }
1007 if (192 <= c && c <= 214) {
1008 return eLTRChar;
1009 }
1010 if (216 <= c && c <= 246) {
1011 return eLTRChar;
1012 }
1013 if (248 <= c && c <= 696) {
1014 return eLTRChar;
1015 }
1016 if (699 <= c && c <= 705) {
1017 return eLTRChar;
1018 }
1019 if (720 <= c && c <= 721) {
1020 return eLTRChar;
1021 }
1022 if (736 <= c && c <= 740) {
1023 return eLTRChar;
1024 }
1025 if (750 <= c && c <= 767) {
1026 return eLTRChar;
1027 }
1028 if (848 <= c && c <= 863) {
1029 return eLTRChar;
1030 }
1031 if (880 <= c && c <= 883) {
1032 return eLTRChar;
1033 }
1034 if (886 <= c && c <= 893) {
1035 return eLTRChar;
1036 }
1037 if (895 <= c && c <= 899) {
1038 return eLTRChar;
1039 }
1040 if (902 <= c && c <= 902) {
1041 return eLTRChar;
1042 }
1043 if (904 <= c && c <= 1013) {
1044 return eLTRChar;
1045 }
1046 if (1015 <= c && c <= 1154) {
1047 return eLTRChar;
1048 }
1049 if (1159 <= c && c <= 1159) {
1050 return eLTRChar;
1051 }
1052 if (1162 <= c && c <= 1417) {
1053 return eLTRChar;
1054 }
1055 if (1419 <= c && c <= 1423) {
1056 return eLTRChar;
1057 }
1058 if (1424 <= c && c <= 1424) {
1059 return eRTLChar;
1060 }
1061 if (1442 <= c && c <= 1442) {
1062 return eRTLChar;
1063 }
1064 if (1466 <= c && c <= 1466) {
1065 return eRTLChar;
1066 }
1067 if (1470 <= c && c <= 1470) {
1068 return eRTLChar;
1069 }
1070 if (1472 <= c && c <= 1472) {
1071 return eRTLChar;
1072 }
1073 if (1475 <= c && c <= 1475) {
1074 return eRTLChar;
1075 }
1076 if (1477 <= c && c <= 1547) {
1077 return eRTLChar;
1078 }
1079 if (1549 <= c && c <= 1610) {
1080 return eRTLChar;
1081 }
1082 if (1622 <= c && c <= 1631) {
1083 return eRTLChar;
1084 }
1085 if (1645 <= c && c <= 1647) {
1086 return eRTLChar;
1087 }
1088 if (1649 <= c && c <= 1749) {
1089 return eRTLChar;
1090 }
1091 if (1757 <= c && c <= 1757) {
1092 return eRTLChar;
1093 }
1094 if (1765 <= c && c <= 1766) {
1095 return eRTLChar;
1096 }
1097 if (1774 <= c && c <= 1775) {
1098 return eRTLChar;
1099 }
1100 if (1786 <= c && c <= 1806) {
1101 return eRTLChar;
1102 }
1103 if (1808 <= c && c <= 1808) {
1104 return eRTLChar;
1105 }
1106 if (1810 <= c && c <= 1839) {
1107 return eRTLChar;
1108 }
1109 if (1867 <= c && c <= 1957) {
1110 return eRTLChar;
1111 }
1112 if (1969 <= c && c <= 1983) {
1113 return eRTLChar;
1114 }
1115 if (1984 <= c && c <= 2304) {
1116 return eLTRChar;
1117 }
1118 if (2307 <= c && c <= 2363) {
1119 return eLTRChar;
1120 }
1121 if (2365 <= c && c <= 2368) {
1122 return eLTRChar;
1123 }
1124 if (2377 <= c && c <= 2380) {
1125 return eLTRChar;
1126 }
1127 if (2382 <= c && c <= 2384) {
1128 return eLTRChar;
1129 }
1130 if (2389 <= c && c <= 2401) {
1131 return eLTRChar;
1132 }
1133 if (2404 <= c && c <= 2432) {
1134 return eLTRChar;
1135 }
1136 if (2434 <= c && c <= 2491) {
1137 return eLTRChar;
1138 }
1139 if (2493 <= c && c <= 2496) {
1140 return eLTRChar;
1141 }
1142 if (2501 <= c && c <= 2508) {
1143 return eLTRChar;
1144 }
1145 if (2510 <= c && c <= 2529) {
1146 return eLTRChar;
1147 }
1148 if (2532 <= c && c <= 2545) {
1149 return eLTRChar;
1150 }
1151 if (2548 <= c && c <= 2561) {
1152 return eLTRChar;
1153 }
1154 if (2563 <= c && c <= 2619) {
1155 return eLTRChar;
1156 }
1157 if (2621 <= c && c <= 2624) {
1158 return eLTRChar;
1159 }
1160 if (2627 <= c && c <= 2630) {
1161 return eLTRChar;
1162 }
1163 if (2633 <= c && c <= 2634) {
1164 return eLTRChar;
1165 }
1166 if (2638 <= c && c <= 2671) {
1167 return eLTRChar;
1168 }
1169 if (2674 <= c && c <= 2688) {
1170 return eLTRChar;
1171 }
1172 if (2691 <= c && c <= 2747) {
1173 return eLTRChar;
1174 }
1175 if (2749 <= c && c <= 2752) {
1176 return eLTRChar;
1177 }
1178 if (2758 <= c && c <= 2758) {
1179 return eLTRChar;
1180 }
1181 if (2761 <= c && c <= 2764) {
1182 return eLTRChar;
1183 }
1184 if (2766 <= c && c <= 2816) {
1185 return eLTRChar;
1186 }
1187 if (2818 <= c && c <= 2875) {
1188 return eLTRChar;
1189 }
1190 if (2877 <= c && c <= 2878) {
1191 return eLTRChar;
1192 }
1193 if (2880 <= c && c <= 2880) {
1194 return eLTRChar;
1195 }
1196 if (2884 <= c && c <= 2892) {
1197 return eLTRChar;
1198 }
1199 if (2894 <= c && c <= 2901) {
1200 return eLTRChar;
1201 }
1202 if (2903 <= c && c <= 2945) {
1203 return eLTRChar;
1204 }
1205 if (2947 <= c && c <= 3007) {
1206 return eLTRChar;
1207 }
1208 if (3009 <= c && c <= 3020) {
1209 return eLTRChar;
1210 }
1211 if (3022 <= c && c <= 3133) {
1212 return eLTRChar;
1213 }
1214 if (3137 <= c && c <= 3141) {
1215 return eLTRChar;
1216 }
1217 if (3145 <= c && c <= 3145) {
1218 return eLTRChar;
1219 }
1220 if (3150 <= c && c <= 3156) {
1221 return eLTRChar;
1222 }
1223 if (3159 <= c && c <= 3262) {
1224 return eLTRChar;
1225 }
1226 if (3264 <= c && c <= 3269) {
1227 return eLTRChar;
1228 }
1229 if (3271 <= c && c <= 3275) {
1230 return eLTRChar;
1231 }
1232 if (3278 <= c && c <= 3392) {
1233 return eLTRChar;
1234 }
1235 if (3396 <= c && c <= 3404) {
1236 return eLTRChar;
1237 }
1238 if (3406 <= c && c <= 3529) {
1239 return eLTRChar;
1240 }
1241 if (3531 <= c && c <= 3537) {
1242 return eLTRChar;
1243 }
1244 if (3541 <= c && c <= 3541) {
1245 return eLTRChar;
1246 }
1247 if (3543 <= c && c <= 3632) {
1248 return eLTRChar;
1249 }
1250 if (3634 <= c && c <= 3635) {
1251 return eLTRChar;
1252 }
1253 if (3643 <= c && c <= 3646) {
1254 return eLTRChar;
1255 }
1256 if (3648 <= c && c <= 3654) {
1257 return eLTRChar;
1258 }
1259 if (3663 <= c && c <= 3760) {
1260 return eLTRChar;
1261 }
1262 if (3762 <= c && c <= 3763) {
1263 return eLTRChar;
1264 }
1265 if (3770 <= c && c <= 3770) {
1266 return eLTRChar;
1267 }
1268 if (3773 <= c && c <= 3783) {
1269 return eLTRChar;
1270 }
1271 if (3790 <= c && c <= 3863) {
1272 return eLTRChar;
1273 }
1274 if (3866 <= c && c <= 3892) {
1275 return eLTRChar;
1276 }
1277 if (3894 <= c && c <= 3894) {
1278 return eLTRChar;
1279 }
1280 if (3896 <= c && c <= 3896) {
1281 return eLTRChar;
1282 }
1283 if (3902 <= c && c <= 3952) {
1284 return eLTRChar;
1285 }
1286 if (3967 <= c && c <= 3967) {
1287 return eLTRChar;
1288 }
1289 if (3973 <= c && c <= 3973) {
1290 return eLTRChar;
1291 }
1292 if (3976 <= c && c <= 3983) {
1293 return eLTRChar;
1294 }
1295 if (3992 <= c && c <= 3992) {
1296 return eLTRChar;
1297 }
1298 if (4029 <= c && c <= 4037) {
1299 return eLTRChar;
1300 }
1301 if (4039 <= c && c <= 4140) {
1302 return eLTRChar;
1303 }
1304 if (4145 <= c && c <= 4145) {
1305 return eLTRChar;
1306 }
1307 if (4147 <= c && c <= 4149) {
1308 return eLTRChar;
1309 }
1310 if (4152 <= c && c <= 4152) {
1311 return eLTRChar;
1312 }
1313 if (4154 <= c && c <= 4183) {
1314 return eLTRChar;
1315 }
1316 if (4186 <= c && c <= 5759) {
1317 return eLTRChar;
1318 }
1319 if (5761 <= c && c <= 5786) {
1320 return eLTRChar;
1321 }
1322 if (5789 <= c && c <= 5905) {
1323 return eLTRChar;
1324 }
1325 if (5909 <= c && c <= 5937) {
1326 return eLTRChar;
1327 }
1328 if (5941 <= c && c <= 5969) {
1329 return eLTRChar;
1330 }
1331 if (5972 <= c && c <= 6001) {
1332 return eLTRChar;
1333 }
1334 if (6004 <= c && c <= 6070) {
1335 return eLTRChar;
1336 }
1337 if (6078 <= c && c <= 6085) {
1338 return eLTRChar;
1339 }
1340 if (6087 <= c && c <= 6088) {
1341 return eLTRChar;
1342 }
1343 if (6100 <= c && c <= 6106) {
1344 return eLTRChar;
1345 }
1346 if (6108 <= c && c <= 6143) {
1347 return eLTRChar;
1348 }
1349 if (6159 <= c && c <= 6312) {
1350 return eLTRChar;
1351 }
1352 if (6314 <= c && c <= 8124) {
1353 return eLTRChar;
1354 }
1355 if (8126 <= c && c <= 8126) {
1356 return eLTRChar;
1357 }
1358 if (8130 <= c && c <= 8140) {
1359 return eLTRChar;
1360 }
1361 if (8144 <= c && c <= 8156) {
1362 return eLTRChar;
1363 }
1364 if (8160 <= c && c <= 8172) {
1365 return eLTRChar;
1366 }
1367 if (8176 <= c && c <= 8188) {
1368 return eLTRChar;
1369 }
1370 if (8191 <= c && c <= 8191) {
1371 return eLTRChar;
1372 }
1373 if (8206 <= c && c <= 8206) {
1374 return eLTRChar;
1375 }
1376 if (8207 <= c && c <= 8207) {
1377 return eRTLChar;
1378 }
1379 if (8275 <= c && c <= 8278) {
1380 return eLTRChar;
1381 }
1382 if (8280 <= c && c <= 8286) {
1383 return eLTRChar;
1384 }
1385 if (8292 <= c && c <= 8297) {
1386 return eLTRChar;
1387 }
1388 if (8305 <= c && c <= 8307) {
1389 return eLTRChar;
1390 }
1391 if (8319 <= c && c <= 8319) {
1392 return eLTRChar;
1393 }
1394 if (8335 <= c && c <= 8351) {
1395 return eLTRChar;
1396 }
1397 if (8370 <= c && c <= 8399) {
1398 return eLTRChar;
1399 }
1400 if (8427 <= c && c <= 8447) {
1401 return eLTRChar;
1402 }
1403 if (8450 <= c && c <= 8450) {
1404 return eLTRChar;
1405 }
1406 if (8455 <= c && c <= 8455) {
1407 return eLTRChar;
1408 }
1409 if (8458 <= c && c <= 8467) {
1410 return eLTRChar;
1411 }
1412 if (8469 <= c && c <= 8469) {
1413 return eLTRChar;
1414 }
1415 if (8473 <= c && c <= 8477) {
1416 return eLTRChar;
1417 }
1418 if (8484 <= c && c <= 8484) {
1419 return eLTRChar;
1420 }
1421 if (8486 <= c && c <= 8486) {
1422 return eLTRChar;
1423 }
1424 if (8488 <= c && c <= 8488) {
1425 return eLTRChar;
1426 }
1427 if (8490 <= c && c <= 8493) {
1428 return eLTRChar;
1429 }
1430 if (8495 <= c && c <= 8497) {
1431 return eLTRChar;
1432 }
1433 if (8499 <= c && c <= 8505) {
1434 return eLTRChar;
1435 }
1436 if (8507 <= c && c <= 8511) {
1437 return eLTRChar;
1438 }
1439 if (8517 <= c && c <= 8521) {
1440 return eLTRChar;
1441 }
1442 if (8524 <= c && c <= 8530) {
1443 return eLTRChar;
1444 }
1445 if (8544 <= c && c <= 8591) {
1446 return eLTRChar;
1447 }
1448 if (9014 <= c && c <= 9082) {
1449 return eLTRChar;
1450 }
1451 if (9109 <= c && c <= 9109) {
1452 return eLTRChar;
1453 }
1454 if (9167 <= c && c <= 9215) {
1455 return eLTRChar;
1456 }
1457 if (9255 <= c && c <= 9279) {
1458 return eLTRChar;
1459 }
1460 if (9291 <= c && c <= 9311) {
1461 return eLTRChar;
1462 }
1463 if (9372 <= c && c <= 9449) {
1464 return eLTRChar;
1465 }
1466 if (9471 <= c && c <= 9471) {
1467 return eLTRChar;
1468 }
1469 if (9748 <= c && c <= 9749) {
1470 return eLTRChar;
1471 }
1472 if (9752 <= c && c <= 9752) {
1473 return eLTRChar;
1474 }
1475 if (9854 <= c && c <= 9855) {
1476 return eLTRChar;
1477 }
1478 if (9866 <= c && c <= 9984) {
1479 return eLTRChar;
1480 }
1481 if (9989 <= c && c <= 9989) {
1482 return eLTRChar;
1483 }
1484 if (9994 <= c && c <= 9995) {
1485 return eLTRChar;
1486 }
1487 if (10024 <= c && c <= 10024) {
1488 return eLTRChar;
1489 }
1490 if (10060 <= c && c <= 10060) {
1491 return eLTRChar;
1492 }
1493 if (10062 <= c && c <= 10062) {
1494 return eLTRChar;
1495 }
1496 if (10067 <= c && c <= 10069) {
1497 return eLTRChar;
1498 }
1499 if (10071 <= c && c <= 10071) {
1500 return eLTRChar;
1501 }
1502 if (10079 <= c && c <= 10080) {
1503 return eLTRChar;
1504 }
1505 if (10133 <= c && c <= 10135) {
1506 return eLTRChar;
1507 }
1508 if (10160 <= c && c <= 10160) {
1509 return eLTRChar;
1510 }
1511 if (10175 <= c && c <= 10191) {
1512 return eLTRChar;
1513 }
1514 if (10220 <= c && c <= 10223) {
1515 return eLTRChar;
1516 }
1517 if (11008 <= c && c <= 11903) {
1518 return eLTRChar;
1519 }
1520 if (11930 <= c && c <= 11930) {
1521 return eLTRChar;
1522 }
1523 if (12020 <= c && c <= 12031) {
1524 return eLTRChar;
1525 }
1526 if (12246 <= c && c <= 12271) {
1527 return eLTRChar;
1528 }
1529 if (12284 <= c && c <= 12287) {
1530 return eLTRChar;
1531 }
1532 if (12293 <= c && c <= 12295) {
1533 return eLTRChar;
1534 }
1535 if (12321 <= c && c <= 12329) {
1536 return eLTRChar;
1537 }
1538 if (12337 <= c && c <= 12341) {
1539 return eLTRChar;
1540 }
1541 if (12344 <= c && c <= 12348) {
1542 return eLTRChar;
1543 }
1544 if (12352 <= c && c <= 12440) {
1545 return eLTRChar;
1546 }
1547 if (12445 <= c && c <= 12447) {
1548 return eLTRChar;
1549 }
1550 if (12449 <= c && c <= 12538) {
1551 return eLTRChar;
1552 }
1553 if (12540 <= c && c <= 12880) {
1554 return eLTRChar;
1555 }
1556 if (12896 <= c && c <= 12976) {
1557 return eLTRChar;
1558 }
1559 if (12992 <= c && c <= 42127) {
1560 return eLTRChar;
1561 }
1562 if (42183 <= c && c <= 64284) {
1563 return eLTRChar;
1564 }
1565 if (64285 <= c && c <= 64285) {
1566 return eRTLChar;
1567 }
1568 if (64287 <= c && c <= 64296) {
1569 return eRTLChar;
1570 }
1571 if (64298 <= c && c <= 64829) {
1572 return eRTLChar;
1573 }
1574 if (64832 <= c && c <= 65023) {
1575 return eRTLChar;
1576 }
1577 if (65040 <= c && c <= 65055) {
1578 return eLTRChar;
1579 }
1580 if (65060 <= c && c <= 65071) {
1581 return eLTRChar;
1582 }
1583 if (65095 <= c && c <= 65096) {
1584 return eLTRChar;
1585 }
1586 if (65107 <= c && c <= 65107) {
1587 return eLTRChar;
1588 }
1589 if (65127 <= c && c <= 65127) {
1590 return eLTRChar;
1591 }
1592 if (65132 <= c && c <= 65135) {
1593 return eLTRChar;
1594 }
1595 if (65136 <= c && c <= 65278) {
1596 return eRTLChar;
1597 }
1598 if (65280 <= c && c <= 65280) {
1599 return eLTRChar;
1600 }
1601 if (65313 <= c && c <= 65338) {
1602 return eLTRChar;
1603 }
1604 if (65345 <= c && c <= 65370) {
1605 return eLTRChar;
1606 }
1607 if (65382 <= c && c <= 65503) {
1608 return eLTRChar;
1609 }
1610 if (65511 <= c && c <= 65511) {
1611 return eLTRChar;
1612 }
1613 if (65519 <= c && c <= 65528) {
1614 return eLTRChar;
1615 }
1616 if (65534 <= c && c <= 65534) {
1617 return eLTRChar;
1618 }
1619 return eNeutralChar;
1620}
1621#endif
#define RequireNotNull(p)
Definition Assertions.h:347
#define Verify(c)
Definition Assertions.h:419
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...