SixLabors / Fonts

:black_nib: Font loading and layout library.
https://sixlabors.com/products/fonts
Other
306 stars 71 forks source link

DrawText generates text with DFKai-SB or KaiU fonts differently than expected #337

Closed LoganChang closed 1 year ago

LoganChang commented 1 year ago

Prerequisites

Description

using chinese fonts on DrawText I got the error pic

Steps to Reproduce

System Configuration

JimBobSquarePants commented 1 year ago

What’s the expected vs actual output?

LoganChang commented 1 year ago

use ImageSharp.Drawing DrawText i get like this Error

but this fonts should be like this right this is create by System.Drawing

JimBobSquarePants commented 1 year ago

Can you supply the System.Drawing code also please?

LoganChang commented 1 year ago

Use FontName is "DFKai-SB" `

    public static byte[] WritetextJpg(string txt, int fontsize, int colorR, int colorG, int colorB, string fontName = "Arial Narrow Italic",bool outside = false)
    {
       Bitmap b = new Bitmap(10, 10);

        byte[] byteImage = new byte[0];

        try
        {
            int width = 0, height = 0;
            int xpos = 0, ypos = 0;
            Font f = new Font(fontName, fontsize, GraphicsUnit.Pixel);
            ArrayList Text = new ArrayList();
            string myText = "";
            int preindex = 0;
            if (txt.IndexOf("\r\n", preindex) == -1)
            {
                while (preindex < txt.Length && txt.IndexOf("\n", preindex) != -1)
                {
                    string mytemp = "";
                    int endindex = txt.IndexOf("\n", preindex);
                    mytemp = txt.Substring(preindex, endindex - preindex);
                    Text.Add(mytemp);
                    preindex = endindex + 1;
                }
                myText = txt.Substring(preindex, txt.Length - preindex);
                if (myText.Length != 0)
                    Text.Add(myText);
            }
            else
            {
                while (preindex < txt.Length && txt.IndexOf("\r\n", preindex) != -1)
                {
                    string mytemp = "";
                    int endindex = txt.IndexOf("\r\n", preindex);
                    mytemp = txt.Substring(preindex, endindex - preindex);
                    Text.Add(mytemp);
                    preindex = endindex + 2;
                }
                myText = txt.Substring(preindex, txt.Length - preindex);
                Text.Add(myText);
            }
            Color brushcolor = Color.FromArgb(colorR, colorG, colorB);
            height = (int)(Text.Count * f.Height);
            string[] tmp = new string[Text.Count];
            int i = 0;
            Bitmap a = new Bitmap(5, 5);
            Graphics h = Graphics.FromImage(a);
            foreach (string textinfo in Text)
            {
                tmp[i] = textinfo;
                width = ((int)h.MeasureString(tmp[i], f).Width > width) ? (int)h.MeasureString(tmp[i], f).Width : width;
                i++;
            }

            h.Dispose();

            a.Dispose();

            b = new Bitmap(width, height);

            Graphics g = Graphics.FromImage(b);
            g.Clear(Color.Transparent);
            SolidBrush blackBrush = new SolidBrush(Color.Black);
            foreach (string t in tmp)
            {
                g.DrawString(t, f, blackBrush, xpos, ypos);
                ypos = ypos + f.Height;
            }
            int Width = b.Width;
            int Height = b.Height;
            if (outside)
            {
                // Create pen.
                Pen pens = new Pen(brushcolor, 6);
                Rectangle rect = new Rectangle(0, 0, Width, Height);
                g.DrawRectangle(pens, rect);
            }
            double R, G, B = 0;
            B = colorB;
            G = colorG;
            R = colorR;
            if (B > 255) B = 255;
            if (G > 255) G = 255;
            if (R > 255) R = 255;

            for (int y = 0; y < Height; ++y)
            {
                for (int x = 0; x < Width; ++x)
                {
                    if (b.GetPixel(x, y).A != 0)
                    {
                        b.SetPixel(x, y, brushcolor);
                    }
                }
            }
            System.IO.MemoryStream ms = new MemoryStream();
            b.Save(ms, ImageFormat.Png);
            byteImage = ms.ToArray();

        }
        catch (Exception e)
        {
            throw e;
        }
        return byteImage;
    }

`

JimBobSquarePants commented 1 year ago

I need something I can use to debug against.

A reduced compilable sample containing.

  1. The text from the images
  2. The font call with size and type (regular, italic etc)
  3. The Graphics call that does the actual drawing.

Thanks

LoganChang commented 1 year ago
  1. word is "標楷體輸出"
  2. font size 20pt 3. using System; using System.Collections; using System.Drawing; using System.Drawing.Imaging; using System.IO; AND this project FrameWork is .Net Core 3.1
JimBobSquarePants commented 1 year ago

I'm going to need some help here.

I had a look at the shaping using Crowbar and it appears no GSUB substitutions take place. There's also no GPOS or KERN data in the font.

However, our output matches the colored glyphs on the right which clearly doesn't match the text in the input which matches what System.Drawing produces.

image

FontKit produces the same output we do and I don't know how to get HarfBuzz running in Visual Studio in order to run a unit test and debug.

I've attached the relevant font kaiu.zip

@behdad Sorry to tag you but is there something obvious I've missed?

behdad commented 1 year ago

@behdad Sorry to tag you but is there something obvious I've missed?

I looked into the font as well. cmap looks sane. GSUB only substitutes two dozen vertical glyphs. I don't see how this font can produce the system-generated output, yet I get the same Crowbar output, including the system rendering, as you do. Investigating more.

behdad commented 1 year ago

Ah. Is it possible that it's one of those fonts that requires bytecode interpretter to be run to render correctly?

behdad commented 1 year ago

Ah. Is it possible that it's one of those fonts that requires bytecode interpretter to be run to render correctly?

Doesn't look like it.

behdad commented 1 year ago

Indeed it's a difference between FreeType vs HarfBuzz outline extraction. Need to investigate more to see why.

behdad commented 1 year ago

Ah. Is it possible that it's one of those fonts that requires bytecode interpretter to be run to render correctly?

If I drop the hinting then the FreeType rendering is broken as well.

behdad commented 1 year ago

I believe FreeType has logic to always enable the bytecode interpretter for fonts like this. Finding.

behdad commented 1 year ago

https://github.com/behdad/freetype/blob/08f66322e32cc882733dfae408e040f5057ee2ac/src/truetype/ttobjs.c#L181

JimBobSquarePants commented 1 year ago

Ah! @behdad you legend!

I'd have never figured that out!

I need to do some work on how we handle composite glyphs and also make some changes to the hinter but local tests are very promising.

image

JimBobSquarePants commented 1 year ago

Yep. This is working.

image

behdad commented 1 year ago

Nice! So you have a hinter as well? Me is jealous.

JimBobSquarePants commented 1 year ago

I'm just standing on the shoulders of giants. 🙂