// 3.3.1.P1 - Split the text into separate paragraphs. // A paragraph separator is kept with the previous paragraph. // Within each paragraph, apply all the other rules of this algorithm. public static Paragraph[] SplitStringToParagraphs(string logicalString) { ArrayList ret = new ArrayList(); int i; StringBuilder sb = new StringBuilder(); for (i = 0; i < logicalString.Length; ++i) { char c = logicalString[i]; BidiCharacterType cType = UnicodeCharacterDataResolver.GetBidiCharacterType(c); if (cType == BidiCharacterType.B) { Paragraph p = new Paragraph(sb.ToString()); p.ParagraphSeparator = c; ret.Add(p); sb.Length = 0; } else { sb.Append(c); } } if (sb.Length > 0) // string ended without a paragraph separator { ret.Add(new Paragraph(sb.ToString())); } return((Paragraph[])ret.ToArray(typeof(Paragraph))); }
private StringBuilder InternalDecompose(ArrayList char_lengths) { StringBuilder target = new StringBuilder(); StringBuilder buffer = new StringBuilder(); _hasArabic = false; _hasNSMs = false; for (int i = 0; i < _text.Length; ++i) { BidiCharacterType ct = UnicodeCharacterDataResolver.GetBidiCharacterType(_text[i]); _hasArabic |= ((ct == BidiCharacterType.AL) || (ct == BidiCharacterType.AN)); _hasNSMs |= (ct == BidiCharacterType.NSM); buffer.Length = 0; GetRecursiveDecomposition(false, _text[i], buffer); char_lengths.Add(1 - buffer.Length); // add all of the characters in the decomposition. // (may be just the original character, if there was // no decomposition mapping) char ch; for (int j = 0; j < buffer.Length; ++j) { ch = buffer[j]; UnicodeCanonicalClass chClass = UnicodeCharacterDataResolver.GetUnicodeCanonicalClass(ch); int k = target.Length; // insertion point if (chClass != UnicodeCanonicalClass.NR) { // bubble-sort combining marks as necessary char ch2; for (; k > 0; --k) { ch2 = target[k - 1]; if (UnicodeCharacterDataResolver.GetUnicodeCanonicalClass(ch2) <= chClass) { break; } } } target.Insert(k, ch); } } return(target); }
// 3.3.1 The Paragraph Level // P2 - In each paragraph, find the first character of type L, AL, or R. // P3 - If a character is found in P2 and it is of type AL or R, then // set the paragraph embedding level to one; otherwise, set it to zero. public void RecalculateParagraphEmbeddingLevel() { embedding_level = 1; foreach (char c in _text) { BidiCharacterType cType = UnicodeCharacterDataResolver.GetBidiCharacterType(c); if (cType == BidiCharacterType.R || cType == BidiCharacterType.AL) { embedding_level = 1; break; } else if (cType == BidiCharacterType.L) { break; } } }
// 3.3.2 Explicit Levels and Directions public void RecalculateCharactersEmbeddingLevels() { // This method is implemented in such a way it handles the string in logical order, // rather than visual order, so it is easier to handle complex layouts. That is why // it is placed BEFORE ReorderString rather than AFTER it, as its number suggests. if (_hasArabic) { _text = PerformArabicShaping(_text); } _text_data = new CharData[_text.Length]; #region rules X1 - X9 // X1 byte embeddingLevel = EmbeddingLevel; DirectionalOverrideStatus dos = DirectionalOverrideStatus.Neutral; Stack dosStack = new Stack(); Stack elStack = new Stack(); int idx = 0; for (int i = 0; i < _text.Length; ++i) { bool x9Char = false; char c = _text[i]; _text_data[i]._ct = UnicodeCharacterDataResolver.GetBidiCharacterType(c); _text_data[i]._char = c; _text_data[i]._idx = idx; idx += _char_lengths[i]; #region rules X2 - X5 // X2. With each RLE, compute the least greater odd embedding level. // X4. With each RLO, compute the least greater odd embedding level. if (c == BidiChars.RLE || c == BidiChars.RLO) { x9Char = true; if (embeddingLevel < 60) { elStack.Push(embeddingLevel); dosStack.Push(dos); ++embeddingLevel; embeddingLevel |= 1; if (c == BidiChars.RLE) { dos = DirectionalOverrideStatus.Neutral; } else { dos = DirectionalOverrideStatus.RTL; } } } // X3. With each LRE, compute the least greater even embedding level. // X5. With each LRO, compute the least greater even embedding level. else if (c == BidiChars.LRE || c == BidiChars.LRO) { x9Char = true; if (embeddingLevel < 59) { elStack.Push(embeddingLevel); dosStack.Push(dos); embeddingLevel |= 1; ++embeddingLevel; if (c == BidiChars.LRE) { dos = DirectionalOverrideStatus.Neutral; } else { dos = DirectionalOverrideStatus.LTR; } } } #endregion #region rule X6 // X6. For all types besides RLE, LRE, RLO, LRO, and PDF: (...) else if (c != BidiChars.PDF) { // a. Set the level of the current character to the current embedding level. _text_data[i]._el = embeddingLevel; //b. Whenever the directional override status is not neutral, //reset the current character type to the directional override status. if (dos == DirectionalOverrideStatus.LTR) { _text_data[i]._ct = BidiCharacterType.L; } else if (dos == DirectionalOverrideStatus.RTL) { _text_data[i]._ct = BidiCharacterType.R; } } #endregion #region rule X7 //Terminating Embeddings and Overrides // X7. With each PDF, determine the matching embedding or override code. // If there was a valid matching code, restore (pop) the last remembered (pushed) // embedding level and directional override. else if (c == BidiChars.PDF) { x9Char = true; if (elStack.Count > 0) { embeddingLevel = (byte)(elStack.Pop()); dos = (DirectionalOverrideStatus)(dosStack.Pop()); } } #endregion // X8. All explicit directional embeddings and overrides are completely // terminated at the end of each paragraph. Paragraph separators are not // included in the embedding. if (x9Char || _text_data[i]._ct == BidiCharacterType.BN) { _text_data[i]._el = embeddingLevel; } } #endregion // X10. The remaining rules are applied to each run of characters at the same level. int prevLevel = EmbeddingLevel; int start = 0; while (start < _text.Length) { #region rule X10 - run level setup byte level = _text_data[start]._el; BidiCharacterType sor = TypeForLevel(Math.Max(prevLevel, level)); int limit = start + 1; while (limit < _text.Length && _text_data[limit]._el == level) { ++limit; } byte nextLevel = limit < _text.Length ? _text_data[limit]._el : EmbeddingLevel; BidiCharacterType eor = TypeForLevel(Math.Max(nextLevel, level)); #endregion ResolveWeakTypes(start, limit, sor, eor); ResolveNeutralTypes(start, limit, sor, eor, level); ResolveImplicitTypes(start, limit, level); prevLevel = level; start = limit; } // Wrap lines ReorderString(0, _text.Length); FixMirroredCharacters(); ArrayList indexes = new ArrayList(); ArrayList lengths = new ArrayList(); StringBuilder sb = new StringBuilder(); foreach (CharData cd in _text_data) { sb.Append(cd._char); indexes.Add(cd._idx); lengths.Add(1); } _bidi_text = sb.ToString(); _bidi_indexes = (int[])indexes.ToArray(typeof(int)); }