private static sbyte[] ProcessEmbeddings(sbyte[] resultTypes, sbyte paragraphEmbeddingLevel)
{
int EXPLICIT_LEVEL_LIMIT = 62;
int textLength = resultTypes.Length;
sbyte[] embeddings = new sbyte[textLength];
// This stack will store the embedding levels and override status in a single sbyte
// as described above.
sbyte[] embeddingValueStack = new sbyte[EXPLICIT_LEVEL_LIMIT];
int stackCounter = 0;
// An LRE or LRO at level 60 is invalid, since the new level 62 is invalid. But
// an RLE at level 60 is valid, since the new level 61 is valid. The current wording
// of the rules requires that the RLE remain valid even if a previous LRE is invalid.
// This keeps track of ignored LRE or LRO codes at level 60, so that the matching PDFs
// will not try to pop the stack.
int overflowAlmostCounter = 0;
// This keeps track of ignored pushes at level 61 or higher, so that matching PDFs will
// not try to pop the stack.
int overflowCounter = 0;
// Rule X1.
// Keep the level separate from the value (level | override status flag) for ease of access.
sbyte currentEmbeddingLevel = paragraphEmbeddingLevel;
sbyte currentEmbeddingValue = paragraphEmbeddingLevel;
// Loop through types, handling all remaining rules
for (int i = 0; i < textLength; ++i) {
embeddings[i] = currentEmbeddingValue;
sbyte t = resultTypes[i];
// Rules X2, X3, X4, X5
switch (t) {
case RLE:
case LRE:
case RLO:
case LRO:
// Only need to compute new level if current level is valid
if (overflowCounter == 0) {
sbyte newLevel;
if (t == RLE || t == RLO) {
newLevel = (sbyte)((currentEmbeddingLevel + 1) | 1); // least greater odd
} else { // t == LRE || t == LRO
newLevel = (sbyte)((currentEmbeddingLevel + 2) & ~1); // least greater even
}
// If the new level is valid, push old embedding level and override status
// No check for valid stack counter, since the level check suffices.
if (newLevel < EXPLICIT_LEVEL_LIMIT) {
embeddingValueStack[stackCounter] = currentEmbeddingValue;
stackCounter++;
currentEmbeddingLevel = newLevel;
if (t == LRO || t == RLO) { // override
currentEmbeddingValue = (sbyte)((byte)newLevel | 0x80);
} else {
currentEmbeddingValue = newLevel;
}
// Adjust level of format mark (for expositional purposes only, this gets
// removed later).
embeddings[i] = currentEmbeddingValue;
break;
}
// Otherwise new level is invalid, but a valid level can still be achieved if this
// level is 60 and we encounter an RLE or RLO further on. So record that we
// 'almost' overflowed.
if (currentEmbeddingLevel == 60) {
overflowAlmostCounter++;
break;
}
}
// Otherwise old or new level is invalid.
overflowCounter++;
break;
case PDF:
// The only case where this did not actually overflow but may have almost overflowed
// is when there was an RLE or RLO on level 60, which would result in level 61. So we
// only test the almost overflow condition in that case.
//
// Also note that there may be a PDF without any pushes at all.
if (overflowCounter > 0) {
--overflowCounter;
} else if (overflowAlmostCounter > 0 && currentEmbeddingLevel != 61) {
--overflowAlmostCounter;
} else if (stackCounter > 0) {
--stackCounter;
currentEmbeddingValue = embeddingValueStack[stackCounter];
currentEmbeddingLevel = (sbyte)(currentEmbeddingValue & 0x7f);
}
break;
case B:
// Rule X8.
// These values are reset for clarity, in this implementation B can only
// occur as the last code in the array.
stackCounter = 0;
overflowCounter = 0;
overflowAlmostCounter = 0;
currentEmbeddingLevel = paragraphEmbeddingLevel;
currentEmbeddingValue = paragraphEmbeddingLevel;
embeddings[i] = paragraphEmbeddingLevel;
break;
default:
break;
}
}
return embeddings;
}