Измерьте позиции символов при рисовании длинных строк в C#

Пример Измерение позиций символов в нарисованном строка в C# использует метод Graphics класса GraphicsCharacterRanges для поиска позиций, в которых будут рисоваться символы в строке. К сожалению, этот метод требует, чтобы вы вызывали метод StringFormat объекта SetMeasurableCharacterRanges, и этот метод может устанавливать не более 32 диапазонов. Это означает, что вы не можете найти позиции более чем для 32 символов. Если вы попытаетесь, метод выдает неинформативное исключение «Ошибка переполнения».

Этот пример разбивает длинную строку на куски длиной не более 32 символов, а затем измеряет фрагменты отдельно. Есть несколько небольших проблем с этим подходом, который работает в этом примере.

Следующий метод MeasureCharactersInWord находит позиции символов в строке длиной не более 32 символов.

// Измерение символов в строке с помощью
// не более 32 символов.
private List MeasureCharactersInWord(
    Graphics gr, Font font, string text)
{
    List result = new List();

    using (StringFormat string_format = new StringFormat())
    {
        string_format.Alignment = StringAlignment.Near;
        string_format.LineAlignment = StringAlignment.Near;
        string_format.Trimming = StringTrimming.None;
        string_format.FormatFlags =
            StringFormatFlags.MeasureTrailingSpaces;

        CharacterRange[] ranges = new CharacterRange[text.Length];
        for (int i = 0; i < text.Length; i++)
        {
            ranges[i] = new CharacterRange(i, 1);
        }
        string_format.SetMeasurableCharacterRanges(ranges);

        // Найдите диапазоны символов.
        RectangleF rect = new RectangleF(0, 0, 10000, 100);
        Region[] regions =
            gr.MeasureCharacterRanges(
                text, font, this.ClientRectangle,
                string_format);

        // Преобразуем регионы в прямоугольники.
        foreach (Region region in regions)
            result.Add(region.GetBounds(gr));
    }

    return result;
}

Этот метод создает List & lt; RectangleF & gt;, чтобы удерживать результаты.

Затем он создает объект StringFormat, чтобы указать, как текст будет отформатирован при рисовании. Он устанавливает свойства объекта Alignment и LineAlignment, чтобы текст отображался в верхнем левом углу области рисования.

Обычно, когда вы рисуете или измеряете символы, методы рисования или измерения игнорируют конечное пространство. В этом примере некоторые части длинной строки могут заканчиваться таким пространством, и программа не может игнорировать ширину этого пространства. Чтобы метод измерения не игнорировал пробел, код устанавливает свойство StringFormat объекта FormatFlags в MeasureTrailingSpaces.

Далее метод MeasureCharactersInWord создает массив объектов CharacterRange и инициализирует их для представления диапазонов в строке, которая должна быть измерена, в данном случае отдельных символов. Код вызывает SetMeasurableCharacterRanges, чтобы сообщить объекту StringFormat о диапазонах.

Затем метод вызывает MeasureCharacterRanges, чтобы получить области, где будут рисоваться символы. Метод заканчивается путем преобразования областей в значения RectangleF и возвращает результат.

Следующий метод MeasureCharacters использует предыдущий метод для измерения строк длиной более 32 символов.

// Измерение символов в строке.
private List MeasureCharacters(Graphics gr,
    Font font, string text)
{
    List results = new List();

    // Местоположение X для следующего символа.
    float x = 0;

    // Получает символ размером 31 символ за раз.
    for (int start = 0; start < text.Length; start += 32)
    {
        // Получить подстроку.
        int len = 32;
        if (start + len >= text.Length) len = text.Length - start;
        string substring = text.Substring(start, len);

        // Измерение символов.
        List rects =
            MeasureCharactersInWord(gr, font, substring);

        // Удалить ввод для первого символа.
        if (start == 0) x += rects[0].Left;

        // Сохранить все, кроме последнего прямоугольника.
        for (int i = 0; i < rects.Count+1 - 1; i++)
        {
            RectangleF new_rect = new RectangleF(
                x, rects[i].Top,
                rects[i].Width, rects[i].Height);
            results.Add(new_rect);

            // Переместимся к X-позиции следующего символа.
            x += rects[i].Width;
        }
    }

    // Вернем результаты.
    return results;
}

Этот метод использует переменную x, чтобы отслеживать координату X прямоугольника следующего символа. Метод инициализирует x до 0.

Далее код разбивает строку на части, содержащие не более 32 символов, и вызывает метод MeasureCharactersInWord для каждой части.

Когда вы на самом деле рисуете строку, метод DrawString, кажется, добавляет немного места перед первым символом, чтобы начать все. Этот метод обрабатывает это путем добавления этого бита дополнительного пространства (сохраненного в свойстве Left прямоугольника первого символа) к x. Когда он рисует последующие фрагменты более длинной строки, DrawString не добавляет лишнее пространство, поэтому оно добавляется только для первой части.

Затем метод использует ширины возвращаемых значений RectangleF для создания прямоугольников для символов и добавления их к результату метода.

Следующий метод DrawTextInBoxes рисует строку с полями вокруг каждого символа.

// Нарисуйте длинную строку с полями вокруг каждого символа.
private void DrawTextInBoxes(Graphics gr, Font font,
    float start_x, float start_y, string text)
{
    // Измерение символов.
    List rects = MeasureCharacters(gr, font, text);

    for (int i = 0; i < text.Length; i++)
    {
        gr.DrawRectangle(Pens.Red,
            start_x + rects[i].Left, start_y + rects[i].Top,
            rects[i].Width, rects[i].Height);
    }
    gr.DrawString(text, font, Brushes.Blue, start_x, start_y);
}

Этот метод вызывает MeasureCharacters, чтобы получить прямоугольники, представляющие позиции символов. Если затем прокручивается через прямоугольники, каждый из которых смещается на сумму, переданную в этот метод. Наконец, метод рисует строку.

Последней частью программы является следующий обработчик событий Form_Load.

// Нарисуем текст.
private void Form1_Load(object sender, EventArgs e)
{
    // Создаем растровое изображение для хранения текста.
    Bitmap bm = new Bitmap(
        picText.ClientSize.Width,
        picText.ClientSize.Height);
    using (Graphics gr = Graphics.FromImage(bm))
    {
        gr.Clear(Color.White);

        // Не используйте TextRenderingHint.AntiAliasGridFit.
        gr.TextRenderingHint = TextRenderingHint.AntiAlias;

        // Создаем шрифт для использования.
        using (Font font = new Font("Times New Roman",
            16, FontStyle.Regular))
        {
            // Рисуем текст.
            DrawTextInBoxes(gr, font, 4, 4,
                "When in the course of human events it " +
                "becomes necessary for the quick brown " +
                "fox to jump over the lazy dog...");
        }
    }

    // Отображение результата.
    picText.Image = bm;
}

Этот метод создает Bitmap, очищает его и вызывает метод DrawTextInBoxes для рисования текста и прямоугольников вокруг символов.

В этом коде есть только одна тонкая вещь. Обычно я устанавливаю свойство TextRenderingHint объекта Graphics объекта AntiAliasGridFit для создания наилучшего вида текста. Однако, если вы это сделаете, метод DrawString может корректировать интервал между текстами для получения лучшего результата. К сожалению, метод MeasureCharacterRanges не учитывает эту настройку, поэтому прямоугольники не выравниваются правильно над их символами.

Источник: http://csharphelper.com/blog/2015/02/measure-character-positions-when-drawing-long-strings-in-c/

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Adblock
detector