Как правильно выровнять текст по левому краю с помощью DrawString? - PullRequest
0 голосов
/ 03 января 2019

Я рисую текст в закадровом растровом изображении. К сожалению, текст не выровнен по левому краю (см. Изображение ниже). Текст должен касаться левого поля (синяя линия), но отклоняется на несколько пикселей. Расстояние увеличивается с размером текста.

Left alignment

Как мне избавиться от этого расстояния?

Я использую .NET Framework 4.6.1. Но мне кажется, что это более общая проблема GDI +, которую я не понимаю.

Код, использованный для генерации образца:

using System.Drawing;
using System.Drawing.Imaging;

namespace LeftAlignment
{
    class Program
    {
        static void Main(string[] args)
        {
            const int LeftMargin = 10;

            // create off-screen bitmap
            using (Bitmap bitmap = new Bitmap(300, 100))
            {
                // create graphics context
                using (Graphics graphics = Graphics.FromImage(bitmap))
                {
                    // clear bitmap
                    graphics.FillRectangle(Brushes.White, 0, 0, bitmap.Width, bitmap.Height);

                    // draw border and left margin
                    graphics.DrawRectangle(Pens.Gray, new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1));
                    graphics.DrawLine(Pens.Blue, LeftMargin, 8, LeftMargin, 92);

                    // draw string at 24 pt
                    Font font = new Font("Arial", 24);
                    graphics.DrawString("Cool water", font, Brushes.Black, LeftMargin, 8);

                    // draw string at 36 pt
                    font = new Font("Arial", 36);
                    graphics.DrawString("Cool water", font, Brushes.Black, LeftMargin, 44);
                }

                // save result as PNG
                bitmap.Save("alignment.png", ImageFormat.Png);
            }
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 03 января 2019

История гласит, что Microsoft добавила заполнение в GDI +, чтобы упростить реализацию элементов управления.У старого GDI такой проблемы не было.

Когда Microsoft поняла, что это ошибка, они добавили класс TextRenderer , который обходит GDI + и использует лучшую реализацию GDI.

Предположительно, отступ составляет 1/6 em слева и 1/4 em справа.

У вас есть два варианта:

  1. Использовать TextRenderer.DrawText .Тем не менее, это часть Windows Forms .Поэтому он не будет доступен ни в .NET Standard, ни в .NET Core.

  2. Использование Graphics.DrawString с волшебной опцией StringFormat.GenericTypographic .Он волшебным образом удаляет отступы.

Также см .:

0 голосов
/ 03 января 2019

На основе ссылки, предоставленной @ChrisW, я создал улучшенную версию (см. Ниже).Он использует MeasureCharacterRanges для измерения заполнения, добавленного DrawString.Результат выглядит много масла:

Improved left alignment

Как вы можете видеть, это не идеально.Между синей линией и буквой «C» все еще есть пробел, потому что измеренный прямоугольник включает в себя так называемый левый подшипник , то есть левую сторону пробела, добавленную между двумя символами.

Так что я все еще ищу еще лучшее решение.Может быть возможным рассчитать подшипник и вычесть половину подшипника в дополнение к измеренному заполнению.Надеемся, что это возможно с .NET Standard 2.0 ...

Кстати: я измерял несколько шрифтов, стили шрифтов, размеры шрифтов и разрешения.Похоже, что заполнение исправлено.Его можно вычислить как:

отступ = 0,002312554 × размер шрифта × разрешение

(заполнение в пикселях, размер шрифта в пикселях, разрешение в пикселях/ inch)

В качестве примера: для шрифта 24pt и графического разрешения 96dpi отступ будет 5,33 пикселя.

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;

namespace LeftAlignment
{
    class Program
    {
        static void ImprovedDrawString(Graphics graphics, string text, Font font, float x, float y)
        {
            // measure left padding
            StringFormat sf = new StringFormat(StringFormatFlags.NoClip);
            sf.SetMeasurableCharacterRanges(new CharacterRange[] { new CharacterRange(0, 1) });
            Region[] r = graphics.MeasureCharacterRanges(text, font, new RectangleF(0, 0, 1000, 100), sf);
            float leftPadding = r[0].GetBounds(graphics).Left;

            // draw string
            sf = new StringFormat(StringFormatFlags.NoClip);
            graphics.DrawString(text, font, Brushes.Black, x - leftPadding, y, sf);
        }

        static void Main(string[] args)
        {
            const int LeftMargin = 10;
            const string Text = "Cool water";

            // create off-screen bitmap
            using (Bitmap bitmap = new Bitmap(300, 100))
            {
                // create graphics context
                using (Graphics graphics = Graphics.FromImage(bitmap))
                {
                    // enable high-quality output
                    graphics.SmoothingMode = SmoothingMode.AntiAlias;
                    graphics.TextRenderingHint = TextRenderingHint.AntiAlias;

                    // clear bitmap
                    graphics.FillRectangle(Brushes.White, 0, 0, bitmap.Width, bitmap.Height);

                    // draw border and left margin
                    graphics.DrawRectangle(Pens.Gray, new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1));
                    graphics.DrawLine(Pens.Blue, LeftMargin, 8, LeftMargin, 92);

                    // draw string at 24 pt
                    Font font = new Font("Arial", 24);
                    ImprovedDrawString(graphics, Text, font, LeftMargin, 8);

                    // draw string at 36 pt
                    font = new Font("Arial", 36);
                    ImprovedDrawString(graphics, Text, font, LeftMargin, 44);
                }

                // save result as PNG
                bitmap.Save("alignment.png", ImageFormat.Png);
            }
        }
    }
}
...