public void TestEquationWithOperatorsAndRelations() { var mathList = MathLists.FromString("2x+3=y"); var display = Typesetter <TFont, TGlyph> .CreateLine(mathList, _font, _context, LineStyle.Display); Assert.Equal(LinePosition.Regular, display.LinePosition); Assert.Equal(new Range(0, 6), display.Range); Assert.False(display.HasScript); Assert.Equal(Range.UndefinedInt, display.IndexInParent); Assert.Single(display.Displays); var line = display.Displays[0] as TextLineDisplay <TFont, TGlyph>; Assert.Equal(6, line.Atoms.Length); Assert.Equal("2x+3=y", line.StringText()); Assert.Equal(new PointF(), line.Position); Assert.Equal(new Range(0, 6), line.Range); Assert.False(line.HasScript); Assert.Equal(display.Ascent, line.Ascent); Assert.Equal(display.Width, line.Width); Assert.Equal(display.Descent, line.Descent); Assertions.ApproximatelyEqual(14, display.Ascent, 0.01); Assertions.ApproximatelyEqual(4, display.Descent, 0.01); Assertions.ApproximatelyEqual(80, display.Width, 0.01); }
public void TestVariablesAndNumbers() { var mathList = MathLists.FromString("xy2w"); var display = Typesetter <TFont, TGlyph> .CreateLine(mathList, _font, _context, LineStyle.Display); Assert.NotNull(display); Assert.Equal(LinePosition.Regular, display.LinePosition); Assert.Equal(new PointF(), display.Position); Assert.Equal(new Range(0, 4), display.Range); Assert.False(display.HasScript); Assert.Equal(Range.UndefinedInt, display.IndexInParent); Assert.Single(display.Displays); var sub0 = display.Displays[0]; var line = sub0 as TextLineDisplay <TFont, TGlyph>; Assert.NotNull(line); Assert.Equal(4, line.Atoms.Length); Assert.Equal("xy2w", line.StringText()); Assert.Equal(new PointF(), line.Position); Assert.Equal(new Range(0, 4), line.Range); Assert.False(line.HasScript); Assert.Equal(display.Ascent, line.Ascent); Assert.Equal(display.Descent, line.Descent); Assert.Equal(display.Width, line.Width); Assertions.ApproximatelyEqual(14, display.Ascent, 0.01); Assertions.ApproximatelyEqual(4, display.Descent, 0.01); Assertions.ApproximatelyEqual(40, display.Width, 0.01); }
private static void RunTypeset(CommandLineOptions commandLineOptions) { var typesetter = new Typesetter(); var documentFormatting = new DocumentFormatting( commandLineOptions.FontFamily, commandLineOptions.PageSize, commandLineOptions.PageMargin, commandLineOptions.LineHeight, commandLineOptions.FontSize, commandLineOptions.PrintPageNumbers, commandLineOptions.PrintMarginals); var documentMetadata = new DocumentMetadata( commandLineOptions.Title, documentFormatting); var markdownPages = commandLineOptions.InputFilePaths.Select(File.ReadAllText).ToArray(); Console.WriteLine("Typesetting document, please wait..."); var stream = typesetter.CreatePdfDocument(documentMetadata, markdownPages); using var fileStream = new FileStream(commandLineOptions.OutputFilepath, FileMode.Create); stream.CopyTo(fileStream); Console.WriteLine($"Finished! {commandLineOptions.OutputFilepath} has been created"); }
public Renderer() { _fontLoader = new FontLoader(); _textMeasurer = new HyperfontTextMeasurer(_fontLoader); _typesetter = new Typesetter(_textMeasurer); _exporter = new PNGExporter(_fontLoader); _parser = new LaTeXParser(); }
internal static int GetInterElementSpaceArrayIndexForType <TFont, TGlyph>(Typesetter <TFont, TGlyph> t, MathAtomType atomType) where TFont : MathFont <TGlyph> { switch (atomType) { case MathAtomType.RaiseBox: return(0); //Same as Color } return(-1); // If reach here, then WILL THROW }
protected override void UpdateDisplayCore(float unused) { if (_displayChanged) { Display = Content == null ? null : Typesetter.CreateLine(Content, Fonts, TypesettingContext.Instance, LineStyle); _displayChanged = false; } }
public void TestSuperScript() { var mathList = new MathList(); var x = MathAtoms.ForCharacter('x'); var superscript = new MathList { MathAtoms.ForCharacter('2') }; x.Superscript = superscript; mathList.Add(x); var display = Typesetter <TFont, TGlyph> .CreateLine(mathList, _font, _context, LineStyle.Display); Assert.NotNull(display); Assert.Equal(LinePosition.Regular, display.LinePosition); Assert.Equal(new PointF(), display.Position); Assert.Equal(new Range(0, 1), display.Range); Assert.False(display.HasScript); Assert.Equal(display.IndexInParent, Range.UndefinedInt); Assert.Equal(2, display.Displays.Count()); var super0 = display.Displays[0]; var line = super0 as TextLineDisplay <TFont, TGlyph>; Assert.NotNull(line); Assert.Single(line.Atoms); Assert.Equal("x", line.StringText()); Assert.Equal(new PointF(), line.Position); Assert.True(line.HasScript); var super1 = display.Displays[1] as ListDisplay <TFont, TGlyph>; Assert.NotNull(super1); Assert.Equal(LinePosition.Superscript, super1.LinePosition); var super1Position = super1.Position; Assertions.ApproximatePoint(10.32, 7.26, super1Position, 0.01); // may change as we implement more details? Assert.Equal(new Range(0, 1), super1.Range); Assert.False(super1.HasScript); Assert.Equal(0, super1.IndexInParent); Assert.Single(super1.Displays); var super10 = super1.Displays[0] as TextLineDisplay <TFont, TGlyph>; Assert.NotNull(super10); Assert.Single(super10.Atoms); Assert.Equal(new PointF(), super10.Position); Assert.False(super10.HasScript); Assertions.ApproximatelyEqual(17.06, display.Ascent, 0.01); Assertions.ApproximatelyEqual(4, display.Descent, 0.01); }
public void TestRadical() { var mathList = new MathList { new Radical { Radicand = new MathList { MathAtoms.ForCharacter('1') } } }; var display = Typesetter <TFont, TGlyph> .CreateLine(mathList, _font, _context, LineStyle.Display); Assert.Equal(LinePosition.Regular, display.LinePosition); Assert.Equal(new PointF(), display.Position); Assert.Equal(new Range(0, 1), display.Range); Assert.False(display.HasScript); Assert.Equal(Range.UndefinedInt, display.IndexInParent); Assert.Single(display.Displays); var radical = display.Displays[0] as RadicalDisplay <TFont, TGlyph>; Assert.Equal(new Range(0, 1), radical.Range); Assert.False(radical.HasScript); Assert.Equal(new PointF(), radical.Position); Assert.NotNull(radical.Radicand); Assert.Null(radical.Degree); var display2 = radical.Radicand as ListDisplay <TFont, TGlyph>; Assert.NotNull(display2); Assert.Equal(LinePosition.Regular, display2.LinePosition); Assertions.ApproximatePoint(10, 0, display2.Position, 0.01); Assert.Equal(new Range(0, 1), display2.Range); Assert.False(display2.HasScript); Assert.Equal(Range.UndefinedInt, display2.IndexInParent); Assert.Single(display2.Displays); var line2 = display2.Displays[0] as TextLineDisplay <TFont, TGlyph>; Assert.Single(line2.Atoms); Assert.Equal("1", line2.StringText()); Assert.Equal(new PointF(), line2.Position); Assert.Equal(new Range(0, 1), line2.Range); Assert.False(line2.HasScript); Assertions.ApproximatelyEqual(18.56, display.Ascent, 0.01); Assertions.ApproximatelyEqual(4, display.Descent, 0.01); Assertions.ApproximatelyEqual(20, display.Width, 0.01); }
public TextPipeline(FontDevice fontDevice) { Contract.Requires(fontDevice != null); _FontDevice = fontDevice; _TextAnalyzer = new Analyzer(_FontDevice.Factory); _AggregatorSink = new AggregatorSink(); _Aggregator = new Aggregator(_AggregatorSink); _ShaperSink = new ShaperSink(); _Shaper = new Shaper(_FontDevice, _ShaperSink); _FormatterSink = new FormatterSink(); _Formatter = new Formatter(_FormatterSink); _TypesetterSink = new TypesetterSink(); _Typesetter = new Typesetter(_TypesetterSink); _GeometryCache = new TextGeometryCache(); }
internal static void CreateDisplayAtom <TFont, TGlyph>(Typesetter <TFont, TGlyph> t, I_ExtensionAtom atom) where TFont : IFont <TGlyph> { switch (atom.AtomType) { case MathAtomType.RaiseBox: t.AddDisplayLine(false); var raiseBox = atom as RaiseBox; var raisedDisplay = Typesetter <TFont, TGlyph> .CreateLine(raiseBox.InnerList, t._font, t._context, t._style); var raisedPosition = t._currentPosition; raisedPosition.Y += raiseBox.Raise.ActualLength(t._mathTable, t._font); raisedDisplay.Position = raisedPosition; t._currentPosition.X += raisedDisplay.Width; t._displayAtoms.Add(raisedDisplay); break; } }
public void TestRaiseBox() { var mathList = new MathList { new Atoms.Extension.RaiseBox { InnerList = new MathList { MathAtoms.ForCharacter('r') }, Raise = new Space(3 * Structures.Space.Point) } }; var display = Typesetter <TFont, TGlyph> .CreateLine(mathList, _font, _context, LineStyle.Display); Assert.Equal(LinePosition.Regular, display.LinePosition); Assert.Equal(new Range(0, 1), display.Range); Assert.False(display.HasScript); Assert.Equal(Range.UndefinedInt, display.IndexInParent); Assert.Single(display.Displays); var display2 = display.Displays[0] as ListDisplay <TFont, TGlyph>; Assert.Equal(LinePosition.Regular, display2.LinePosition); Assert.Equal(new PointF(0, 3), display2.Position); Assert.Equal(new Range(0, 1), display2.Range); Assert.False(display2.HasScript); Assert.Equal(Range.UndefinedInt, display2.IndexInParent); Assert.Equal(1, display2.Displays.Count); var line = display2.Displays[0] as TextLineDisplay <TFont, TGlyph>; Assert.Single(line.Atoms); Assert.Equal("r", line.StringText()); Assert.Equal(new PointF(), line.Position); Assert.False(line.HasScript); Assertions.ApproximatelyEqual(17, display.Ascent, 0.01); Assertions.ApproximatelyEqual(1, display.Descent, 0.01); Assertions.ApproximatelyEqual(10, display.Width, 0.01); }
public static MathListDisplay<TFont, TGlyph> CreateLine<TFont, TGlyph>(this TypesettingContext<TFont, TGlyph> context, IMathList list, TFont font, LineStyle style) where TFont: MathFont<TGlyph> { return Typesetter<TFont, TGlyph>.CreateLine(list, font, context, style); }
static void Main(string[] args) { var typesetter = new Typesetter(); }
public TypesetterTests() { _textMeasurer = new TestTextMeasurer(); _typesetter = new Typesetter(_textMeasurer); }
public static (Displays relative, Displays absolute) Layout(TextAtom input, Fonts inputFont, float canvasWidth) { if (input == null) { return (new Displays(Array.Empty <IDisplay <Fonts, Glyph> >()), new Displays(Array.Empty <IDisplay <Fonts, Glyph> >())); } float accumulatedHeight = 0; TextDisplayLineBuilder line = new TextDisplayLineBuilder(); void BreakLine(List <IDisplay <Fonts, Glyph> > displayList) { accumulatedHeight += line.Ascent; line.Clear(0, -accumulatedHeight, displayList.Add, () => accumulatedHeight += line.Descent); } void AddDisplaysWithLineBreaks(TextAtom atom, Fonts fonts, List <IDisplay <Fonts, Glyph> > displayList, List <IDisplay <Fonts, Glyph> > displayMathList, FontStyle style = FontStyle.Roman, /*FontStyle.Default is FontStyle.Italic, FontStyle.Roman is no change to characters*/ Structures.Color?color = null) { IDisplay <Fonts, Glyph> display; switch (atom) { case TextAtom.List list: foreach (var a in list.Content) { AddDisplaysWithLineBreaks(a, fonts, displayList, displayMathList, style, color); } break; case TextAtom.Style st: AddDisplaysWithLineBreaks(st.Content, fonts, displayList, displayMathList, st.FontStyle, color); break; case TextAtom.Size sz: AddDisplaysWithLineBreaks(sz.Content, new Fonts(fonts, sz.PointSize), displayList, displayMathList, style, color); break; case TextAtom.Color c: AddDisplaysWithLineBreaks(c.Content, fonts, displayList, displayMathList, style, c.Colour); break; case TextAtom.Space sp: //Allow space at start of line since user explicitly specified its length //Also \par generates this kind of spaces line.AddSpace(sp.Content.ActualLength(MathTable.Instance, fonts)); break; case TextAtom.Newline n: BreakLine(displayList); break; case TextAtom.Math m when m.DisplayStyle: BreakLine(displayList); #warning Replace 12 with a more appropriate spacing accumulatedHeight += 12; display = Typesetter <Fonts, Glyph> .CreateLine(m.Content, fonts, TypesettingContext.Instance, LineStyle.Display); if (color != null) { display.SetTextColorRecursive(color); } accumulatedHeight += display.Ascent; display.Position = new System.Drawing.PointF( IPainterExtensions.GetDisplayPosition(display.Width, display.Ascent, display.Descent, fonts.PointSize, false, canvasWidth, float.NaN, TextAlignment.Top, default, default, default).X, -accumulatedHeight); accumulatedHeight += display.Descent; accumulatedHeight += 12; if (color != null) { display.SetTextColorRecursive(color); } displayMathList.Add(display); break; void FinalizeInlineDisplay(float ascentMin, bool forbidAtLineStart = false) { if (color != null) { display.SetTextColorRecursive(color); } if (line.Width + display.Width > canvasWidth && !forbidAtLineStart) { BreakLine(displayList); } line.Add(display, ascentMin); } case TextAtom.Text t: var content = UnicodeFontChanger.Instance.ChangeFont(t.Content, style); var glyphs = GlyphFinder.Instance.FindGlyphs(fonts, content); //Calling Select(g => g.Typeface).Distinct() speeds up query up to 10 times, //Calling Max(Func<,>) instead of Select(Func<,>).Max() speeds up query 2 times float maxLineSpacing = glyphs.Select(g => g.Typeface).Distinct().Max(tf => tf.CalculateRecommendLineSpacing() * tf.CalculateScaleToPixelFromPointSize(fonts.PointSize) ); display = new TextRunDisplay <Fonts, Glyph>(Display.Text.AttributedGlyphRuns.Create(content, glyphs, fonts, false), t.Range, TypesettingContext.Instance); FinalizeInlineDisplay(maxLineSpacing); break; case TextAtom.Math m: if (m.DisplayStyle) { throw new InvalidCodePathException("Display style maths should have been handled above this switch."); } display = Typesetter <Fonts, Glyph> .CreateLine(m.Content, fonts, TypesettingContext.Instance, Enumerations.LineStyle.Text); FinalizeInlineDisplay(fonts.MathTypeface.CalculateRecommendLineSpacing() * fonts.MathTypeface.CalculateScaleToPixelFromPointSize(fonts.PointSize)); break; case TextAtom.ControlSpace cs: var spaceGlyph = GlyphFinder.Instance.Lookup(fonts, ' '); display = new TextRunDisplay <Fonts, Glyph>(Display.Text.AttributedGlyphRuns.Create(" ", new[] { spaceGlyph }, fonts, false), cs.Range, TypesettingContext.Instance); FinalizeInlineDisplay(spaceGlyph.Typeface.CalculateRecommendLineSpacing() * spaceGlyph.Typeface.CalculateScaleToPixelFromPointSize(fonts.PointSize), forbidAtLineStart: true); //No spaces at start of line break; case null: throw new InvalidOperationException("TextAtoms should never be null. You must have sneaked one in."); case var a: throw new InvalidCodePathException($"There should not be an unknown type of TextAtom. However, one with type {a.GetType()} was encountered."); } } var relativePositionList = new List <IDisplay <Fonts, Glyph> >(); var absolutePositionList = new List <IDisplay <Fonts, Glyph> >(); AddDisplaysWithLineBreaks(input, inputFont, relativePositionList, absolutePositionList); BreakLine(relativePositionList); //remember to finalize the last line return(new Displays(relativePositionList), new Displays(absolutePositionList)); }
void TestOuter(string latex, int rangeMax, double ascent, double descent, double width, params System.Action <IDisplay <TFont, TGlyph> >[] inspectors) => TestList(rangeMax, ascent, descent, width, 0, 0, LinePosition.Regular, Range.UndefinedInt, inspectors) (Typesetter.CreateLine(LaTeXParserTest.ParseLaTeX(latex), _font, _context, LineStyle.Display));
internal static ListDisplay <TFont, TGlyph> ParseLaTeXToDisplay(string latex) => Typesetter.CreateLine(LaTeXParserTest.ParseLaTeX(latex), _font, _context, LineStyle.Display);
public static (Display relative, Display absolute) Layout (TextAtom input, Fonts inputFont, float canvasWidth, float additionalLineSpacing) { #warning Multiply these constants by resolution const float abovedisplayskip = 12, abovedisplayshortskip = 0, belowdisplayskip = 12, belowdisplayshortskip = 7; if (input == null) { return (new Display(Array.Empty <IDisplay <Fonts, Glyph> >()), new Display(Array.Empty <IDisplay <Fonts, Glyph> >())); } float accumulatedHeight = 0; //indicator of the need to apply belowdisplay(short)skip when line break bool afterDisplayMaths = false; void BreakLine(TextLayoutLineBuilder line, List <IDisplay <Fonts, Glyph> > displayList, List <IDisplay <Fonts, Glyph> > displayMathList, bool appendLineGap = true) { if (afterDisplayMaths) { accumulatedHeight += line.Width > displayMathList.Last().Position.X ? belowdisplayskip : belowdisplayshortskip; afterDisplayMaths = false; } line.Clear(0, -accumulatedHeight, displayList, ref accumulatedHeight, true, appendLineGap, additionalLineSpacing); } //variables captured by this method are currently unchangable by TextAtoms void AddDisplaysWithLineBreaks( TextAtom atom, Fonts fonts, TextLayoutLineBuilder line, List <IDisplay <Fonts, Glyph> > displayList, List <IDisplay <Fonts, Glyph> > displayMathList, FontStyle style, Color?color ) { IDisplay <Fonts, Glyph> display; switch (atom) { case TextAtom.List list: foreach (var a in list.Content) { AddDisplaysWithLineBreaks (a, fonts, line, displayList, displayMathList, style, color); } break; case TextAtom.Style st: AddDisplaysWithLineBreaks (st.Content, fonts, line, displayList, displayMathList, st.FontStyle, color); break; case TextAtom.Size sz: AddDisplaysWithLineBreaks (sz.Content, new Fonts(fonts, sz.PointSize), line, displayList, displayMathList, style, color); break; case TextAtom.Color c: AddDisplaysWithLineBreaks (c.Content, fonts, line, displayList, displayMathList, style, c.Colour); break; case TextAtom.Space sp: //Allow space at start of line since user explicitly specified its length //Also \par generates this kind of spaces line.AddSpace(sp.Content.ActualLength(MathTable.Instance, fonts)); break; case TextAtom.Newline n: BreakLine(line, displayList, displayMathList); break; case TextAtom.Math m when m.DisplayStyle: var lastLineWidth = line.Width; BreakLine(line, displayList, displayMathList, false); display = Typesetter.CreateLine(m.Content, fonts, TypesettingContext.Instance, LineStyle.Display); var displayX = IPainterExtensions.GetDisplayPosition (display.Width, display.Ascent, display.Descent, fonts.PointSize, false, canvasWidth, float.NaN, TextAlignment.Top, default, default, default).X; //\because When displayList.LastOrDefault() is null, //the false condition is selected //\therefore Append abovedisplayshortskip which defaults //to 0 when nothing is above the display-style maths accumulatedHeight += lastLineWidth > displayX ? abovedisplayskip : abovedisplayshortskip; accumulatedHeight += display.Ascent; display.Position = new System.Drawing.PointF(displayX, -accumulatedHeight); accumulatedHeight += display.Descent; afterDisplayMaths = true; if (color != null) { display.SetTextColorRecursive(color); } displayMathList.Add(display); break; void FinalizeInlineDisplay(float ascender, float rawDescender, float lineGap, bool forbidAtLineStart = false) { if (color != null) { display.SetTextColorRecursive(color); } if (line.Width + display.Width > canvasWidth && !forbidAtLineStart) { BreakLine(line, displayList, displayMathList); } //rawDescender is taken directly from font file and is negative, //while IDisplay.Descender is positive line.Add(display, ascender, -rawDescender, lineGap); } case TextAtom.Text t: var content = UnicodeFontChanger.Instance.ChangeFont(t.Content, style); var glyphs = GlyphFinder.Instance.FindGlyphs(fonts, content); //Calling Select(g => g.Typeface).Distinct() speeds up query up to 10 times, //Calling Max(Func<,>) instead of Select(Func<,>).Max() speeds up query 2 times var typefaces = glyphs.Select(g => g.Typeface).Distinct().ToList(); display = new TextRunDisplay <Fonts, Glyph>( new AttributedGlyphRun <Fonts, Glyph>(content, glyphs, fonts), t.Range, TypesettingContext.Instance ); FinalizeInlineDisplay( typefaces.Max(tf => tf.Ascender * tf.CalculateScaleToPixelFromPointSize(fonts.PointSize)), typefaces.Min(tf => tf.Descender * tf.CalculateScaleToPixelFromPointSize(fonts.PointSize)), typefaces.Max(tf => tf.LineGap * tf.CalculateScaleToPixelFromPointSize(fonts.PointSize)) ); break; case TextAtom.Math m: if (m.DisplayStyle) { throw new InvalidCodePathException ("Display style maths should have been handled above this switch."); } display = Typesetter.CreateLine(m.Content, fonts, TypesettingContext.Instance, LineStyle.Text); var scale = fonts.MathTypeface.CalculateScaleToPixelFromPointSize(fonts.PointSize); FinalizeInlineDisplay(fonts.MathTypeface.Ascender * scale, fonts.MathTypeface.Descender * scale, fonts.MathTypeface.LineGap * scale); break; case TextAtom.ControlSpace cs: var spaceGlyph = GlyphFinder.Instance.Lookup(fonts, ' '); display = new TextRunDisplay <Fonts, Glyph>( new AttributedGlyphRun <Fonts, Glyph>(" ", new[] { spaceGlyph }, fonts), cs.Range, TypesettingContext.Instance ); scale = spaceGlyph.Typeface.CalculateScaleToPixelFromPointSize(fonts.PointSize); FinalizeInlineDisplay(spaceGlyph.Typeface.Ascender * scale, spaceGlyph.Typeface.Descender * scale, spaceGlyph.Typeface.LineGap * scale, forbidAtLineStart: true); //No spaces at start of line break; case TextAtom.Accent a: var accentGlyph = GlyphFinder.Instance.FindGlyphForCharacterAtIndex( fonts, a.AccentChar.Length - 1, a.AccentChar ); scale = accentGlyph.Typeface.CalculateScaleToPixelFromPointSize(fonts.PointSize); var accenteeDisplayList = new List <IDisplay <Fonts, Glyph> >(); var invalidDisplayMaths = new List <IDisplay <Fonts, Glyph> >(); var accentDisplayLine = new TextLayoutLineBuilder(); AddDisplaysWithLineBreaks(a.Content, fonts, accentDisplayLine, accenteeDisplayList, invalidDisplayMaths, style, color); float _ = default; accentDisplayLine.Clear (0, 0, accenteeDisplayList, ref _, false, false, additionalLineSpacing); System.Diagnostics.Debug.Assert(invalidDisplayMaths.Count == 0, "Display maths inside an accentee is unsupported -- ignoring display maths"); var accentee = new Display(accenteeDisplayList); var accenteeCodepoint = a.Content.SingleChar(style); var accenteeSingleGlyph = accenteeCodepoint.HasValue ? GlyphFinder.Instance.Lookup(fonts, accenteeCodepoint.GetValueOrDefault()) : GlyphFinder.Instance.EmptyGlyph; var accentDisplay = new AccentDisplay <Fonts, Glyph>( Typesetter.CreateAccentGlyphDisplay( accentee, accenteeSingleGlyph, accentGlyph, TypesettingContext.Instance, fonts, a.Range ), accentee); display = accentDisplay; //accentDisplay.Ascent does not take account of accent glyph's extra height //-> accent will be out of bounds if it is on the first line FinalizeInlineDisplay( Math.Max(accentGlyph.Typeface.Ascender * scale, accentDisplay.Accent.Position.Y + accentDisplay.Ascent), accentGlyph.Typeface.Descender * scale, accentGlyph.Typeface.LineGap * scale); break; case TextAtom.Comment _: break; case null: throw new InvalidOperationException ("TextAtoms should never be null. You must have sneaked one in."); case var a: throw new InvalidCodePathException ($"There should not be an unknown type of TextAtom. However, one with type {a.GetType()} was encountered."); } } var relativePositionList = new List <IDisplay <Fonts, Glyph> >(); var absolutePositionList = new List <IDisplay <Fonts, Glyph> >(); var globalLine = new TextLayoutLineBuilder(); AddDisplaysWithLineBreaks( input, inputFont, globalLine, relativePositionList, absolutePositionList, FontStyle.Roman /*FontStyle.Default is FontStyle.Italic, FontStyle.Roman is no change to characters*/, null ); BreakLine(globalLine, relativePositionList, absolutePositionList); //remember to finalize the last line return(new Display(relativePositionList), new Display(absolutePositionList)); }
public void TestSuperSubscript() { var mathList = new MathList(); var x = MathAtoms.ForCharacter('x'); var superscript = new MathList { MathAtoms.ForCharacter('2') }; var subscript = new MathList { MathAtoms.ForCharacter('1') }; x.Subscript = subscript; x.Superscript = superscript; mathList.Add(x); var display = Typesetter <TFont, TGlyph> .CreateLine(mathList, _font, _context, LineStyle.Display); Assert.NotNull(display); Assert.Equal(LinePosition.Regular, display.LinePosition); Assert.Equal(new PointF(), display.Position); Assert.Equal(new Range(0, 1), display.Range); Assert.False(display.HasScript); Assert.Equal(Range.UndefinedInt, display.IndexInParent); Assert.Equal(3, display.Displays.Count()); var line = display.Displays[0] as TextLineDisplay <TFont, TGlyph>; Assert.Single(line.Atoms); Assert.Equal("x", line.StringText()); Assert.Equal(new PointF(), line.Position); Assert.True(line.HasScript); var display2 = display.Displays[1] as ListDisplay <TFont, TGlyph>; Assert.Equal(LinePosition.Superscript, display2.LinePosition); Assertions.ApproximatePoint(10.32, 9.68, display2.Position, 0.01); Assert.Equal(new Range(0, 1), display2.Range); Assert.False(display2.HasScript); Assert.Equal(0, display2.IndexInParent); Assert.Single(display2.Displays); var line2 = display2.Displays[0] as TextLineDisplay <TFont, TGlyph>; Assert.Single(line2.Atoms); Assert.Equal("2", line2.StringText()); Assert.Equal(new PointF(), line2.Position); Assert.False(line2.HasScript); var display3 = display.Displays[2] as ListDisplay <TFont, TGlyph>; Assert.Equal(LinePosition.Subscript, display3.LinePosition); // Because both subscript and superscript are present, coords are // different from the subscript-only case. Assertions.ApproximatePoint(10, -6.12, display3.Position, 0.01); Assert.Equal(new Range(0, 1), display3.Range); Assert.False(display3.HasScript); Assert.Equal(0, display3.IndexInParent); Assert.Single(display3.Displays); var line3 = display3.Displays[0] as TextLineDisplay <TFont, TGlyph>; Assert.Single(line3.Atoms); Assert.Equal("1", line3.StringText()); Assert.Equal(new PointF(), line3.Position); Assert.False(line3.HasScript); Assertions.ApproximatelyEqual(19.48, display.Ascent, 0.01); Assertions.ApproximatelyEqual(8.92, display.Descent, 0.01); Assertions.ApproximatelyEqual(17.32, display.Width, 0.01); Assertions.ApproximatelyEqual(display.Ascent, display2.Position.Y + line2.Ascent, 0.01); Assertions.ApproximatelyEqual(display.Descent, line3.Descent - display3.Position.Y, 0.01); }
public void TestInner() { var mathList = new MathList { new Inner { InnerList = new MathList { MathAtoms.ForCharacter('x'), }, LeftBoundary = MathAtoms.Create(MathAtomType.Boundary, '('), RightBoundary = MathAtoms.Create(MathAtomType.Boundary, ')') } }; var display = Typesetter <TFont, TGlyph> .CreateLine(mathList, _font, _context, LineStyle.Display); Assert.Equal(LinePosition.Regular, display.LinePosition); Assert.Equal(new Range(0, 1), display.Range); Assert.False(display.HasScript); Assert.Equal(Range.UndefinedInt, display.IndexInParent); Assert.Single(display.Displays); var display2 = display.Displays[0] as ListDisplay <TFont, TGlyph>; Assert.Equal(LinePosition.Regular, display2.LinePosition); Assert.Equal(new PointF(), display2.Position); Assert.Equal(new Range(0, 1), display2.Range); Assert.False(display2.HasScript); Assert.Equal(Range.UndefinedInt, display2.IndexInParent); Assert.Equal(3, display2.Displays.Count); var glyph = display2.Displays[0] as GlyphDisplay <TFont, TGlyph>; Assert.Equal(new PointF(), glyph.Position); Assert.Equal(Range.NotFound, glyph.Range); Assert.False(glyph.HasScript); var display3 = display2.Displays[1] as ListDisplay <TFont, TGlyph>; Assert.Equal(LinePosition.Regular, display3.LinePosition); Assertions.ApproximatePoint(10, 0, display3.Position, 0.01); Assert.Equal(new Range(0, 1), display3.Range); Assert.False(display3.HasScript); Assert.Equal(Range.UndefinedInt, display3.IndexInParent); Assert.Single(display3.Displays); var line = display3.Displays[0] as TextLineDisplay <TFont, TGlyph>; Assert.Single(line.Atoms); Assert.Equal("x", line.StringText()); Assert.Equal(new PointF(), line.Position); Assert.False(line.HasScript); var glyph2 = display2.Displays[2] as GlyphDisplay <TFont, TGlyph>; Assertions.ApproximatePoint(20, 0, glyph2.Position, 0.01); Assert.Equal(Range.NotFound, glyph2.Range); Assert.False(glyph2.HasScript); Assert.Equal(display.Ascent, display2.Ascent); Assert.Equal(display.Descent, display2.Descent); Assert.Equal(display.Width, display2.Width); Assertions.ApproximatelyEqual(14, display.Ascent, 0.01); Assertions.ApproximatelyEqual(4, display.Descent, 0.01); Assertions.ApproximatelyEqual(30, display.Width, 0.01); }
public void TestAtop() { var mathList = new MathList { new Fraction(false) { Numerator = new MathList { MathAtoms.ForCharacter('1') }, Denominator = new MathList { MathAtoms.ForCharacter('3') } } }; var display = Typesetter <TFont, TGlyph> .CreateLine(mathList, _font, _context, LineStyle.Display); Assert.Equal(LinePosition.Regular, display.LinePosition); Assert.Equal(new PointF(), display.Position); Assert.Equal(new Range(0, 1), display.Range); Assert.False(display.HasScript); Assert.Equal(Range.UndefinedInt, display.IndexInParent); Assert.Single(display.Displays); var fraction = display.Displays[0] as FractionDisplay <TFont, TGlyph>; Assert.Equal(new Range(0, 1), fraction.Range); Assert.False(fraction.HasScript); Assert.Equal(new PointF(), fraction.Position); var numerator = fraction.Numerator as ListDisplay <TFont, TGlyph>; Assert.NotNull(numerator); Assert.Equal(LinePosition.Regular, numerator.LinePosition); Assert.False(numerator.HasScript); Assertions.ApproximatePoint(0, 13.54, numerator.Position, 0.01); Assert.Equal(new Range(0, 1), numerator.Range); Assert.Equal(Range.UndefinedInt, numerator.IndexInParent); Assert.Single(numerator.Displays); var subNumerator = numerator.Displays[0] as TextLineDisplay <TFont, TGlyph>; Assert.Single(subNumerator.Atoms); Assert.Equal("1", subNumerator.StringText()); Assert.Equal(new PointF(), subNumerator.Position); Assert.Equal(new Range(0, 1), subNumerator.Range); Assert.False(subNumerator.HasScript); var denominator = fraction.Denominator as ListDisplay <TFont, TGlyph>; Assert.NotNull(denominator); Assert.Equal(LinePosition.Regular, denominator.LinePosition); Assertions.ApproximatePoint(0, -13.73, denominator.Position, 0.01); Assert.Equal(new Range(0, 1), denominator.Range); Assert.False(denominator.HasScript); Assert.Equal(Range.UndefinedInt, denominator.IndexInParent); Assert.Single(denominator.Displays); var subDenominator = denominator.Displays[0] as TextLineDisplay <TFont, TGlyph>; Assert.Single(subDenominator.Atoms); Assert.Equal("3", subDenominator.StringText()); Assert.Equal(new PointF(), subDenominator.Position); Assert.Equal(new Range(0, 1), subDenominator.Range); Assert.False(subDenominator.HasScript); Assertions.ApproximatelyEqual(27.54, display.Ascent, 0.01); Assertions.ApproximatelyEqual(17.72, display.Descent, 0.01); Assertions.ApproximatelyEqual(10, display.Width, 0.01); }
public void TestBinomial() { var list = new MathList(); var fraction = new Fraction(false) { Numerator = new MathList { MathAtoms.ForCharacter('1') }, Denominator = new MathList { MathAtoms.ForCharacter('3') }, LeftDelimiter = "(", RightDelimiter = ")" }; list.Add(fraction); var display = Typesetter <TFont, TGlyph> .CreateLine(list, _font, _context, LineStyle.Display); Assert.Equal(LinePosition.Regular, display.LinePosition); Assert.Equal(new PointF(), display.Position); Assert.Equal(new Range(0, 1), display.Range); Assert.False(display.HasScript); Assert.Equal(Range.UndefinedInt, display.IndexInParent); Assert.Single(display.Displays); var display0 = display.Displays[0] as ListDisplay <TFont, TGlyph>; Assert.Equal(LinePosition.Regular, display0.LinePosition); Assert.Equal(new PointF(), display0.Position); Assert.Equal(new Range(0, 1), display.Range); Assert.False(display0.HasScript); Assert.Equal(Range.UndefinedInt, display0.IndexInParent); Assert.Equal(3, display0.Displays.Count); var glyph = display0.Displays[0] as GlyphDisplay <TFont, TGlyph>; Assert.Equal(new PointF(), glyph.Position); Assert.Equal(Range.NotFound, glyph.Range); Assert.False(glyph.HasScript); var subFraction = display0.Displays[1] as FractionDisplay <TFont, TGlyph>; Assert.Equal(new Range(0, 1), subFraction.Range); Assert.False(subFraction.HasScript); Assertions.ApproximatePoint(10, 0, subFraction.Position, 0.01); var numerator = subFraction.Numerator as ListDisplay <TFont, TGlyph>; Assert.NotNull(numerator); Assert.Equal(LinePosition.Regular, numerator.LinePosition); Assertions.ApproximatePoint(10, 13.54, numerator.Position, 0.01); Assert.Single(numerator.Displays); Assert.Equal(Range.UndefinedInt, numerator.IndexInParent); Assert.False(numerator.HasScript); var subNumerator = numerator.Displays[0] as TextLineDisplay <TFont, TGlyph>; Assert.Single(subNumerator.Atoms); Assert.Equal("1", subNumerator.StringText()); Assert.Equal(new PointF(), subNumerator.Position); Assert.Equal(new Range(0, 1), subNumerator.Range); Assert.False(subNumerator.HasScript); var denominator = subFraction.Denominator as ListDisplay <TFont, TGlyph>; Assert.NotNull(denominator); Assert.Equal(LinePosition.Regular, denominator.LinePosition); Assertions.ApproximatePoint(10, -13.72, denominator.Position, 0.01); Assert.Equal(new Range(0, 1), denominator.Range); Assert.False(denominator.HasScript); Assert.Equal(Range.UndefinedInt, denominator.IndexInParent); Assert.Single(denominator.Displays); var subDenominator = denominator.Displays[0] as TextLineDisplay <TFont, TGlyph>; Assert.Single(subDenominator.Atoms); Assert.Equal("3", subDenominator.StringText()); Assert.Equal(new PointF(), subDenominator.Position); Assert.Equal(new Range(0, 1), subDenominator.Range); Assert.False(subDenominator.HasScript); var subRight = display0.Displays[2] as GlyphDisplay <TFont, TGlyph>; Assert.False(subRight.HasScript); Assert.Equal(Range.NotFound, subRight.Range); Assertions.ApproximatePoint(20, 0, subRight.Position, 0.01); Assertions.ApproximatelyEqual(27.54, display.Ascent, 0.01); Assertions.ApproximatelyEqual(17.72, display.Descent, 0.01); Assertions.ApproximatelyEqual(30, display.Width, 0.01); }