private bool Run(BiDiTestData testData) { var bidi = new BidiAlgorithm(); // Run the algorithm... ArraySlice <sbyte> resultLevels; bidi.Process( testData.Classes, ArraySlice <BidiPairedBracketType> .Empty, ArraySlice <int> .Empty, testData.ParagraphEmbeddingLevel, false, null, null, null); resultLevels = bidi.ResolvedLevels; // Check the results match var pass = true; if (resultLevels.Length == testData.Levels.Length) { for (var i = 0; i < testData.Levels.Length; i++) { if (testData.Levels[i] == -1) { continue; } if (resultLevels[i] != testData.Levels[i]) { pass = false; break; } } } else { pass = false; } if (!pass) { _outputHelper.WriteLine($"Failed line {testData.LineNumber}"); _outputHelper.WriteLine($" Data: {string.Join(" ", testData.Classes)}"); _outputHelper.WriteLine($" Embed Level: {testData.ParagraphEmbeddingLevel}"); _outputHelper.WriteLine($" Expected: {string.Join(" ", testData.Levels)}"); _outputHelper.WriteLine($" Actual: {string.Join(" ", resultLevels)}"); return(false); } return(true); }
private bool Run(BiDiClassData t) { var bidi = new BidiAlgorithm(); var bidiData = new BidiData(t.ParagraphLevel); var text = Encoding.UTF32.GetString(MemoryMarshal.Cast <int, byte>(t.CodePoints).ToArray()); // Append bidiData.Append(text.AsMemory()); // Act for (int i = 0; i < 10; i++) { bidi.Process(bidiData); } var resultLevels = bidi.ResolvedLevels; var resultParagraphLevel = bidi.ResolvedParagraphEmbeddingLevel; // Assert var passed = true; if (t.ResolvedParagraphLevel != resultParagraphLevel) { return(false); } for (var i = 0; i < t.ResolvedLevels.Length; i++) { if (t.ResolvedLevels[i] == -1) { continue; } if (t.ResolvedLevels[i] != resultLevels[i]) { passed = false; break; } } if (passed) { return(true); } _outputHelper.WriteLine($"Failed line {t.LineNumber}"); _outputHelper.WriteLine( $" Code Points: {string.Join(" ", t.CodePoints.Select(x => x.ToString("X4")))}"); _outputHelper.WriteLine( $" Pair Bracket Types: {string.Join(" ", bidiData.PairedBracketTypes.Select(x => " " + x.ToString()))}"); _outputHelper.WriteLine( $" Pair Bracket Values: {string.Join(" ", bidiData.PairedBracketValues.Select(x => x.ToString("X4")))}"); _outputHelper.WriteLine($" Embed Level: {t.ParagraphLevel}"); _outputHelper.WriteLine($" Expected Embed Level: {t.ResolvedParagraphLevel}"); _outputHelper.WriteLine($" Actual Embed Level: {resultParagraphLevel}"); _outputHelper.WriteLine($" Directionality: {string.Join(" ", bidiData.Classes)}"); _outputHelper.WriteLine($" Expected Levels: {string.Join(" ", t.ResolvedLevels)}"); _outputHelper.WriteLine($" Actual Levels: {string.Join(" ", resultLevels)}"); return(false); }
/// <summary> /// Shape specified text runs with specified paragraph embedding. /// </summary> /// <param name="textRuns">The text runs to shape.</param> /// <param name="paragraphProperties">The default paragraph properties.</param> /// <param name="resolvedFlowDirection">The resolved flow direction.</param> /// <returns> /// A list of shaped text characters. /// </returns> private static List <DrawableTextRun> ShapeTextRuns(List <TextRun> textRuns, TextParagraphProperties paragraphProperties, out FlowDirection resolvedFlowDirection) { var flowDirection = paragraphProperties.FlowDirection; var drawableTextRuns = new List <DrawableTextRun>(); var biDiData = new BidiData((sbyte)flowDirection); foreach (var textRun in textRuns) { if (textRun.Text.IsEmpty) { var text = new char[textRun.TextSourceLength]; biDiData.Append(text); } else { biDiData.Append(textRun.Text); } } var biDi = new BidiAlgorithm(); biDi.Process(biDiData); var resolvedEmbeddingLevel = biDi.ResolveEmbeddingLevel(biDiData.Classes); resolvedFlowDirection = (resolvedEmbeddingLevel & 1) == 0 ? FlowDirection.LeftToRight : FlowDirection.RightToLeft; var processedRuns = new List <TextRun>(textRuns.Count); foreach (var coalescedRuns in CoalesceLevels(textRuns, biDi.ResolvedLevels)) { processedRuns.AddRange(coalescedRuns); } for (var index = 0; index < processedRuns.Count; index++) { var currentRun = processedRuns[index]; switch (currentRun) { case DrawableTextRun drawableRun: { drawableTextRuns.Add(drawableRun); break; } case ShapeableTextCharacters shapeableRun: { var groupedRuns = new List <ShapeableTextCharacters>(2) { shapeableRun }; var text = currentRun.Text; var start = currentRun.Text.Start; var length = currentRun.Text.Length; var bufferOffset = currentRun.Text.BufferOffset; while (index + 1 < processedRuns.Count) { if (processedRuns[index + 1] is not ShapeableTextCharacters nextRun) { break; } if (shapeableRun.CanShapeTogether(nextRun)) { groupedRuns.Add(nextRun); length += nextRun.Text.Length; if (start > nextRun.Text.Start) { start = nextRun.Text.Start; } if (bufferOffset > nextRun.Text.BufferOffset) { bufferOffset = nextRun.Text.BufferOffset; } text = new ReadOnlySlice <char>(text.Buffer, start, length, bufferOffset); index++; shapeableRun = nextRun; continue; } break; } var shaperOptions = new TextShaperOptions(currentRun.Properties !.Typeface.GlyphTypeface, currentRun.Properties.FontRenderingEmSize, shapeableRun.BidiLevel, currentRun.Properties.CultureInfo, paragraphProperties.DefaultIncrementalTab); drawableTextRuns.AddRange(ShapeTogether(groupedRuns, text, shaperOptions)); break; } } } return(drawableTextRuns); }