public bool PrepareLetterDefinitions(string text) { List <char> newChars = new List <char>(); FindNewCharacters(text, ref newChars); if (newChars.Count == 0) { return(false); } var newCharString = new string(newChars.ToArray()); var glyphs = _TextPaint.GetGlyphs(newCharString); var glyphWidths = _TextPaint.GetGlyphWidths(newCharString); for (int i = 0; i < glyphs.Length; i++) { FontLetterDefinition tempDef = new FontLetterDefinition(); if (glyphs[i] == 0) { _LetterDefinitions[newCharString[i]] = tempDef; } else { tempDef.ValidDefinition = true; tempDef.AdvanceX = glyphWidths[i]; _LetterDefinitions[newCharString[i]] = tempDef; } } return(true); }
/// <summary> /// Calculate any overhang for this text line /// </summary> /// <param name="right"></param> /// <param name="leftOverhang"></param> /// <param name="rightOverhang"></param> internal void UpdateOverhang(float right, ref float leftOverhang, ref float rightOverhang) { if (RunKind == FontRunKind.TrailingWhitespace) { return; } if (Glyphs.Length == 0) { return; } using (var paint = new SKPaint()) { float glyphScale = 1; if (Style.FontVariant == FontVariant.SuperScript) { glyphScale = 0.65f; } if (Style.FontVariant == FontVariant.SubScript) { glyphScale = 0.65f; } paint.TextEncoding = SKTextEncoding.GlyphId; paint.Typeface = Typeface; paint.TextSize = Style.FontSize * glyphScale; paint.SubpixelText = true; paint.IsAntialias = true; paint.LcdRenderText = false; unsafe { fixed(ushort *pGlyphs = Glyphs.Underlying) { paint.GetGlyphWidths((IntPtr)(pGlyphs + Start), sizeof(ushort) * Glyphs.Length, out var bounds); if (bounds != null) { for (int i = 0; i < bounds.Length; i++) { float gx = GlyphPositions[i].X; var loh = -(gx + bounds[i].Left); if (loh > leftOverhang) { leftOverhang = loh; } var roh = (gx + bounds[i].Right + 1) - right; if (roh > rightOverhang) { rightOverhang = roh; } } } } } } }
public void GetGlyphWidthsReturnsTheCorrectAmount() { var paint = new SKPaint(); var widths = paint.GetGlyphWidths("Hello World!", out var bounds); Assert.Equal(widths.Length, bounds.Length); }
private static void DrawSkia(SKCanvas canvas, SKImageInfo info, SKBitmap skBitmap, string text) { // get the screen density for scaling var scale = 1f; var scaledSize = new SKSize(info.Width / scale, info.Height / scale); // handle the device screen density canvas.Scale(scale); // make sure the canvas is blank canvas.Clear(SKColors.Pink); var scaleBg = skBitmap.Width * 1f / skBitmap.Height; var scalePanel = 16 * 1f / 9; var left = 0f; var top = 0f; var size = new SKSize(0, 0); if (scaleBg > scalePanel) { size.Height = scaledSize.Height; size.Width = size.Height * scaleBg; left = (scaledSize.Width - size.Width) / 2; } else { //size.Width = scaledSize.Width; //size.Height = size.Width / scaleBg; //top = (scaledSize.Height - size.Height) / 2; size.Height = scaledSize.Height; size.Width = size.Height * scaleBg; left = (scaledSize.Width - size.Width) / 2; } canvas.DrawBitmap(skBitmap, new SKRect(left, top, left + size.Width, top + size.Height)); // draw some text var paint = new SKPaint { Color = SKColors.Black, IsAntialias = true, Style = SKPaintStyle.Fill, TextAlign = SKTextAlign.Center, TextSize = 50 }; var widths = paint.GetGlyphWidths(text); var total = widths.Sum() / 2; //var coord = new SKPoint(scaledSize.Width / 2, (scaledSize.Height + paint.TextSize) / 2); var coord = new SKPoint(left + 20 + total, 20 + paint.TextSize); canvas.DrawText(text, coord, paint); }
public void GetGlyphWidthsAreCorrect() { var paint = new SKPaint(); var widths = paint.GetGlyphWidths("Hello World!", out var bounds); // make sure the 'l' glyphs are the same width Assert.True(widths[2] > 0); Assert.True(widths[2] == widths[3]); Assert.True(widths[2] == widths[9]); // make sure the 'l' bounds are the same size Assert.False(bounds[2].IsEmpty); Assert.True(bounds[2] == bounds[3]); Assert.True(bounds[2] == bounds[9]); // make sure the 'l' and 'W' glyphs are NOT the same width Assert.True(widths[2] != widths[6]); // make sure the 'l' and 'W' bounds are NOT the same width Assert.True(bounds[2] != bounds[6]); }
/// <summary> /// Constructs a new TextShaper /// </summary> /// <param name="typeface">The typeface of this shaper</param> private TextShaper(SKTypeface typeface) { // Store typeface _typeface = typeface; // Load the typeface stream to a HarfBuzz font int index; using (var blob = GetHarfBuzzBlob(typeface.OpenStream(out index))) using (var face = new Face(blob, (uint)index)) { face.UnitsPerEm = typeface.UnitsPerEm; _font = new HarfBuzzSharp.Font(face); _font.SetScale(overScale, overScale); _font.SetFunctionsOpenType(); } // Get font metrics for this typeface using (var paint = new SKPaint()) { paint.Typeface = typeface; paint.TextSize = overScale; _fontMetrics = paint.FontMetrics; // This is a temporary hack until SkiaSharp exposes // a way to check if a font is fixed pitch. For now // we just measure and `i` and a `w` and see if they're // the same width. float[] widths = paint.GetGlyphWidths("iw", out var rects); _isFixedPitch = widths != null && widths.Length > 1 && widths[0] == widths[1]; if (_isFixedPitch) { _fixedCharacterWidth = widths[0]; } } }
internal unsafe void Run() { if (!needRun) { return; } lines.Clear(); //设置paint参数 var paint = new SKPaint(); paint.TextEncoding = SKTextEncoding.Utf16; font.ApplyToSKPaint(paint, GraphicsUnit.Pixel, dpi); //注意目标类型为像素 //测量FontMetric SKFontMetrics metrics = font.FontMetrics; //根据文字排版方向确认限宽及限高 float maxWidth = Math.Min(32767, IsVertical ? height : width); //TODO: float maxHeight = IsVertical ? width : height; //TODO:计算高度不满足一行的情况 fixed(char *ptr = Text) { byte *textPtr = (byte *)ptr; byte *startTextPtr = (byte *)ptr; int totalBytes = text.Length * 2; int leftBytes = totalBytes; float y = 0; while (true) { //根据是否允许换行,调用不同的breakText方法 var measuredWidth = 0f; var lineBytes = (int)paint.BreakText(new IntPtr(startTextPtr), new IntPtr(leftBytes), maxWidth, out measuredWidth); //var lineBytes = SkiaApi.sk_paint_break_text_icu(paint, new IntPtr(startTextPtr), leftBytes, maxWidth, null); //TODO:***** fix maxWidth var newLine = new TextLayoutLine(this); newLine.startByteIndex = (int)(startTextPtr - textPtr); newLine.byteLength = lineBytes; newLine.widths = paint.GetGlyphWidths(new IntPtr(startTextPtr), lineBytes); //newLine.startCharIndex = 0; //TODO: //根据对齐方式计算Line的offsetX值 if (stringFormat != null && stringFormat.Alignment != StringAlignment.Near) { //计算当前行Ink总宽度 float lineInkWidth = 0f; for (int i = 0; i < newLine.widths.Length; i++) { lineInkWidth += newLine.widths[i]; } float lineSpace = width - lineInkWidth; if (lineSpace > 0f) { if (stringFormat.Alignment == StringAlignment.Center) { newLine.offsetX = lineSpace / 2f; } else if (stringFormat.Alignment == StringAlignment.Far) { newLine.offsetX = lineSpace; } } } //添加新行 lines.Add(newLine); //判断是否允许换行,不允许直接退出循环 if (IsNoWrap) { break; } //计算下一行高度是否超出限高 y = y - metrics.Ascent + metrics.Descent + metrics.Leading; if ((y - metrics.Ascent + metrics.Descent) > maxHeight) { break; } //偏移剩余部分,并判断是否全部计算完毕 startTextPtr += lineBytes; if ((int)(startTextPtr - textPtr) >= totalBytes) { break; } leftBytes -= lineBytes; } } //根据对齐方式计算offsetY的值 CalcOffsetY(); needRun = false; }