Измерение размеров текста и строк

Все исходники /  Язык программирования C# /  OS Windows /  Desktop /  WPF программирование / Измерение размеров текста и строк

Измерение текста

Текстовые и символьные шрифты представляют из себя довольно сложные графические объекты, состоящие из множества элементов. Для вычисления габаритов и других измерительных величин текстовых строк удобно использовать класс низкоуровневого рисования FormattedText. Подобные измерения часто необходимы например, для выравнивания текста по центру, для вписывания всех строк в данный прямоугольник и т.п.

У строк текста достаточно большое количество измерительных параметров, поэтому удобнее их разделить на измерения по горизонтали и вертикали. Размеры в WPF представляют собой аппаратно-независимые единицы равные 1/96 дюйма. Это значит, что 1 дюйм длины может быть отображен различным количеством пикселей на разных по пиксельным характеристикам мониторах.

Измерения по горизонтали

Ширина строки текста, ведущие OverhangLeading и замыкающие OverhangTrailing выступы.

Класс FormattedText не имеет отдельного свойства значение, которого выражает актуальную ширину, занимаемую символами текста. Ширина текста вычисляется на основе нескольких свойств класса FormattedText - расстояния от ведущей линии выравнивания до первого пикселя символов текста, расстояния от замыкающей линии выравнивания до последнего пикселя текста и расстояния между линиями выравнивания. В случае с многострочным текстом ширина строки будет равна самой длинной строке. Все свойства измерения ширины доступны только для чтения.

FormattedText.OverhangLeading (ведущий выступ) – размещается перед символами строки, выражает расстояние от первого пикселя символов текста до точки выравнивания. В зависимости от вида и стиля шрифта OverhangLeading может иметь как положительные величины, так и отрицательные.

Программный код визуального отображения вертикальной линии ведущего выступа:
// DrawingContext dc
// x - координата позиционирования текста на поле контейнера.
dc.DrawLine(new Pen(Brushes.Red, 50), 
                new Point(x, y + 25), 
                new Point(x + formattedText.OverhangLeading, y + 25));

// Граница formattedText.OverhangLeading может смещаться 
// влево и вправо относительно координаты х.

FormattedText.OverhangTrailing (выступающий хвост)– замыкает строку текста, выражает расстояние от последнего пикселя рисования до замыкающей точки выравнивания строки. Также, как и ведущий выступ, OverhangTrailing имеет область значений от отрицательных и положительных величин.

Программный код визуального отображения вертикальной линии хвостового выступа строк текста:
// Положение formattedText.OverhangTrailing вычисляется от начала координат оси Х,
// без учёта ведущего выступа. 
dc.DrawLine(new Pen(new SolidColorBrush(Colors.Violet), 50),
    new Point(x + formattedText.Width, y + 25), 
    new Point(x + formattedText.Width - formattedText.OverhangTrailing, y + 25));

FormattedText.Width - расстояние между ведущей и замыкающей точками выравнивания. Данное свойство не выражает точную ширину текста и для получения актуальной ширины необходимо дополнительно учитывать расстояния от крайних пикселей до ведущей и замыкающей линий выступов.

Код наглядного отображения актуальной ширины текста в виде толстой линии синего цвета:
// где x,y начало координат размещения текста
dc.DrawLine(new Pen(new SolidColorBrush(Colors.Blue), 10), 
                new Point(x + fmtText.OverhangLeading, y), 
                new Point(x + fmtText.Width - fmtText.OverhangTrailing, y));

// new Point(x + fmtText.OverhangLeading, y) - крайняя левая точка ширины текста. 
// В случае отрицательного значения fmtText.OverhangLeading Х-координата левой точки
// будет находится левее начала координат размещения х.

// new Point(x + fmtText.Width - fmtText.OverhangTrailing, y) - крайняя правая точка ширины текста.
// При отрицательном значении fmtText.OverhangTrailing, ширина текста 
// увеличивается на это значение.

В случае отрицательных значений свойств OverhangLeading и OverhangTrailing, расстояния до крайних пикселей выходят за пределы величины FormattedText.Width, при положительных значениях они находятся внутри зоны FormattedText.Width. Значения свойств OverhangLeading, OverhangTrailing изменяются не симметрично и могут иметь самые различные величины каждое в отдельности, без влияния друг на друга.

Измерения по вертикали

Высота строки, extent, высота текста всех строк

Вертикальных измерительных величин гораздо больше чем горизонтальных. Сюда входит высота одной строки, высота всех строк текста, высота только пикселей текста, нижнее выравнивающее пространство для вычисления положения самых нижних пикселей текста.

FormattedText.Baseline - расстояние от верхнего края первой строки до нижней линии текста образуемой большинством символов текущего шрифта. Базовая линия проходит по нижнему краю текста без учета нижних выносных элементов некоторых символов шрифтов. Базовая линия наглядно представляется если в строке текста только прописные буквы.

Код рисования базовой линии строки текста:
dc.DrawLine(new Pen(Brushes.Yellow, 2), 
    new Point(x, y + formattedText.Baseline + 1), 
    new Point(x + formattedText.Width, y + formattedText.Baseline + 1));

FormattedText.Extent (протяженность) – фактическая высота символов всех строк текста, актуальное пространство занимаемое только пикселями буквами, не включает межстрочное пространство первой строки и учитывает выравнивающее пространство от низа последней строки до самого нижнего пикселя текста. Расстояние от самого верхнего края пикселей текста до нижнего края пикселей текста последней строки. Если в тексте есть символы с нижними выносными элементами , их размер включается в FormattedText.Extent. Если в тексте только строчные буквы, FormattedText.Extent будет отражать высоту только строчных символов, без учета возможных прописных. Значения FormattedText.Extent можно использовать для точного выравнивания текста в собственных визуальных элементах.

// Находим нижний пиксель
// низ последней строки - fmtText.Height
// расстояние от низа последней строки до нижнего пикселя - fmtText.OverhangAfter
// Поскольку значение fmtText.OverhangAfter является положительным, если самый нижний
// рисуемый пиксел находится под нижней границей строки и
// является отрицательным, если он находится в пределах последней строки, то
// чтобы найти границу самого нижнего рисуемого пикселя, необходимо:
double maxbottompixel = y + formattedText.Height + formattedText.OverhangAfter;

// затем от самого нижнего пикселя получаем расстояние 
// до самого верхнего пикселя при помощи значения fmtText.Extent:
double maxtoppixel = maxbottompixel - formattedText.Extent;

// В итоге получаем высоту только пикселей всех строк текста.
Rect rectExtent = new(
    x + formattedText.Width / 2 - 25, 
    maxtoppixel, 
    50, 
    maxbottompixel - maxtoppixel
);
dc.DrawRectangle(Brushes.Gray), null, rectExtent);

FormattedText.Height – общая высота всех строк, от верхней границы первой строки до нижней границы последней строки. Высота включает верхнее межстрочное пространство первой строки и может включать выравнивающее пространство до самых нижних пикселей последней строки. Значение высоты определяется для всего текущего вида шрифта. Независимо от того есть ли свисающие нижние элементы у символов в тексте или нет, их размер учитывается свойством FormattedText.Height. Для однострочного текста описываемая величина равна значению свойства FormattedText.LineHeight, что даёт возможность вычислить дефолтную высоту строки. Для вычисления актуальной габаритной высоты всех строк текста необходимо учитывать значение свойства FormattedText.OverhangAfter.

Программный код визуального отображения ширины и высоты строк:
// x, y - начальные координаты расположения строк текста
Rect rectWidthText = new(
    new Point(x, y),
    new Point(x + formattedText.Width, y + formattedText.Height)
    );

dc.DrawRectangle(Brushes.Chocolate, null, rectWidthText);

FormattedText.LineHeight - высота одной строки, единственное свойство которое может изменять программист. По умолчанию автоматически вычисляется системой на основе указываемых в конструкторе FormattedText() вида и размеров шрифта. Вычисленное системное значение нельзя получить из этого свойства: величина по умолчанию всегда равна нулю. Чтобы всё же получить значение высоты строки по умолчанию нужно дополнительно создать ещё один экземпляр класса FormattedText с идентичными параметрами, но в качестве текста использовать всего один символ. Такая настройка объекта гарантирует создание текста из одной строки и посредством свойства FormattedText.Height точно определяется высота созданной строки. Зная дефолтную высоту строки легко получить количество строк в тексте.

// Экземпляр для измерений количества строк, высоты строки и др.
FormattedText addFormattedText = new(
    "A",
    System.Globalization.CultureInfo.CurrentCulture,
    FlowDirection.LeftToRight,
    typeface,
    _fontSize,
    Brushes.Black,
    pixelsPerDip
    );

// Если высота строки основного экземпляра FormattedText значение по умолчанию (т.е. ноль),
// для вычислений используем свойство дополнительного объекта FormattedText.
double lineheight = formattedText.LineHeight == 0 ? 
                    addFormattedText.Height : formattedText.LineHeight;

int numlines = (int)formattedText.Height / (int)lineheight;

FormattedText.OverhangAfter – нижнее выравнивающее пространство для строк текста. Выражает расстояние от низа последней строки до самого нижнего рисуемого пикселя символов. Поскольку самый нижний пиксель текста может быть ниже или выше нижней линии последней строки, поэтому область значений FormattedText.OverhangAfter от отрицательных до положительных чисел. Если значение FormattedText.OverhangAfter положительное - пиксели текста находятся ниже последней строки, если отрицательное - все пиксели символов находятся в пределах строки. При вычислениях позиции текста по высоте значение этого свойства необходимо учитывать.

Программный код визуального отображения расстояния от низа последней строки до самых нижних пикселей текста:
// x,y начальные координаты позиции текста при рисовании 
dc.DrawLine(new Pen(Brushes.Gray, 200),
    new Point(x + 100, y + formattedText.Height), 
    new Point(x + 100, y + formattedText.Height + formattedText.OverhangAfter));

Программа наглядного отображения измерений текста

Ниже прикреплена программа с исходным кодом для исследования свойств класса FormattedText использующихся для различных измерений текста. Код программы написан в MS Visual Studio 2019, платформа .NET5.

Скачать исходник