in src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs [856:1259]
private void ProcessIsolatedRunSequence(BidiClass sos, BidiClass eos, int runLevel)
{
// Create mappings onto the underlying data
var isolatedRunMapping = _isolatedRunMapping.AsSlice();
_runResolvedClasses = new MappedArraySlice<BidiClass>(_workingClasses, isolatedRunMapping);
_runOriginalClasses = new MappedArraySlice<BidiClass>(_originalClasses, isolatedRunMapping);
_runLevels = new MappedArraySlice<sbyte>(_resolvedLevels, isolatedRunMapping);
if (_hasBrackets)
{
_runBiDiPairedBracketTypes = new MappedArraySlice<BidiPairedBracketType>(_pairedBracketTypes, isolatedRunMapping);
_runPairedBracketValues = new MappedArraySlice<int>(_pairedBracketValues, isolatedRunMapping);
}
_runLevel = runLevel;
_runDirection = DirectionFromLevel(runLevel);
_runLength = _runResolvedClasses.Length;
// Rule W1
// Also, set hasXX flags
int i;
var previousClass = sos;
const uint isolateMask =
(1U << (int)BidiClass.LeftToRightIsolate) |
(1U << (int)BidiClass.RightToLeftIsolate) |
(1U << (int)BidiClass.FirstStrongIsolate) |
(1U << (int)BidiClass.PopDirectionalIsolate);
const uint wRulesMask =
(1U << (int)BidiClass.EuropeanNumber) |
(1U << (int)BidiClass.ArabicLetter) |
(1U << (int)BidiClass.EuropeanSeparator) |
(1U << (int)BidiClass.CommonSeparator) |
(1U << (int)BidiClass.ArabicNumber) |
(1U << (int)BidiClass.EuropeanTerminator);
uint wRules = 0;
for (i = 0; i < _runLength; i++)
{
var resolvedClass = _runResolvedClasses[i];
if (resolvedClass == BidiClass.NonspacingMark)
{
_runResolvedClasses[i] = previousClass;
}
else
{
var classBit = 1U << (int)resolvedClass;
if ((classBit & isolateMask) != 0U)
{
previousClass = BidiClass.OtherNeutral;
}
else
{
wRules |= classBit & wRulesMask;
previousClass = resolvedClass;
}
}
}
// By tracking the types of characters known to be in the current run, we can
// skip some of the rules that we know won't apply.
var hasEN = (wRules & (1U << (int)BidiClass.EuropeanNumber)) != 0U;
var hasAL = (wRules & (1U << (int)BidiClass.ArabicLetter)) != 0U;
var hasES = (wRules & (1U << (int)BidiClass.EuropeanSeparator)) != 0U;
var hasCS = (wRules & (1U << (int)BidiClass.CommonSeparator)) != 0U;
var hasAN = (wRules & (1U << (int)BidiClass.ArabicNumber)) != 0U;
var hasET = (wRules & (1U << (int)BidiClass.EuropeanTerminator)) != 0U;
// Rule W2
if (hasEN)
{
for (i = 0; i < _runLength; i++)
{
if (_runResolvedClasses[i] != BidiClass.EuropeanNumber)
{
continue;
}
for (var j = i - 1; j >= 0; j--)
{
var resolvedClass = _runResolvedClasses[j];
switch (resolvedClass)
{
case BidiClass.LeftToRight:
case BidiClass.RightToLeft:
case BidiClass.ArabicLetter:
{
if (resolvedClass == BidiClass.ArabicLetter)
{
_runResolvedClasses[i] = BidiClass.ArabicNumber;
hasAN = true;
}
j = -1;
break;
}
}
}
}
}
// Rule W3
if (hasAL)
{
for (i = 0; i < _runLength; i++)
{
if (_runResolvedClasses[i] == BidiClass.ArabicLetter)
{
_runResolvedClasses[i] = BidiClass.RightToLeft;
}
}
}
// Rule W4
if ((hasES || hasCS) && (hasEN || hasAN))
{
for (i = 1; i < _runLength - 1; ++i)
{
ref var resolvedClass = ref _runResolvedClasses[i];
if (resolvedClass == BidiClass.EuropeanSeparator)
{
var previousSeparatorClass = _runResolvedClasses[i - 1];
var nextSeparatorClass = _runResolvedClasses[i + 1];
if (previousSeparatorClass == BidiClass.EuropeanNumber && nextSeparatorClass == BidiClass.EuropeanNumber)
{
// ES between EN and EN
resolvedClass = BidiClass.EuropeanNumber;
}
}
else if (resolvedClass == BidiClass.CommonSeparator)
{
var previousSeparatorClass = _runResolvedClasses[i - 1];
var nextSeparatorClass = _runResolvedClasses[i + 1];
if ((previousSeparatorClass == BidiClass.ArabicNumber && nextSeparatorClass == BidiClass.ArabicNumber) ||
(previousSeparatorClass == BidiClass.EuropeanNumber && nextSeparatorClass == BidiClass.EuropeanNumber))
{
// CS between (AN and AN) or (EN and EN)
resolvedClass = previousSeparatorClass;
}
}
}
}
// Rule W5
if (hasET && hasEN)
{
for (i = 0; i < _runLength; ++i)
{
if (_runResolvedClasses[i] != BidiClass.EuropeanTerminator)
{
continue;
}
// Locate end of sequence
var sequenceStart = i;
var sequenceEnd = i;
while (sequenceEnd < _runLength && _runResolvedClasses[sequenceEnd] == BidiClass.EuropeanTerminator)
{
sequenceEnd++;
}
// Preceded by, or followed by EN?
if ((sequenceStart == 0 ? sos : _runResolvedClasses[sequenceStart - 1]) == BidiClass.EuropeanNumber
|| (sequenceEnd == _runLength ? eos : _runResolvedClasses[sequenceEnd]) == BidiClass.EuropeanNumber)
{
// Change the entire range
for (var j = sequenceStart; i < sequenceEnd; ++i)
{
_runResolvedClasses[i] = BidiClass.EuropeanNumber;
}
}
// continue at end of sequence
i = sequenceEnd;
}
}
// Rule W6
if (hasES || hasET || hasCS)
{
for (i = 0; i < _runLength; ++i)
{
ref var resolvedClass = ref _runResolvedClasses[i];
switch (resolvedClass)
{
case BidiClass.EuropeanSeparator:
case BidiClass.EuropeanTerminator:
case BidiClass.CommonSeparator:
{
resolvedClass = BidiClass.OtherNeutral;
break;
}
}
}
}
// Rule W7.
if (hasEN)
{
var previousStrongClass = sos;
for (i = 0; i < _runLength; ++i)
{
ref var resolvedClass = ref _runResolvedClasses[i];
switch (resolvedClass)
{
case BidiClass.EuropeanNumber:
{
// If prev strong type was an L change this to L too
if (previousStrongClass == BidiClass.LeftToRight)
{
_runResolvedClasses[i] = BidiClass.LeftToRight;
}
break;
}
case BidiClass.LeftToRight:
case BidiClass.RightToLeft:
{
// Remember previous strong type (NB: AL should already be changed to R)
previousStrongClass = resolvedClass;
break;
}
}
}
}
// Rule N0 - process bracket pairs
if (_hasBrackets)
{
int count;
var pairedBrackets = LocatePairedBrackets();
for (i = 0, count = pairedBrackets.Count; i < count; i++)
{
var pairedBracket = pairedBrackets[i];
var strongDirection = InspectPairedBracket(pairedBracket);
// Case "d" - no strong types in the brackets, ignore
if (strongDirection == BidiClass.OtherNeutral)
{
continue;
}
// Case "b" - strong type found that matches the embedding direction
if ((strongDirection == BidiClass.LeftToRight || strongDirection == BidiClass.RightToLeft) && strongDirection == _runDirection)
{
SetPairedBracketDirection(pairedBracket, strongDirection);
continue;
}
// Case "c" - found opposite strong type found, look before to establish context
strongDirection = InspectBeforePairedBracket(pairedBracket, sos);
if (strongDirection == _runDirection || strongDirection == BidiClass.OtherNeutral)
{
strongDirection = _runDirection;
}
SetPairedBracketDirection(pairedBracket, strongDirection);
}
}
// Rules N1 and N2 - resolve neutral types
for (i = 0; i < _runLength; ++i)
{
var resolvedClass = _runResolvedClasses[i];
if (IsNeutralClass(resolvedClass))
{
// Locate end of sequence
var seqStart = i;
var seqEnd = i;
while (seqEnd < _runLength && IsNeutralClass(_runResolvedClasses[seqEnd]))
{
seqEnd++;
}
// Work out the preceding class
BidiClass classBefore;
if (seqStart == 0)
{
classBefore = sos;
}
else
{
classBefore = _runResolvedClasses[seqStart - 1];
switch (classBefore)
{
case BidiClass.ArabicNumber:
case BidiClass.EuropeanNumber:
{
classBefore = BidiClass.RightToLeft;
break;
}
}
}
// Work out the following class
BidiClass classAfter;
if (seqEnd == _runLength)
{
classAfter = eos;
}
else
{
classAfter = _runResolvedClasses[seqEnd];
switch (classAfter)
{
case BidiClass.ArabicNumber:
case BidiClass.EuropeanNumber:
{
classAfter = BidiClass.RightToLeft;
break;
}
}
}
// Work out the final resolved type
BidiClass finalResolveClass;
if (classBefore == classAfter)
{
// Rule N1
finalResolveClass = classBefore;
}
else
{
// Rule N2
finalResolveClass = _runDirection;
}
// Apply changes
for (var j = seqStart; j < seqEnd; j++)
{
_runResolvedClasses[j] = finalResolveClass;
}
// continue after this run
i = seqEnd;
}
}
// Rules I1 and I2 - resolve implicit types
if ((_runLevel & 0x01) == 0)
{
// Rule I1 - even
for (i = 0; i < _runLength; i++)
{
var resolvedClass = _runResolvedClasses[i];
ref var currentRunLevel = ref _runLevels[i];
switch (resolvedClass)
{
case BidiClass.RightToLeft:
{
currentRunLevel++;
break;
}
case BidiClass.ArabicNumber:
case BidiClass.EuropeanNumber:
{
currentRunLevel += 2;
break;
}
}
}
}
else
{
// Rule I2 - odd
for (i = 0; i < _runLength; i++)
{
var resolvedClass = _runResolvedClasses[i];
ref var currentRunLevel = ref _runLevels[i];
if (resolvedClass != BidiClass.RightToLeft)
{
currentRunLevel++;
}
}
}
}