public void NewWrappedLinesShouldNotStartOrEndWithWhiteSpace(string text, HorizontalAlignment horizontalAlignment) { Font font = CreateFont("\t x"); var r = new GlyphRenderer(); IReadOnlyList <GlyphLayout> layout = new TextLayout().GenerateLayout(text.AsSpan(), new TextOptions(new Font(font, 30)) { WrappingLength = 350, HorizontalAlignment = horizontalAlignment }); float lineYPos = layout[0].Location.Y; for (int i = 0; i < layout.Count; i++) { GlyphLayout glyph = layout[i]; if (lineYPos != glyph.Location.Y) { Assert.False(glyph.IsWhiteSpace()); Assert.False(layout[i - 1].IsWhiteSpace()); lineYPos = glyph.Location.Y; } } }
public ColorTypeface(string name) { m_gtf = GetGlyphTypeface(first_candidate: name); if (m_gtf == null) { return; } // Read the actual font data using Typography.OpenFont using (var s = m_gtf.GetFontStream()) { var r = new OpenFontReader(); m_openfont = r.Read(s, 0, ReadFlags.Full); } // Create a reusable layout for glyphs m_layout = new GlyphLayout() { Typeface = m_openfont, EnableBuiltinMathItalicCorrection = false, // not needed EnableComposition = true, EnableGpos = true, EnableGsub = true, EnableLigature = true, PositionTechnique = PositionTechnique.OpenFont, }; // Cache the glyph index for the zero-width joiner foreach (var g in StringToGlyphLayout("\u200d", use_gpos: false)) { m_zwj_glyph = g.glyphIndex; } }
public void NewWrappedLinesShouldNotStartOrEndWithWhiteSpace(string text, HorizontalAlignment horiAlignment) { var font = CreateFont("\t x"); GlyphRenderer r = new GlyphRenderer(); ImmutableArray <GlyphLayout> layout = new TextLayout().GenerateLayout(text, new RendererOptions(new Font(font, 30), 72) { WrappingWidth = 350, HorizontalAlignment = horiAlignment }); float lineYPos = layout[0].Location.Y; for (int i = 0; i < layout.Length; i++) { GlyphLayout glyph = layout[i]; if (lineYPos != glyph.Location.Y) { Assert.Equal(false, glyph.IsWhiteSpace); Assert.Equal(false, layout[i - 1].IsWhiteSpace); lineYPos = glyph.Location.Y; } } }
public GlyphPlanSequence Layout(GlyphLayout glyphLayout, TextBuffer buffer, int startAt, int len) { //this func get the raw char from buffer //and create glyph list //check if we have the string cache in specific value //--------- if (len > _glyphPlanSeqSet.MaxCacheLen) { //layout string is too long to be cache //it need to split into small buffer } GlyphPlanSequence planSeq = GlyphPlanSequence.Empty; GlyphPlanSeqCollection seqCol = _glyphPlanSeqSet.GetSeqCollectionOrCreateIfNotExist(len); int hashValue = CalculateHash(buffer, startAt, len); if (!seqCol.TryGetCacheGlyphPlanSeq(hashValue, out planSeq)) { ////not found then create glyph plan seq //bool useOutputScale = glyphLayout.UsePxScaleOnReadOutput; ////save //some font may have 'special' glyph x,y at some font size(eg. for subpixel-rendering position) //but in general we store the new glyph plan seq with unscale glyph pos //glyphLayout.UsePxScaleOnReadOutput = false; planSeq = CreateGlyphPlanSeq(glyphLayout, buffer, startAt, len); //glyphLayout.UsePxScaleOnReadOutput = useOutputScale;//restore seqCol.Register(hashValue, planSeq); } //--- //on unscale font=> we use original return(planSeq); }
public OpenFontIFonts() { typefaceStore = new TypefaceStore(); typefaceStore.FontCollection = InstalledFontCollection.GetSharedFontCollection(null); glyphLayout = new GlyphLayout(); //create glyph layout with default value userGlyphPlanList = new GlyphPlanList(); userCharToGlyphMapList = new List <UserCharToGlyphIndexMap>(); }
public static void GenerateGlyphPlans(this GlyphLayout glyphLayout, char[] textBuffer, int startAt, int len, UnscaledGlyphPlanList list) { //generate glyph plan based on its current setting glyphLayout.Layout(textBuffer, startAt, len); ReadOutput(glyphLayout, list); }
/// <summary> /// read GlyphPlan latest layout output /// </summary> private static void ReadOutput(GlyphLayout glyphLayout, UnscaledGlyphPlanList outputGlyphPlanList) { Typeface typeface = glyphLayout.Typeface; var glyphPositions = glyphLayout._glyphPositions; //3.read back int finalGlyphCount = glyphPositions.Count; int cx = 0; short cy = 0; PositionTechnique posTech = glyphLayout.PositionTechnique; ushort prev_index = 0; for (int i = 0; i < finalGlyphCount; ++i) { GlyphPos glyphPos = glyphPositions[i]; switch (posTech) { default: throw new NotSupportedException(); case PositionTechnique.None: outputGlyphPlanList.Append(new UnscaledGlyphPlan( 0, glyphPos.glyphIndex, glyphPos.advanceW, cx, cy)); break; case PositionTechnique.OpenFont: outputGlyphPlanList.Append(new UnscaledGlyphPlan( 0, glyphPos.glyphIndex, glyphPos.advanceW, cx + glyphPos.xoffset, (short)(cy + glyphPos.yoffset))); break; case PositionTechnique.Kerning: if (i > 0) { cx += typeface.GetKernDistance(prev_index, glyphPos.glyphIndex); } outputGlyphPlanList.Append(new UnscaledGlyphPlan( 0, prev_index = glyphPos.glyphIndex, glyphPos.advanceW, cx, cy)); break; } cx += glyphPos.advanceW; } }
private void Init(string name) { // Get a GlyphTypeface either from a system typeface or from a // file path. if (name != null) { m_gtf = GetGlyphTypeface(name); } // If not found, look for other possible emoji fonts such as // the Firefox one. Fall back to Arial, a font available since // Windows 3.1 \o/ foreach (var f in m_fallback_fonts) { m_gtf = m_gtf ?? GetGlyphTypeface(f); } // Read the actual font data using Typography.OpenFont using (var s = m_gtf.GetFontStream()) { var r = new Typography.OpenFont.OpenFontReader(); m_openfont = r.Read(s, Typography.OpenFont.ReadFlags.Full); } // Create a layout for glyphs m_layout = new GlyphLayout(); m_layout.ScriptLang = ScriptLangs.Default; m_layout.Typeface = m_openfont; m_layout.PositionTechnique = PositionTechnique.OpenFont; m_layout.FontSizeInPoints = 0.75f; // 1 pixel #if FALSE // debug stuff var font = m_openfont; GlyphIndexList gl = new GlyphIndexList(); gl.Add(font.LookupIndex(0x1f431)); // U+1F431 CAT FACE gl.Add(font.LookupIndex(0x200d)); // U+200D ZERO WIDTH JOINER gl.Add(font.LookupIndex(0x1f453)); // U+1F453 EYEGLASSES //gl.Add(font.LookupIndex(0x1f46a)); // U+1F46A FAMILY //gl.Add(font.LookupIndex(0x1f3fe)); // U+1F3FE EMOJI MODIFIER FITZPATRICK TYPE-5 foreach (var lookup_table in font.GSUBTable.LookupList) { lookup_table.DoSubstitution(gl, 0, gl.Count); } #endif }
public virtual void GenerateGlyphPlan( char[] textBuffer, int startAt, int len, GlyphPlanList outputGlyphPlanList, List <UserCodePointToGlyphIndex> charToGlyphMapList) { GlyphLayout glyphLayout = this.GlyphLayoutMan; glyphLayout.Layout(textBuffer, startAt, len); GlyphLayoutExtensions.GenerateGlyphPlans( glyphLayout.ResultUnscaledGlyphPositions, this.Typeface.CalculateScaleToPixelFromPointSize(this.FontSizeInPoints), false, outputGlyphPlanList); }
GlyphPlanSequence CreateGlyphPlanSeq(GlyphLayout glyphLayout, TextBuffer buffer, int startAt, int len) { GlyphPlanList planList = GlyphPlanBuffer.UnsafeGetGlyphPlanList(_glyphPlanBuffer); int pre_count = planList.Count; glyphLayout.Typeface = _typeface; glyphLayout.ScriptLang = _scLang; glyphLayout.Layout( TextBuffer.UnsafeGetCharBuffer(buffer), startAt, len); int post_count = planList.Count; return(new GlyphPlanSequence(_glyphPlanBuffer, pre_count, post_count - pre_count)); }
public ColorTypeface(string name) { m_gtf = GetGlyphTypeface(first_candidate: name); if (m_gtf == null) { return; } // Read the actual font data using Typography.OpenFont using (var s = m_gtf.GetFontStream()) { var r = new Typography.OpenFont.OpenFontReader(); m_openfont = r.Read(s, 0, Typography.OpenFont.ReadFlags.Full); } // Create a layout for glyphs m_layout = new GlyphLayout(); m_layout.ScriptLang = ScriptLangs.Default; m_layout.Typeface = m_openfont; m_layout.PositionTechnique = PositionTechnique.OpenFont; }
private void Init(string name) { m_gtf = GetGlyphTypeface(first_candidate: name); if (m_gtf == null) { return; } // Read the actual font data using Typography.OpenFont using (var s = m_gtf.GetFontStream()) { var r = new Typography.OpenFont.OpenFontReader(); m_openfont = r.Read(s, Typography.OpenFont.ReadFlags.Full); } // Create a layout for glyphs m_layout = new GlyphLayout(); m_layout.ScriptLang = ScriptLangs.Default; m_layout.Typeface = m_openfont; m_layout.PositionTechnique = PositionTechnique.OpenFont; #if FALSE // debug stuff var font = m_openfont; GlyphIndexList gl = new GlyphIndexList(); gl.Add(font.LookupIndex(0x1f431)); // U+1F431 CAT FACE gl.Add(font.LookupIndex(0x200d)); // U+200D ZERO WIDTH JOINER gl.Add(font.LookupIndex(0x1f453)); // U+1F453 EYEGLASSES //gl.Add(font.LookupIndex(0x1f46a)); // U+1F46A FAMILY //gl.Add(font.LookupIndex(0x1f3fe)); // U+1F3FE EMOJI MODIFIER FITZPATRICK TYPE-5 foreach (var lookup_table in font.GSUBTable.LookupList) { lookup_table.DoSubstitution(gl, 0, gl.Count); } #endif }
private void WriteFont(SWFFont font, int fid) { WriteBuffer fontTag = this.OpenTag(Tag.DefineFont3, font.Name + "; id=" + fid); char[] codes = font.CodePoints; /* Tag.DefineFont3 */ { fontTag.WriteUI16((uint)fid); fontTag.WriteBit(font.HasLayout); fontTag.WriteBit(false); /* ISSUE 50: ShiftJIS support */ fontTag.WriteBit(font.IsSmall); fontTag.WriteBit(false); /* ISSUE 51: ANSI support, though I think this might never be false. */ fontTag.WriteBit(true); /* ISSUE 52: We always write wide offsets. This is because we're too lazy to measure our table. */ fontTag.WriteBit(true); /* Spec says must be true. */ fontTag.WriteBit(font.IsItalic); fontTag.WriteBit(font.IsBold); fontTag.WriteUI8((uint)font.LanguageCode); fontTag.WriteString(font.Name, true); fontTag.WriteUI16((uint)font.GlyphCount); byte[][] shapeData = new byte[font.GlyphCount][]; int totalShapeBytes = 0; for (int i = 0; i < font.GlyphCount; i++) { Tag format; shapeData[i] = ShapeWriter.ShapeToBytes(font.GetGlyphShape(codes[i]), out format); if (format != Tag.DefineFont3) { throw new SWFModellerException(SWFModellerError.Internal, "Can't write non-font shapes as glyphs"); } totalShapeBytes += shapeData[i].Length; } int startOffset = font.GlyphCount * 4 + 4; /* 4 bytes per offset (wide offsets) + 4 for the code table offset */ int nextOffset = startOffset; foreach (byte[] shapeBytes in shapeData) { fontTag.WriteUI32((uint)nextOffset); nextOffset += shapeBytes.Length; } fontTag.WriteUI32((uint)(startOffset + totalShapeBytes)); foreach (byte[] shapeBytes in shapeData) { fontTag.WriteBytes(shapeBytes); } foreach (char code in codes) { fontTag.WriteUI16((uint)code); } if (font.HasLayout) { fontTag.WriteSI16(font.Ascent.Value); fontTag.WriteSI16(font.Descent.Value); fontTag.WriteSI16(font.Leading.Value); Rect[] bounds = new Rect[font.GlyphCount]; int boundsPos = 0; foreach (char c in codes) { GlyphLayout gl = font.GetLayout(c); fontTag.WriteSI16(gl.Advance); bounds[boundsPos++] = gl.Bounds; } foreach (Rect bound in bounds) { fontTag.WriteRect(bound); fontTag.Align8(); } fontTag.WriteUI16((uint)font.KerningTable.Length); foreach (KerningPair kern in font.KerningTable) { fontTag.WriteUI16(kern.LeftChar); fontTag.WriteUI16(kern.RightChar); fontTag.WriteSI16(kern.Adjustment); } } } this.CloseTag(); if (font.HasPixelAlignment) { WriteBuffer zonesTag = this.OpenTag(Tag.DefineFontAlignZones, font.Name + "; id=" + fid); zonesTag.WriteUI16((uint)fid); if (font.ThicknessHint == null) { throw new SWFModellerException(SWFModellerError.Internal, "Can't have pixel aligmnent without a font thickness hint."); } zonesTag.WriteUBits((uint)font.ThicknessHint, 2); zonesTag.WriteUBits(0, 6); /* Reserved */ foreach (char c in codes) { PixelAlignment pa = font.GetPixelAligment(c); if (pa.ZoneInfo.Length != 2) { throw new SWFModellerException(SWFModellerError.Internal, "Pixel aligment should always have 2 zones."); } zonesTag.WriteUI8((uint)pa.ZoneInfo.Length); foreach (PixelAlignment.ZoneData zi in pa.ZoneInfo) { /* These int values are just unparsed 16-bit floats. */ zonesTag.WriteUI16((uint)zi.AlignmentCoord); zonesTag.WriteUI16((uint)zi.Range); } zonesTag.WriteUBits(0, 6); /* Reserved */ zonesTag.WriteBit(pa.HasY); zonesTag.WriteBit(pa.HasX); } this.CloseTag(); } if (font.HasExtraNameInfo) { WriteBuffer nameTag = this.OpenTag(Tag.DefineFontName, font.FullName + "; id=" + fid); nameTag.WriteUI16((uint)fid); nameTag.WriteString(font.FullName); nameTag.WriteString(font.Copyright); this.CloseTag(); } }
private static void AddGlyph(Dictionary <char, GlyphLayout> glyphDict, GlyphLayout glyph) { glyphDict.Add(glyph.Character, glyph); }
internal TextShapingService(TextServiceHub hub) { this._hub = hub; //create glyph layout instance with default setting _glyphLayout = new GlyphLayout(); }
private void button3_Click(object sender, EventArgs e) { //it should be faster if we use 'mesh' cache //instead of read-transform it every time like code above(button2_click) LoadFont(); float font_size_in_Point = 20; _glyphMeshStore.SetFont(_latinModernMathFont, font_size_in_Point);//20= font size _glyphMeshStore.FlipGlyphUpward = true; float px_scale = _latinModernMathFont.CalculateScaleToPixelFromPointSize(font_size_in_Point); using (Tools.BorrowAggPainter(_memBmp, out var p)) { p.Clear(PixelFarm.Drawing.Color.White); float prevX = p.OriginX; float prevY = p.OriginY; int line_left = 10; int line_top = 50; p.SetOrigin(line_left, line_top);//*** test //draw reference point p.FillRect(0, 0, 5, 5, PixelFarm.Drawing.Color.Red); char[] test_str = "‽_x‾".ToCharArray(); int inline_left = 0; int inline_top = 0; //---------- GlyphLayout glyphLayout = new GlyphLayout(); glyphLayout.ScriptLang = new ScriptLang("math"); glyphLayout.Typeface = _latinModernMathFont; //temp fix for some typeface glyphLayout.SetGlyphIndexNotFoundHandler((glyph_layout, codepoint, next_codepoint) => { switch (codepoint) { //overline unicode case 8254: return(2246); //overline-combine, this will break into 3 parts in math layout process } return(0); }); // glyphLayout.Layout(test_str, 0, test_str.Length); List <UnscaledGlyphPlan> glyphPlans = new List <UnscaledGlyphPlan>(); foreach (UnscaledGlyphPlan glypyPlan in glyphLayout.GetUnscaledGlyphPlanIter()) { glyphPlans.Add(glypyPlan); } //-------- for (int i = 0; i < glyphPlans.Count; ++i) { //ushort glyphIndex = _latinModernMathFont.GetGlyphIndex((int)test_str[i]); ////do some glyph-substitution //ushort advW = _latinModernMathFont.GetAdvanceWidth((int)test_str[i]);//unscale glyph width //now scale it to specific font size UnscaledGlyphPlan glyphPlan = glyphPlans[i]; int advW_s = (int)System.Math.Round(px_scale * glyphPlan.AdvanceX); VertexStore v1 = _glyphMeshStore.GetGlyphMesh(glyphPlan.glyphIndex); p.SetOrigin(line_left + inline_left, line_top + inline_top); p.Fill(v1, PixelFarm.Drawing.Color.Black); inline_left += advW_s;//move } //restore p.SetOrigin(prevX, prevY); } //----------- CopyBitmapToScreen(); }
public TextServices() { typefaceStore = new TypefaceStore(); typefaceStore.FontCollection = InstalledFontCollection.GetSharedFontCollection(null); _glyphLayout = new GlyphLayout(); }
/// <summary> /// Renders the glyph to the configured rasterizer. /// </summary> /// <param name="glyphLayout">The glyph layout.</param> /// <param name="scalingFactor">The scaling factor.</param> internal void RenderGlyph(GlyphLayout glyphLayout, int scalingFactor) { int x = glyphLayout.TopLeft.X; int y = glyphLayout.TopLeft.Y; Glyph glyph = glyphLayout.glyph; var rasterizer = new ToPixelRasterizer(x, y, scalingFactor, FontToPixelDivisor, _rasterizer); ushort[] contours = glyph.EndPoints; short[] xs = glyph.X; short[] ys = glyph.Y; bool[] onCurves = glyph.On; int npoints = xs.Length; int startContour = 0; int cpoint_index = 0; rasterizer.BeginRead(contours.Length); int lastMoveX = 0; int lastMoveY = 0; int controlPointCount = 0; for (int i = 0; i < contours.Length; i++) { int nextContour = contours[startContour] + 1; bool isFirstPoint = true; Point <int> secondControlPoint = new Point <int>(); Point <int> thirdControlPoint = new Point <int>(); bool justFromCurveMode = false; for (; cpoint_index < nextContour; ++cpoint_index) { short vpoint_x = xs[cpoint_index]; short vpoint_y = ys[cpoint_index]; if (onCurves[cpoint_index]) { //on curve if (justFromCurveMode) { switch (controlPointCount) { case 1: { rasterizer.Curve3( secondControlPoint.x, secondControlPoint.y, vpoint_x, vpoint_y); } break; case 2: { rasterizer.Curve4( secondControlPoint.x, secondControlPoint.y, thirdControlPoint.x, thirdControlPoint.y, vpoint_x, vpoint_y); } break; default: { throw new NotSupportedException(); } } controlPointCount = 0; justFromCurveMode = false; } else { if (isFirstPoint) { isFirstPoint = false; lastMoveX = vpoint_x; lastMoveY = vpoint_y; rasterizer.MoveTo(lastMoveX, lastMoveY); } else { rasterizer.LineTo(vpoint_x, vpoint_y); } } } else { switch (controlPointCount) { case 0: { secondControlPoint = new Point <int>(vpoint_x, vpoint_y); } break; case 1: { //we already have prev second control point //so auto calculate line to //between 2 point Point <int> mid = GetMidPoint(secondControlPoint, vpoint_x, vpoint_y); //---------- //generate curve3 rasterizer.Curve3( secondControlPoint.x, secondControlPoint.y, mid.x, mid.y); //------------------------ controlPointCount--; //------------------------ //printf("[%d] bzc2nd, x: %d,y:%d \n", mm, vpoint.x, vpoint.y); secondControlPoint = new Point <int>(vpoint_x, vpoint_y); } break; default: { throw new NotSupportedException("Too many control points"); } } controlPointCount++; justFromCurveMode = true; } } //-------- //close figure //if in curve mode if (justFromCurveMode) { switch (controlPointCount) { case 0: break; case 1: { rasterizer.Curve3( secondControlPoint.x, secondControlPoint.y, lastMoveX, lastMoveY); } break; case 2: { rasterizer.Curve4( secondControlPoint.x, secondControlPoint.y, thirdControlPoint.x, thirdControlPoint.y, lastMoveX, lastMoveY); } break; default: { throw new NotSupportedException("Too many control points"); } } justFromCurveMode = false; controlPointCount = 0; } rasterizer.CloseFigure(); //-------- startContour++; } rasterizer.EndRead(); }
private void ReadFont(Tag fontType) { int fontID = this.sdtr.ReadUI16(); /* Bunch of flags */ bool hasLayout = this.sdtr.ReadBit(); bool isShiftJIS = this.sdtr.ReadBit(); bool isSmallText = this.sdtr.ReadBit(); bool isANSI = this.sdtr.ReadBit(); bool isWideOffsets = this.sdtr.ReadBit(); bool isWideCodes = this.sdtr.ReadBit(); bool isItalic = this.sdtr.ReadBit(); bool isBold = this.sdtr.ReadBit(); if (!isWideCodes) { throw new SWFModellerException(SWFModellerError.SWFParsing, "Non-wide codes in font encodings are not valid.", swf.Context); } if (isShiftJIS) { /* ISSUE 50 */ throw new SWFModellerException(SWFModellerError.UnimplementedFeature, "ShiftJIS character encoding is not supported.", swf.Context); } int language = this.sdtr.ReadUI8(); string name = this.sdtr.ReadLengthedUTF8(this.sdtr.ReadUI8()); int numGlyphs = this.sdtr.ReadUI16(); #if DEBUG this.Log("id=" + fontID + ", name=" + name); #endif SWFFont font = new SWFFont( (SWFFont.Language)language, name, isBold, isItalic, isSmallText, numGlyphs); int startOffset = (int)this.sdtr.Offset; /* The offset table measures from this point. */ int[] shapeOffsets = new int[numGlyphs]; for (int i = 0; i < numGlyphs; i++) { shapeOffsets[i] = isWideOffsets ? (int)this.sdtr.ReadUI32() : this.sdtr.ReadUI16(); } int codeTableOffset = isWideOffsets ? (int)this.sdtr.ReadUI32() : this.sdtr.ReadUI16(); IShape[] shapes = new IShape[numGlyphs]; for (int i = 0; i < numGlyphs; i++) { int shapeOffset = (int)sdtr.Offset - startOffset; if (shapeOffsets[i] != shapeOffset) { throw new SWFModellerException(SWFModellerError.SWFParsing, "Bad font data.", swf.Context); } int end = codeTableOffset; if (i < numGlyphs - 1) { end = shapeOffsets[i + 1]; } int len = end - shapeOffset; byte[] shapeData = this.sdtr.ReadByteBlock(len); shapes[i] = new ShapeParser().Parse(fontType, shapeData, this); } char[] codes = new char[numGlyphs]; for (int i = 0; i < numGlyphs; i++) { codes[i] = (char)this.sdtr.ReadUI16(); font.AddGlyph(codes[i], shapes[i]); } if (hasLayout) { font.Ascent = this.sdtr.ReadSI16(); font.Descent = this.sdtr.ReadSI16(); font.Leading = this.sdtr.ReadSI16(); GlyphLayout[] layouts = new GlyphLayout[numGlyphs]; for (int i = 0; i < numGlyphs; i++) { layouts[i] = new GlyphLayout() { Advance = this.sdtr.ReadSI16() }; } for (int i = 0; i < numGlyphs; i++) { layouts[i].Bounds = this.sdtr.ReadRect(); font.AddLayout(codes[i], layouts[i]); } int kerningCount = this.sdtr.ReadUI16(); KerningPair[] kernTable = new KerningPair[kerningCount]; for (int i = 0; i < kerningCount; i++) { kernTable[i] = new KerningPair() { LeftChar = (char)(isWideCodes ? this.sdtr.ReadUI16() : this.sdtr.ReadUI8()), RightChar = (char)(isWideCodes ? this.sdtr.ReadUI16() : this.sdtr.ReadUI8()), Adjustment = this.sdtr.ReadSI16() }; } font.KerningTable = kernTable; } fontDict.Add(fontID, font); swf.AddFont(font); }
void Issue118(PaintEventArgs e) { Text = "Issue 118 Demo"; const string s = "0123456789"; const float z = 80; const bool f = true; var m = GetType().Assembly; var t = new OpenFontReader().Read (m.GetManifestResourceStream (Array.Find(m.GetManifestResourceNames(), n => n.EndsWith("otf")))); t.UpdateAllCffGlyphBounds(); var c = t.CalculateScaleToPixelFromPointSize(z); var l = new GlyphLayout { Typeface = t }; var q = new GlyphLayout { Typeface = t }; l.Layout(s.ToCharArray(), 0, s.Length); var p = l.ResultUnscaledGlyphPositions; var b = new B(t); var r = new SampleWinForms.GlyphTranslatorToGdiPath(); var h = Pens.Black.Brush; var u = Pens.Blue; var v = Pens.Red; const bool _ = true; var j = true; using (var g = e.Graphics) { if (f) { g.ScaleTransform(1, -1); g.TranslateTransform(0, -Height / 2); } for (var i = 0; i < s.Length; i++, j ^= true) { var o = q.LayoutAndMeasureString(new[] { s[i] }, 0, 1, z); var n = p.GetGlyph(i, out var x, out var y, out var w); var a = g.Save(); var d = t.GetGlyphByIndex(n).Bounds; var k = R.FromLTRB(d.XMin * c, d.YMin * c, d.XMax * c, d.YMax * c); g.TranslateTransform(x * c, y * c); b.Build(s[i], z); b.ReadShapes(r); r.ResultGraphicsPath.CloseFigure(); g.FillPath(h, r.ResultGraphicsPath); if (_ || j) { g.DrawRectangle(u, 0, 0, o.width, o.ascending - o.descending); } if (_ || !j) { g.DrawRectangle(v, k.X, k.Y, k.Width, k.Height); } g.Restore(a); g.TranslateTransform(w * c, 0); } g.ResetTransform(); g.DrawString("Blue = LayoutAndMeasureString, Red = Glyph.Bounds", Font, h, 0, 0); } }