Custom richtextbox control kerning issues

泪湿孤枕 提交于 2020-01-06 06:56:25

问题


Okay, so I have been working on something for a little while and I have gotten to the point where I am planning the Text rendering part.

I can already draw strings of text in two ways; DrawString and TextRenderer.DrawText. I prefer DrawText since measuring text is more accurate when using TextRenderer.Measure text.

I have a class:

public class Character 
{
public string character {get; set; } 
public Font font {get; set; }
public Point position {get; set; }
} 

And a list of all characters pressed:

public List<Character> chars = new List<Character>();

Now my problem is that I need to be able to set a different font and color and boldness or italicization to any given selected characters or words at runtime. So I can't just draw a whole string because then there'd be no way for me to set individual font settings for each character the user has selected to change.

So I need to be able to store different font style info for each character and then add them all to a list so I can kinda go through each one and draw each one as it should be drawn (I. E. each char having its own style etc).

This solution works fine for me. And since I've not been able to find any info about this anywhere for months, I'm totally stuck.

My main problem is that because I am drawing char by char, I have no idea how far apart each character should be from the previously drawn character (kerning).

For input (text box) controls, how can we custom draw text and allow the user to make a part of a word blue, and the other half of the word a different size and color and style, for example, while still adhering to proper kerning settings?

How do we know where to draw each character?

People have said just keep restarting the whole string at once. But that doesn't solve my initial problem. I need to be able to draw each char one by one so I can save font info about it.


回答1:


Kerning and Character Spacing are different and if you want to have complete control over what your code prints you may need to implement both.

Let's look at an example output first :

Image one shows direct output with an extra character spacing of 1 pixel, no kerning.

Image two has some kerning applied, but only for three kerning pairs.

I have tried to make things clearer by also drawing the result of the characterwise text measurements. Also there is a tiled 1 pixel raster as the panel BackgroundImage. (To see it better you may want to download the png files!)

private void panel2_Paint(object sender, PaintEventArgs e)
{
   string fullText = "Text;1/2' LTA";
   StringFormat strgfmt = StringFormat.GenericTypographic;
   Font font = new Font("Times", 60f, FontStyle.Regular);
   float x = 0f;
   using (SolidBrush brush = new SolidBrush(Color.FromArgb(127, 0, 127, 127)))
   {
        for (int i = 0; i < fullText.Length; i++)
        {
            string text = fullText.Substring(i, 1);
            SizeF sf = e.Graphics.MeasureString(text, font, 9999, strgfmt );
            e.Graphics.FillRectangle(brush, new RectangleF(new PointF(x, 0f), sf));
            e.Graphics.DrawString(text, font, Brushes.Black, x, 0, strgfmt );
            x += sf.Width + 1;  // character spacing = +1

            //if (i < fullText.Length - 1) doKerning(fullText.Substring(i, 2), ref x);
        }
   }
}

void doKerning(string c12, ref float x)
{
    if (smallKerningTable.ContainsKey(c12)) x -= smallKerningTable[c12];
}

Dictionary<string, float> smallKerningTable = new Dictionary<string, float>();

void initKerningTable()
{
    smallKerningTable.Add("Te", 7f);
    smallKerningTable.Add("LT", 8f);
    smallKerningTable.Add("TA", 11f);
    //..
}

This is how the background is created:

public Form1()
{
   InitializeComponent(); 
   Bitmap bmpCheck2 = new Bitmap(2, 2);   
   bmpCheck2.SetPixel(0, 0, Color.FromArgb(127, 127, 127, 0));
   panel2.BackgroundImage = bmpCheck2;
   panel2.BackgroundImageLayout = ImageLayout.Tile;
   //..
 }

If you want to use kerning you will need to build a much longer kerning table.

In real life typographers and font designers do that manually, looking hard at the glyphs, tweaking the kerning until it looks real good.

That is rather expensive and still doesn't cover font mixes.

So you may want to either

  • not use kerning after all. Make sure to use the StringFormat.GenericTypographic option both for measuring and for drawing the strings!
  • create a small kerning table for some of the especially problematic characters, like 'L', 'T', 'W', "V' and 'A'..
  • write code to create a full kerning table for all pairs you need or..
  • for all pairs

To write code to create a kerning table you would:

  • Create a Bitmap for each charcter
  • Iterate over all pairs and
  • move the second bitmap to the left until some non-transparent/ black pixels collide.
  • the moving should not got further than, say, half of the width, otherwise the distance should be reset to 0, because some character pairs will not collide at all and should not have any kerning, e.g.: '^_' or '.-'

If you want to mix fonts and /or FontStyles the key to the kerning table would have to be expanded to include some ID of the two respective fonts&styles the characters have..



来源:https://stackoverflow.com/questions/29018430/custom-richtextbox-control-kerning-issues

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!