5

Maybe I've got something wrong, but... I want to simulate character spacing. I break the word (text) into the list of single characters, measure their widths, and then painting them one after another on the bitmap. I supposed, that overall width of the rendered text will be the same as the width of the whole not splitted string, but there is something wrong. Rendering characters in a loop show wider result. Is there any way to get common (expected) results?

here is a code snippet:

private struct CharWidths
{
    public char Char;
    public float Width;
}

private List<CharWidths> CharacterWidths = new List<CharWidths>();

...

private void GetCharacterWidths(string Text, Bitmap BMP)
{
    int i;
    int l = Text.Length;
    CharacterWidths.Clear();
    Graphics g = Graphics.FromImage(BMP);

    CharWidths cw = new CharWidths();
    for (i = 0; i < l; i++)
    {
        Size textSize = TextRenderer.MeasureText(Text[i].ToString(), Font);
        cw.Char = Text[i];
        cw.Width = textSize.Width;
        CharacterWidths.Add(cw);
    }
}

...

public void RenderToBitmap(Bitmap BMP)
{
    //MessageBox.Show("color");

    Graphics g = Graphics.FromImage(BMP);
    GetCharacterWidths("Lyborko", BMP);

    int i;
    float X = 0;
    PointF P = new PointF(); 
    for (i = 0; i < CharacterWidths.Count; i++)
    {
        P.X = X;
        P.Y = 0;

        g.DrawString(CharacterWidths[i].Char.ToString(), Font, Brushes.White, P);

        X = X+CharacterWidths[i].Width;
    }

    P.X = 0;
    P.Y = 30;
    g.DrawString("Lyborko", Font, Brushes.White, P);
    // see the difference
}

Thanx a lot

John H
  • 14,422
  • 4
  • 41
  • 74
lyborko
  • 2,571
  • 3
  • 26
  • 54
  • 2
    Your approach is pretty much doomed once you get to "unusual" characters. For example there are combining characters, where several codepoints are rendered as a single symbol on the screen. Your code also splits the text into UTF-16 codeunits, not codepoints. – CodesInChaos Mar 31 '12 at 17:16
  • 2
    @CodeInChaos Ok, it is surely not the best of, but what should I do? – lyborko Mar 31 '12 at 17:20
  • 1
    This question demonstrates well why this doesn't work: http://stackoverflow.com/q/9945972/17034 Measure strings, not characters. – Hans Passant Mar 31 '12 at 18:19

1 Answers1

2

First of all should say that don't have a silver bullet solution for this, but have a couple of suggessions on subject:

  1. Considering that you by calling TextRenderer.MeasureText do not pass current device context (the same one you use to draw a string after) and knowing a simple fact that MeasureText simply in case of lack of that parameter creates a new one compatible with desktop and calls DrawTextEx WindowsSDK function, I would say first use an overload of MeasureText where you specify like a first argument device context which you use to render a text after. Could make a difference.

  2. If it fails, I would try to use Control.GetPreferredSize method to guess most presize possible rendering dimension of the control on the screen, so actually the dimension of you future string's bitmap. To do that you can create some temporary control, assign a string, render and after call this function. It's clear to me that this solution may hardly fit in your app architecture, but can possibly produce a better results.

Hope this helps.

Tigran
  • 61,654
  • 8
  • 86
  • 123
  • I tried to specify DeviceContext as you proposed: Size textSize = TextRenderer.MeasureText(g, Text[i].ToString(), Font); but no change occured... – lyborko Mar 31 '12 at 17:52
  • @lyborko: and what about second option ? – Tigran Mar 31 '12 at 18:10
  • the second option I didn't tried. I have got remarkable results by setting TextFlags, but still, the difference is visible (in pixel though). See : TextFormatFlags flags = TextFormatFlags.NoPadding; Size si = new Size(1, 1); Size textSize = TextRenderer.MeasureText(g, Text[i].ToString(), Font, si, flags); – lyborko Mar 31 '12 at 18:15