mono / SkiaSharp

SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.
MIT License
4.41k stars 535 forks source link

[BUG] Text size measured improperly in Linux OS #838

Open VijayRM opened 5 years ago

VijayRM commented 5 years ago

Description

Particular text width was calculated wrongly in Linux OS. The same text was measured properly in Windows.

Code

        SKPaint paint = new SKPaint();
        paint.TextEncoding = SKTextEncoding.Utf8;
        paint.IsAntialias = true;
        paint.Typeface = SKTypeface.FromFamilyName("Arial", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright);
        paint.TextSize = 9;
        paint.TextAlign = SKTextAlign.Left;

        SKRect rect = new SKRect();
        float width = paint.MeasureText("Locatio", ref rect);
        Console.WriteLine(width);

        string text = ((char)61608).ToString();
        paint.Typeface = SKTypeface.FromFamilyName("Wingdings", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright);
        paint.TextSize = 12;
        rect = new SKRect();
        width = paint.MeasureText(text, ref rect);
        Console.WriteLine(width);

Expected Behavior

The width of the text should be same as Windows OS.

Text Expected Width
 Locatio  32.00098
   10.93359

Actual Behavior

Text Actual Width
 Locatio  34
   11

Basic Information

Gillibald commented 5 years ago

You have to load the same font file from stream to get the same result. Using SKTypeface.FromFamilyName("Arial") will load different fonts depending on the platform. On Linux for example FontConfig is used to query the font and most likely "Arial" is mapped as an alias to a similar font but not the same as on Windows.

VijayRM commented 5 years ago

Could you please suggest me the best alternative overload for below method, as the suggested SKTypeface.FromStream() overload doesn't have option to specify the SKFontStyleWeight, SKFontStyleWidth and SKFontStyleSlant?

SKTypeface.FromFamilyName("Arial", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright);

Gillibald commented 5 years ago

Most font files .ttf and .otf only contain one set of glyphs. For example MyFont-Regular.ttf which will be loaded as a SKTypeface with weight set to normal etc.

https://fonts.google.com/specimen/Roboto for example

VijayRM commented 5 years ago

SKTypeface.FromStream() also causes the same problem in Linux OS.

        SKPaint paint = new SKPaint();
        paint.TextEncoding = SKTextEncoding.Utf8;
        paint.IsAntialias = true;
        FileStream fStream = new FileStream(@"arialbd.ttf", FileMode.Open, FileAccess.Read);
        paint.Typeface = SKTypeface.FromStream(fStream);//SKTypeface.FromFamilyName("Arial", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright);
        paint.TextSize = 9;
        paint.TextAlign = SKTextAlign.Left;

        SKRect rect = new SKRect();
        float width = paint.MeasureText("Locatio", ref rect);
        Console.WriteLine(width);

I think it could be a bug, as most of other cases are working fine in both Windows and Linux by using below overload. Could you please confirm this?

SKTypeface.FromFamilyName("Arial", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright);

Please provide solution to resolve the issue by using above overload, as the Font stream overload requires the installed font location in all the environment. I hope already getting font from the installed location may handled internally in above API.

Gillibald commented 5 years ago

Can you try to set paint.SubpixelText = true? Maybe that isn't set by default on Linux. I will investigate this further if I find some spare time.

VijayRM commented 5 years ago

The default value of paint.SubpixelText is false in both Windows and Linux OS.

As per your suggestion, if paint.SubpixelText property was set as true, then width of the text size was calculated as expected in Linux OS.

Could you please provide more information about the use of this property? Whether this changes will introduce any side effects in Linux or Windows?

Gillibald commented 5 years ago

It seems that on Windows Subpixel positioning of glyphs is always used. There should be no side effects. You should enable it to get better rendering results. I can't give you more info. If you need more info about this you have to contact the Skia devs.

VijayRM commented 5 years ago

Thanks @Gillibald . I will do the necessary changes and update you the confirmation once after ensuring the complete testing.

VijayRM commented 5 years ago

@Gillibald - Although the provided suggestions resolved the above problems. I have found that the same problem exist for another text. Please find the example code to reproduce the issue.

        SKPaint paint = new SKPaint();
        paint.TextEncoding = SKTextEncoding.Utf8;
        paint.IsAntialias = true;
        paint.TextAlign = SKTextAlign.Left;
        //As per your suggestion, I have enabled SubpixelText property.
        paint.SubpixelText = true;
         string text = char.ConvertFromUtf32(254);
        paint.Typeface = SKTypeface.FromFile(@"wingding.ttf");
        paint.TextSize = 12;
        SKRect rect = new SKRect();
        float width = paint.MeasureText(text, ref rect);
        Console.WriteLine(width);

The width of the text was calculated in Linux is invalid. Please find the size difference between Windows & Linux from below.

Windows - 10.69922 Linux - 6

VijayRM commented 5 years ago

@Gillibald - Could you please provide solution for this issue?

Gillibald commented 5 years ago

I need the font file you are using to investigate this further. If you measure a string your should set paint.TextEncoding = SKTextEncoding.Utf16 not Utf8. That can result in errors.

VijayRM commented 5 years ago

I have attached the fonts file wingding.zip. Even if i use Utf16 encoding the same issue still exists.

martinhonovais commented 5 years ago

Hi, I've faced this problem, yesterday and I guess that I had found a workaround.

Try this: SKTypeface.FromData(SKData.Create(stream));

It has worked for me, :).

mattleibow commented 5 years ago

@Tazjoubire so are you saying that you get different results when loading the font via a stream vs a data? That seems to be a bug somewhere...

Gillibald commented 5 years ago

Yes there is a bug. That's why I implemented the GetTableData etc

Gillibald commented 5 years ago

This fails on Linux https://github.com/mono/SkiaSharp/blob/master/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz.Shared/BlobExtensions.cs#L28

GetMemoryBase returns IntPtr.Zero on Linux

martinhonovais commented 5 years ago

Hi all!

Yes @mattleibow, If I load the font from stream it does not work, otherwise, if I load font from SKData it works perfectly in both environments linux and windows.

Additional, I am trying to load a barcode font and it works very well.

Thanks.

Ghasan commented 4 years ago

For me, loading from SKData is still same. The only thing that fixed it is by setting SubpixelText to true as suggested by Gillibald. Thanks :)

parthipanr commented 3 years ago

Facing same issue even if we load from SKData and setting SubpixelText to true. Can any one please suggest to overcome this issue.

kyugoa commented 2 years ago

Not sure if it's related, the lineheight is also off in linux (is it calculated from the font size? ) see the related link on QuestPDF for detail.

Seegras commented 9 months ago

This might be unrelated to the issue, but as this is one of the few bug reports I can find that seems to be related, I add this here:

I still can't set the font size to be easily readable as a user. No matter to what I set the DPI with Xft.dpi. And MONO_MWF_SCALING doesn't seem to do anything either.

Yes, this is a serious accessibility issue.

Somebody did a patch. https://web.archive.org/web/20161025103319/http://www.vakuumverpackt.de/configurablesystemfontsformono/ which sadly isn't available any more; but I'm wondering why something like this still isn't in mono.