public void BasicLatinTest() { var lineBreaker = new LineBreaker(); lineBreaker.Reset("Hello World\r\nThis is a test."); LineBreak b; Assert.True(lineBreaker.NextBreak(out b)); Assert.Equal(6, b.PositionWrap); Assert.False(b.Required); Assert.True(lineBreaker.NextBreak(out b)); Assert.Equal(13, b.PositionWrap); Assert.True(b.Required); Assert.True(lineBreaker.NextBreak(out b)); Assert.Equal(18, b.PositionWrap); Assert.False(b.Required); Assert.True(lineBreaker.NextBreak(out b)); Assert.Equal(21, b.PositionWrap); Assert.False(b.Required); Assert.True(lineBreaker.NextBreak(out b)); Assert.Equal(23, b.PositionWrap); Assert.False(b.Required); Assert.True(lineBreaker.NextBreak(out b)); Assert.Equal(28, b.PositionWrap); Assert.False(b.Required); Assert.False(lineBreaker.NextBreak(out b)); }
internal float MeasureTextBoxHeight(Microsoft.ReportingServices.Rendering.RichText.TextBox textBox, FlowContext flowContext) { if (m_bitsGraphics == null) { CreateGraphics(); } float height = 0f; LineBreaker.Flow(textBox, m_bitsGraphics, FontCache, flowContext, keepLines: false, out height); return(height); }
public float MeasureTextBoxHeight(AspNetCore.ReportingServices.Rendering.RichText.TextBox textBox, FlowContext flowContext) { if (this.m_bitsGraphics == null) { this.CreateGraphics(); } float result = 0f; LineBreaker.Flow(textBox, this.m_bitsGraphics, this.FontCache, flowContext, false, out result); return(result); }
public static void ReadingFile(string fileName) { string str; using (StreamReader sr = new StreamReader(fileName)) { while ((str = sr.ReadLine()) != null) { string[] words = LineBreaker.ReturnWordArr('|', str, 0); Greeting.ListOfBooks.Add(new Book(words[0], words[1], words[2], words[3], words[4], words[5])); } } }
void ForwardTest() { var lineBreaker = new LineBreaker(); lineBreaker.Reset("Apples Pears Bananas"); var positionsF = lineBreaker.GetBreaks().ToList(); Assert.Equal(7, positionsF[0].PositionWrap); Assert.Equal(6, positionsF[0].PositionMeasure); Assert.Equal(13, positionsF[1].PositionWrap); Assert.Equal(12, positionsF[1].PositionMeasure); Assert.Equal(20, positionsF[2].PositionWrap); Assert.Equal(20, positionsF[2].PositionMeasure); }
void ForwardTextWithOuterWhitespace() { var lineBreaker = new LineBreaker(); lineBreaker.Reset(" Apples Pears Bananas "); var positionsF = lineBreaker.GetBreaks().ToList(); Assert.Equal(1, positionsF[0].PositionWrap); Assert.Equal(0, positionsF[0].PositionMeasure); Assert.Equal(8, positionsF[1].PositionWrap); Assert.Equal(7, positionsF[1].PositionMeasure); Assert.Equal(14, positionsF[2].PositionWrap); Assert.Equal(13, positionsF[2].PositionMeasure); Assert.Equal(24, positionsF[3].PositionWrap); Assert.Equal(21, positionsF[3].PositionMeasure); }
public TextLayout(string text, TextFont font, BoxAlignment alignment, StringTrimming trimming, Vector2 maxSize) { textLines = LineBreaker.Split(text, (int)Math.Ceiling(maxSize.X), c => font.GetGlyph(c).Width); var glyphIndex = 0; var width = 0.0f; var height = 0.0f; foreach (var textLine in textLines) { var line = new TextLayoutLine(this, height, alignment, lines.Count == 0); foreach (var c in textLine) { line.Add(font.GetGlyph(c), glyphIndex++); } // trimming != StringTrimming.None && //if (maxSize.Y > 0 && height + line.Height > maxSize.Y) // break; lines.Add(line); width = Math.Max(width, line.Width); height += line.Height; } if (lines.Count == 0) { lines.Add(new TextLayoutLine(this, 0, alignment, true)); } var lastLine = lines[lines.Count - 1]; if (lastLine.GlyphCount == 0) { height += font.LineHeight; } lastLine.Add(new FontGlyph(null, 0, font.LineHeight), glyphIndex++); size = new Vector2(width, height); }
/// <summary> /// Generates the layout. /// </summary> /// <param name="text">The text.</param> /// <param name="options">The style.</param> /// <returns>A collection of layout that describe all thats needed to measure or render a series of glyphs.</returns> public IReadOnlyList <GlyphLayout> GenerateLayout(ReadOnlySpan <char> text, RendererOptions options) { if (text.IsEmpty) { return(Array.Empty <GlyphLayout>()); } var dpi = new Vector2(options.DpiX, options.DpiY); Vector2 origin = options.Origin / dpi; float maxWidth = float.MaxValue; float originX = 0; if (options.WrappingWidth > 0) { // trim trailing white spaces from the text text = text.TrimEnd(null); maxWidth = options.WrappingWidth / options.DpiX; } // lets convert the text into codepoints Memory <int> codePointsMemory = LineBreaker.ToUtf32(text); if (codePointsMemory.IsEmpty) { return(Array.Empty <GlyphLayout>()); } Span <int> codepoints = codePointsMemory.Span; var lineBreaker = new LineBreaker(); lineBreaker.Reset(codePointsMemory); AppliedFontStyle spanStyle = options.GetStyle(0, codepoints.Length); var layout = new List <GlyphLayout>(codepoints.Length); float unscaledLineHeight = 0f; float lineHeight = 0f; float unscaledLineMaxAscender = 0f; float unscaledLineMaxDescender = 0f; float lineMaxAscender = 0f; float lineMaxDescender = 0f; Vector2 location = Vector2.Zero; float lineHeightOfFirstLine = 0; // Remember where the top of the layouted text is for accurate vertical alignment. // This is important because there is considerable space between the lineHeight at the glyph's ascender. float top = 0; bool firstLine = true; GlyphInstance?previousGlyph = null; float scale = 0; int lastWrappableLocation = -1; int nextWrappableLocation = codepoints.Length; bool nextWrappableRequired = false; bool startOfLine = true; float totalHeight = 0; int graphemeIndex = 0; if (lineBreaker.TryGetNextBreak(out LineBreak b)) { nextWrappableLocation = b.PositionWrap - 1; nextWrappableRequired = b.Required; } for (int i = 0; i < codepoints.Length; i++) { if (spanStyle.End < i) { spanStyle = options.GetStyle(i, codepoints.Length); previousGlyph = null; } int codePoint = codepoints[i]; GlyphInstance[] glyphs = spanStyle.GetGlyphLayers(codePoint, options.ColorFontSupport); if (glyphs.Length == 0) { return(FontsThrowHelper.ThrowGlyphMissingException <IReadOnlyList <GlyphLayout> >(codePoint)); } GlyphInstance?glyph = glyphs[0]; float fontHeight = glyph.Font.LineHeight * options.LineSpacing; if (fontHeight > unscaledLineHeight) { // get the larget lineheight thus far unscaledLineHeight = fontHeight; scale = glyph.Font.EmSize * 72; lineHeight = unscaledLineHeight * spanStyle.PointSize / scale; } if (glyph.Font.Ascender > unscaledLineMaxAscender) { unscaledLineMaxAscender = glyph.Font.Ascender; scale = glyph.Font.EmSize * 72; lineMaxAscender = unscaledLineMaxAscender * spanStyle.PointSize / scale; } if (Math.Abs(glyph.Font.Descender) > unscaledLineMaxDescender) { unscaledLineMaxDescender = Math.Abs(glyph.Font.Descender); scale = glyph.Font.EmSize * 72; lineMaxDescender = unscaledLineMaxDescender * spanStyle.PointSize / scale; } if (firstLine) { // Reset the line height for the first line to prevent initial lead. float unspacedLineHeight = lineHeight / options.LineSpacing; if (unspacedLineHeight > lineHeightOfFirstLine) { lineHeightOfFirstLine = unspacedLineHeight; } switch (options.VerticalAlignment) { case VerticalAlignment.Top: top = lineMaxAscender; break; case VerticalAlignment.Center: top = (lineMaxAscender / 2F) - (lineMaxDescender / 2F); break; case VerticalAlignment.Bottom: top = -lineMaxDescender; break; } } if ((options.WrappingWidth > 0 && nextWrappableLocation == i) || nextWrappableRequired) { // keep a record of where to wrap text and ensure that no line starts with white space for (int j = layout.Count - 1; j >= 0; j--) { if (!layout[j].IsWhiteSpace) { lastWrappableLocation = j + 1; break; } } } if (nextWrappableLocation == i) { if (lineBreaker.TryGetNextBreak(out b)) { nextWrappableLocation = b.PositionWrap - 1; nextWrappableRequired = b.Required; } } float glyphWidth = glyph.AdvanceWidth * spanStyle.PointSize / scale; float glyphHeight = glyph.Height * spanStyle.PointSize / scale; if (codepoints[i] != '\r' && codepoints[i] != '\n' && codepoints[i] != '\t' && codepoints[i] != ' ') { Vector2 glyphLocation = location; if (spanStyle.ApplyKerning && previousGlyph != null) { // if there is special instructions for this glyph pair use that width Vector2 scaledOffset = spanStyle.GetOffset(glyph, previousGlyph) * spanStyle.PointSize / scale; glyphLocation += scaledOffset; // only fix the 'X' of the current tracked location but use the actual 'X'/'Y' of the offset location.X = glyphLocation.X; } foreach (GlyphInstance?g in glyphs) { float w = g.AdvanceWidth * spanStyle.PointSize / scale; float h = g.Height * spanStyle.PointSize / scale; layout.Add(new GlyphLayout(graphemeIndex, codePoint, new Glyph(g, spanStyle.PointSize), glyphLocation, w, h, lineHeight, startOfLine, false, false)); if (w > glyphWidth) { glyphWidth = w; } } // Increment the index to signify we have moved on the a new cluster. graphemeIndex++; startOfLine = false; // move forward the actual width of the glyph, we are retaining the baseline location.X += glyphWidth; // if the word extended pass the end of the box, wrap it if (location.X >= maxWidth && lastWrappableLocation > 0) { if (lastWrappableLocation < layout.Count) { float wrappingOffset = layout[lastWrappableLocation].Location.X; startOfLine = true; // move the characters to the next line for (int j = lastWrappableLocation; j < layout.Count; j++) { if (layout[j].IsWhiteSpace) { wrappingOffset += layout[j].Width; layout.RemoveAt(j); j--; continue; } Vector2 current = layout[j].Location; layout[j] = new GlyphLayout(layout[j].GraphemeIndex, layout[j].CodePoint, layout[j].Glyph, new Vector2(current.X - wrappingOffset, current.Y + lineHeight), layout[j].Width, layout[j].Height, layout[j].LineHeight, startOfLine, layout[j].IsWhiteSpace, layout[j].IsControlCharacter); startOfLine = false; location.X = layout[j].Location.X + layout[j].Width; } location.Y += lineHeight; totalHeight += lineHeight; firstLine = false; lastWrappableLocation = -1; } } previousGlyph = glyph; } else if (codepoints[i] == '\r') { // carriage return resets the XX coordinate to 0 location.X = 0; previousGlyph = null; startOfLine = true; layout.Add(new GlyphLayout(-1, codePoint, new Glyph(glyph, spanStyle.PointSize), location, 0, glyphHeight, lineHeight, startOfLine, true, true)); startOfLine = false; } else if (codepoints[i] == '\n') { // carriage return resets the XX coordinate to 0 layout.Add(new GlyphLayout(-1, codePoint, new Glyph(glyph, spanStyle.PointSize), location, 0, glyphHeight, lineHeight, startOfLine, true, true)); location.X = 0; location.Y += lineHeight; totalHeight += lineHeight; unscaledLineHeight = 0; unscaledLineMaxAscender = 0; previousGlyph = null; firstLine = false; lastWrappableLocation = -1; startOfLine = true; } else if (codepoints[i] == '\t') { float tabStop = glyphWidth * spanStyle.TabWidth; float finalWidth = 0; if (tabStop > 0) { finalWidth = tabStop - (location.X % tabStop); } if (finalWidth < glyphWidth) { // if we are not going to tab atleast a glyph width add another tabstop to it ??? // should I be doing this? finalWidth += tabStop; } layout.Add(new GlyphLayout(-1, codePoint, new Glyph(glyph, spanStyle.PointSize), location, finalWidth, glyphHeight, lineHeight, startOfLine, true, false)); startOfLine = false; // advance to a position > width away that location.X += finalWidth; previousGlyph = null; } else if (codepoints[i] == ' ') { layout.Add(new GlyphLayout(-1, codePoint, new Glyph(glyph, spanStyle.PointSize), location, glyphWidth, glyphHeight, lineHeight, startOfLine, true, false)); startOfLine = false; location.X += glyphWidth; previousGlyph = null; } } var offsetY = new Vector2(0, top); switch (options.VerticalAlignment) { case VerticalAlignment.Center: offsetY += new Vector2(0, -(totalHeight / 2F)); break; case VerticalAlignment.Bottom: offsetY += new Vector2(0, -totalHeight); break; } Vector2 offsetX = Vector2.Zero; for (int i = 0; i < layout.Count; i++) { GlyphLayout glyphLayout = layout[i]; graphemeIndex = glyphLayout.GraphemeIndex; // Scan ahead getting the width. if (glyphLayout.StartOfLine) { float width = 0; for (int j = i; j < layout.Count; j++) { GlyphLayout current = layout[j]; int currentGraphemeIndex = current.GraphemeIndex; if (current.StartOfLine && (currentGraphemeIndex != graphemeIndex)) { // Leading graphemes are made up of multiple glyphs all marked as 'StartOfLine so we only // break when we are sure we have entered a new cluster or previously defined break. break; } width = current.Location.X + current.Width; } switch (options.HorizontalAlignment) { case HorizontalAlignment.Left: offsetX = new Vector2(originX, 0) + offsetY; break; case HorizontalAlignment.Right: offsetX = new Vector2(originX - width, 0) + offsetY; break; case HorizontalAlignment.Center: offsetX = new Vector2(originX - (width / 2F), 0) + offsetY; break; } } // TODO calculate an offset from the 'origin' based on TextAlignment for each line layout[i] = new GlyphLayout( glyphLayout.GraphemeIndex, glyphLayout.CodePoint, glyphLayout.Glyph, glyphLayout.Location + offsetX + origin, glyphLayout.Width, glyphLayout.Height, glyphLayout.LineHeight, glyphLayout.StartOfLine, glyphLayout.IsWhiteSpace, glyphLayout.IsControlCharacter); } return(layout); }
public void Test() { // Read the test file var location = System.IO.Path.GetDirectoryName(typeof(LineBreakTests).Assembly.Location); var lines = System.IO.File.ReadAllLines(System.IO.Path.Combine(location, "LineBreakTest.txt")); // Process each line int lineNumber = 0; foreach (var line in lines) { // Ignore deliberately skipped test? if (_skipLines.Contains(lineNumber)) { continue; } lineNumber++; // Split the line var parts = line.Split("#"); var test = parts[0].Trim(); // Ignore blank/comment only lines if (string.IsNullOrWhiteSpace(test)) { continue; } // Parse the test var p = 0; List <int> codePoints = new List <int>(); List <int> breakPoints = new List <int>(); while (p < test.Length) { // Ignore white space if (char.IsWhiteSpace(test[p])) { p++; continue; } if (test[p] == '×') { p++; continue; } if (test[p] == '÷') { breakPoints.Add(codePoints.Count); p++; continue; } int codePointPos = p; while (p < test.Length && IsHexDigit(test[p])) { p++; } var codePointStr = test.Substring(codePointPos, p - codePointPos); var codePoint = Convert.ToInt32(codePointStr, 16); codePoints.Add(codePoint); } // Run the line breaker and build a list of break points List <int> foundBreaks = new List <int>(); var lineBreaker = new LineBreaker(); lineBreaker.Reset(new Slice <int>(codePoints.ToArray())); while (lineBreaker.NextBreak(out var b)) { foundBreaks.Add(b.PositionWrap); } // Check the same Assert.Equal(breakPoints, foundBreaks); } }
public void LineBreakerTest(string text, LineBreak[] expectedLineBreaks) { var lb = new LineBreaker(text); Assert.Equal(expectedLineBreaks, lb.ToArray()); }
internal override void DrawContent(GdiContext context) { RPLTextBoxPropsDef rPLTextBoxPropsDef = DefinitionProperties as RPLTextBoxPropsDef; RPLTextBoxProps rPLTextBoxProps = InstanceProperties as RPLTextBoxProps; if (rPLTextBoxProps.IsToggleParent) { GetToggleImage(context.GdiWriter, out Bitmap image); if (m_toggleRectangleMM.Width > 0f) { DrawResourceImage(imageRectanglePX: new RectangleF(0f, 0f, image.Width, image.Height), context: context, image: image, itemRectangleMM: m_toggleRectangleMM); } } if (rPLTextBoxPropsDef.CanSort) { GetSortImage(context.GdiWriter, out Bitmap image2, out SortOrder _); if (m_sortRectangleMM.Width > 0f) { DrawResourceImage(imageRectanglePX: new RectangleF(0f, 0f, image2.Width, image2.Height), context: context, image: image2, itemRectangleMM: m_sortRectangleMM); } } if (TextPosition.Width <= 0f || TextPosition.Height <= 0f) { return; } RPLFormat.WritingModes writingMode = WritingMode; float contentHeight = 0f; float contentHeight2 = 0f; if (writingMode == RPLFormat.WritingModes.Horizontal) { contentHeight = rPLTextBoxProps.ContentHeight; } else { contentHeight2 = rPLTextBoxProps.ContentHeight; } List <Paragraph> list = null; List <RTSelectionHighlight> list2 = null; FlowContext flowContext = new FlowContext(TextPosition.Width, TextPosition.Height); bool flag = !rPLTextBoxPropsDef.CanGrow && !rPLTextBoxPropsDef.CanShrink; if (flag) { m_richTextBox.Paragraphs = new List <Paragraph>(m_paragraphs.Count); for (int i = 0; i < m_paragraphs.Count; i++) { RTSelectionHighlight searchHit = m_paragraphs[i].GetSearchHit(context); if (searchHit != null) { context.TextRunIndexHitStart = searchHit.SelectionStart.TextRunIndex; context.TextRunIndexHitEnd = searchHit.SelectionEnd.TextRunIndex; } m_paragraphs[i].DrawContent(context); m_richTextBox.Paragraphs.Add(m_paragraphs[i].RichParagraph); context.TextRunIndexHitStart = -1; context.TextRunIndexHitEnd = -1; } m_richTextBox.ScriptItemize(); if (writingMode == RPLFormat.WritingModes.Horizontal) { TextBox.MeasureFullHeight(m_richTextBox, context.Graphics, context.FontCache, flowContext, out contentHeight); } else { TextBox.MeasureFullHeight(m_richTextBox, context.Graphics, context.FontCache, flowContext, out contentHeight2); } list = m_richTextBox.Paragraphs; m_richTextBox.Paragraphs = null; } if (writingMode == RPLFormat.WritingModes.Horizontal) { if (contentHeight + 0.001f > TextPosition.Height) { flowContext.LineLimit = false; } } else if (contentHeight2 + 0.001f > TextPosition.Width) { flowContext.LineLimit = false; } m_richTextBox.Paragraphs = new List <Paragraph>(1); bool flag2 = true; float num = 0f; float num2 = 0f; for (int j = 0; j < m_paragraphs.Count; j++) { if (writingMode == RPLFormat.WritingModes.Horizontal) { if (num > 0f) { if (num >= TextPosition.Height) { break; } flowContext.Height = TextPosition.Height - num; } } else if (num2 > 0f) { if (num2 >= TextPosition.Width) { break; } flowContext.Width = TextPosition.Width - num2; } RTSelectionHighlight searchHit2 = m_paragraphs[j].GetSearchHit(context); if (!flag) { if (searchHit2 != null) { context.TextRunIndexHitStart = searchHit2.SelectionStart.TextRunIndex; context.TextRunIndexHitEnd = searchHit2.SelectionEnd.TextRunIndex; } m_paragraphs[j].DrawContent(context); m_richTextBox.Paragraphs.Add(m_paragraphs[j].RichParagraph); context.TextRunIndexHitStart = -1; context.TextRunIndexHitEnd = -1; m_richTextBox.ScriptItemize(); } else { m_richTextBox.Paragraphs.Add(list[0]); } if (searchHit2 != null) { searchHit2.SelectionStart.ParagraphIndex = 0; searchHit2.SelectionEnd.ParagraphIndex = 0; list2 = new List <RTSelectionHighlight>(0); list2.Add(searchHit2); } float height = 0f; List <Paragraph> rTParagraphs = LineBreaker.Flow(m_richTextBox, context.Graphics, context.FontCache, flowContext, keepLines: true, out height); RectangleF layoutParagraph = RectangleF.Empty; PointF offset = PointF.Empty; float delta = 0f; if (writingMode == RPLFormat.WritingModes.Horizontal) { AdjustParagraphLayout(contentHeight, height, num, flag2, writingMode, ref delta, ref layoutParagraph, ref offset); } else { AdjustParagraphLayout(contentHeight2, height, num2, flag2, writingMode, ref delta, ref layoutParagraph, ref offset); } m_context = context; m_paragraphs[j].TextPosition = layoutParagraph; RichTextRenderer richTextRenderer = new RichTextRenderer(); try { richTextRenderer.SetTextbox(m_richTextBox); richTextRenderer.RTParagraphs = rTParagraphs; richTextRenderer.FontCache = context.FontCache; if (writingMode == RPLFormat.WritingModes.Horizontal) { RenderRichText(richTextRenderer, context.Graphics, TextPosition, offset, list2); } else { RenderRichText(richTextRenderer, context.Graphics, layoutParagraph, offset, list2); } richTextRenderer.FontCache = null; } finally { if (richTextRenderer != null) { richTextRenderer.Dispose(); richTextRenderer = null; } } m_context = null; flowContext.Reset(); if (writingMode == RPLFormat.WritingModes.Horizontal) { if (flag2) { num += delta; flag2 = false; } num += height; } else { if (flag2) { num2 += delta; flag2 = false; } num2 += height; } if (searchHit2 != null) { searchHit2.SelectionStart.ParagraphIndex = j; searchHit2.SelectionEnd.ParagraphIndex = j; } m_richTextBox.Paragraphs.RemoveAt(0); if (!flag) { m_paragraphs[j].RichParagraph = null; } else { list.RemoveAt(0); } } if (list != null && list.Count > 0) { list.Clear(); } list = null; }
public static bool Run() { Console.WriteLine("Grapheme Cluster Tests"); Console.WriteLine("----------------------"); Console.WriteLine(); // Read the test file var location = System.IO.Path.GetDirectoryName(typeof(LineBreakTests).Assembly.Location); var lines = System.IO.File.ReadAllLines(System.IO.Path.Combine(location, Path.Combine("TestData", "GraphemeBreakTest.txt"))); // Process each line var tests = new List <Test>(); for (int lineNumber = 1; lineNumber < lines.Length + 1; lineNumber++) { // Get the line, remove comments var line = lines[lineNumber - 1].Split('#')[0].Trim(); // Ignore blank/comment only lines if (string.IsNullOrWhiteSpace(line)) { continue; } var codePoints = new List <int>(); var breakPoints = new List <int>(); // Parse the test var p = 0; while (p < line.Length) { // Ignore white space if (char.IsWhiteSpace(line[p])) { p++; continue; } if (line[p] == '×') { p++; continue; } if (line[p] == '÷') { breakPoints.Add(codePoints.Count); p++; continue; } int codePointPos = p; while (p < line.Length && IsHexDigit(line[p])) { p++; } var codePointStr = line.Substring(codePointPos, p - codePointPos); var codePoint = Convert.ToInt32(codePointStr, 16); codePoints.Add(codePoint); } // Create test var test = new Test() { LineNumber = lineNumber, CodePoints = codePoints.ToArray(), BreakPoints = breakPoints.ToArray(), }; tests.Add(test); } // Preload GraphemeClusterAlgorithm.IsBoundary(new Slice <int>(new int[10]), 0); var lineBreaker = new LineBreaker(); var tr = new TestResults(); var foundBreaks = new List <int>(); foundBreaks.Capacity = 100; for (int testNumber = 0; testNumber < tests.Count; testNumber++) { var t = tests[testNumber]; foundBreaks.Clear(); var codePointsSlice = new Slice <int>(t.CodePoints.ToArray()); tr.EnterTest(); // Run the algorithm for (int i = 0; i < codePointsSlice.Length + 1; i++) { if (GraphemeClusterAlgorithm.IsBoundary(codePointsSlice, i)) { foundBreaks.Add(i); } } tr.LeaveTest(); // Check the same bool pass = true; if (foundBreaks.Count != t.BreakPoints.Length) { pass = false; } else { for (int i = 0; i < foundBreaks.Count; i++) { if (foundBreaks[i] != t.BreakPoints[i]) { pass = false; } } } if (!pass) { Console.WriteLine($"Failed test on line {t.LineNumber}"); Console.WriteLine(); Console.WriteLine($" Code Points: {string.Join(" ", t.CodePoints)}"); Console.WriteLine($"Expected Breaks: {string.Join(" ", t.BreakPoints)}"); Console.WriteLine($" Actual Breaks: {string.Join(" ", foundBreaks)}"); Console.WriteLine($" Char Props: {string.Join(" ", t.CodePoints.Select(x => UnicodeClasses.GraphemeClusterClass(x)))}"); Console.WriteLine(); return(false); } // Record it tr.TestPassed(pass); } tr.Dump(); return(tr.AllPassed); }
public void NegativeTest() { LineBreaker breaker = new LineBreaker(""); Assert.Throws <ArgumentOutOfRangeException>(() => breaker.Break(-1)); }
public void SpecialPostiveTests(string input, int columns, string expectedOutput) { LineBreaker breaker = new LineBreaker(input); Assert.Equal(expectedOutput, breaker.Break(columns)); }