// RichTextKit // Copyright © 2019-2020 Topten Software. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); you may // not use this product except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Topten.RichTextKit.Utils; namespace Topten.RichTextKit { /// /// Represents a unicode string and all associated attributes /// for each character required for the Bidi algorithm /// class BidiData { /// /// Construct a new empty BidiData /// public BidiData() { _types = new Buffer(); _pairedBracketTypes= new Buffer(); _pairedBracketValues = new Buffer(); } List _paragraphPositions = new List(); sbyte _paragraphEmbeddingLevel; public sbyte ParagraphEmbeddingLevel => _paragraphEmbeddingLevel; bool _hasBrackets; public bool HasBrackets => _hasBrackets; bool _hasEmbeddings; public bool HasEmbeddings => _hasEmbeddings; bool _hasIsolates; public bool HasIsolates => _hasIsolates; /// /// Initialize with an array of Unicode code points /// /// The unicode code points to be processed /// The paragraph embedding level public void Init(Slice codePoints, sbyte paragraphEmbeddingLevel) { // Set working buffer sizes _types.Length = codePoints.Length; _pairedBracketTypes.Length = codePoints.Length; _pairedBracketValues.Length = codePoints.Length; _paragraphPositions.Clear(); _paragraphEmbeddingLevel = paragraphEmbeddingLevel; // Resolve the directionality, paired bracket type and paired bracket values for // all code points _hasBrackets = false; _hasEmbeddings = false; _hasIsolates = false; for (int i = 0; i < codePoints.Length; i++) { var bidiData = UnicodeClasses.BidiData(codePoints[i]); // Look up directionality var dir = (Directionality)(bidiData >> 24); _types[i] = dir; switch (dir) { case Directionality.LRE: case Directionality.LRO: case Directionality.RLE: case Directionality.RLO: case Directionality.PDF: _hasEmbeddings = true; break; case Directionality.LRI: case Directionality.RLI: case Directionality.FSI: case Directionality.PDI: _hasIsolates = true; break; } // Lookup paired bracket types var pbt = (PairedBracketType)((bidiData >> 16) & 0xFF); _pairedBracketTypes[i] = pbt; switch (pbt) { case PairedBracketType.o: _pairedBracketValues[i] = MapCanon((int)(bidiData & 0xFFFF)); _hasBrackets = true; break; case PairedBracketType.c: _pairedBracketValues[i] = MapCanon(codePoints[i]); _hasBrackets = true; break; } /* if (_types[i] == RichTextKit.Directionality.B) { _types[i] = (Directionality)Directionality.WS; _paragraphPositions.Add(i); } */ } // Create slices on work buffers Types = _types.AsSlice(); PairedBracketTypes = _pairedBracketTypes.AsSlice(); PairedBracketValues = _pairedBracketValues.AsSlice(); } /// /// Map bracket types 0x3008 and 0x3009 to their canonical equivalents /// /// The code point to be mapped /// The mapped canonical code point, or the passed code point static int MapCanon(int codePoint) { if (codePoint == 0x3008) return 0x2329; if (codePoint == 0x3009) return 0x232A; else return codePoint; } /// /// Get the length of the data held by the BidiData /// public int Length => _types.Length; Buffer _types; Buffer _pairedBracketTypes; Buffer _pairedBracketValues; Buffer _savedTypes; Buffer _savedPairedBracketTypes; /// /// Save the Types and PairedBracketTypes of this bididata /// /// /// This is used when processing embedded style runs with /// directionality overrides. TextBlock saves the data, /// overrides the style runs to neutral, processes the bidi /// data for the entire paragraph and then restores this data /// before processing the embedded runs. /// public void SaveTypes() { // Make sure we have a buffer if (_savedTypes == null) { _savedTypes = new Buffer(); _savedPairedBracketTypes = new Buffer(); } // Capture the types data _savedTypes.Clear(); _savedTypes.Add(_types.AsSlice()); _savedPairedBracketTypes.Clear(); _savedPairedBracketTypes.Add(_pairedBracketTypes.AsSlice()); } /// /// Restore the data saved by SaveTypes /// public void RestoreTypes() { _types.Clear(); _types.Add(_savedTypes.AsSlice()); _pairedBracketTypes.Clear(); _pairedBracketTypes.Add(_savedPairedBracketTypes.AsSlice()); } /// /// The directionality of each code point /// public Slice Types; /// /// The paired bracket type for each code point /// public Slice PairedBracketTypes; /// /// The paired bracket value for code code point /// /// /// The paired bracket values are the code points /// of each character where the opening code point /// is replaced with the closing code point for easier /// matching. Also, bracket code points are mapped /// to their canonical equivalents /// public Slice PairedBracketValues; public Buffer _tempLevelBuffer; /// /// Gets a temporary level buffer. Used by TextBlock when /// resolving style runs with different directionality. /// /// Length of the required buffer /// An uninitialized level buffer public Slice GetTempLevelBuffer(int length) { if (_tempLevelBuffer == null) _tempLevelBuffer = new Buffer(); _tempLevelBuffer.Clear(); return _tempLevelBuffer.Add(length, false); } } }