Рисование текста из разных шрифтов в одной строке

Разные стили типов в семействе шрифтов могут иметь разную ширину. Например, полужирные и курсивные стили семейства всегда шире римского стиля для указанного размера точек. При отображении или печати нескольких стилей типов в одной строке необходимо отслеживать ширину строки, чтобы избежать отображения или печати символов друг над другом.

Для получения ширины (или экстента) текста в текущем шрифте можно использовать две функции. Функция GetTabbedTextExtent вычисляет ширину и высоту символьной строки. Если строка содержит один или несколько символов табуляции, ширина строки зависит от указанного массива позиций табуляции. Функция GetTextExtentPoint32 вычисляет ширину и высоту строки текста.

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

Одним из способов написания строки текста, содержащей несколько шрифтов, является использование функции GetTextExtentPoint32 после каждого вызова TextOut и добавление длины в текущую позицию. В следующем примере строка "Это пример строки" записывается полужирным шрифтом для "This is a", переключается на курсив для "sample", а затем возвращается полужирный шрифт для "string". После печати всех строк восстанавливается системные символы по умолчанию.

int XIncrement; 
int YStart; 
TEXTMETRIC tm; 
HFONT hfntDefault, hfntItalic, hfntBold; 
SIZE sz; 
LPSTR lpszString1 = "This is a "; 
LPSTR lpszString2 = "sample "; 
LPSTR lpszString3 = "string."; 
HRESULT hr;
size_t * pcch;
 
// Create a bold and an italic logical font.  
 
hfntItalic = MyCreateFont(); 
hfntBold = MyCreateFont(); 
 
 
// Select the bold font and draw the first string  
// beginning at the specified point (XIncrement, YStart).  
 
XIncrement = 10; 
YStart = 50; 
hfntDefault = SelectObject(hdc, hfntBold); 
hr = StringCchLength(lpszString1, 11, pcch);
        if (FAILED(hr))
        {
        // TODO: write error handler 
        }
TextOut(hdc, XIncrement, YStart, lpszString1, *pcch); 
 
// Compute the length of the first string and add  
// this value to the x-increment that is used for the  
// text-output operation.  

hr = StringCchLength(lpszString1, 11, pcch);
        if (FAILED(hr))
        {
        // TODO: write error handler 
        } 
GetTextExtentPoint32(hdc, lpszString1, *pcch, &sz); 
XIncrement += sz.cx; 
 
// Retrieve the overhang value from the TEXTMETRIC  
// structure and subtract it from the x-increment.  
// (This is only necessary for non-TrueType raster  
// fonts.)  
 
GetTextMetrics(hdc, &tm); 
XIncrement -= tm.tmOverhang; 
 
// Select an italic font and draw the second string  
// beginning at the point (XIncrement, YStart).  
 
hfntBold = SelectObject(hdc, hfntItalic); 
GetTextMetrics(hdc, &tm); 
XIncrement -= tm.tmOverhang;
hr = StringCchLength(lpszString2, 8, pcch);
        if (FAILED(hr))
        {
        // TODO: write error handler 
        } 
TextOut(hdc, XIncrement, YStart, lpszString2, *pcch); 
 
// Compute the length of the second string and add  
// this value to the x-increment that is used for the  
// text-output operation.  

hr = StringCchLength(lpszString2, 8, pcch);
        if (FAILED(hr))
        {
        // TODO: write error handler 
        }  
GetTextExtentPoint32(hdc, lpszString2, *pcch, &sz); 
XIncrement += sz.cx; 
 
// Reselect the bold font and draw the third string  
// beginning at the point (XIncrement, YStart).  
 
SelectObject(hdc, hfntBold);
hr = StringCchLength(lpszString3, 8, pcch);
        if (FAILED(hr))
        {
        // TODO: write error handler 
        }  
TextOut(hdc, XIncrement - tm.tmOverhang, YStart, lpszString3, 
            *pcch); 
 
// Reselect the original font.  
 
SelectObject(hdc, hfntDefault); 
 
// Delete the bold and italic fonts.  
 
DeleteObject(hfntItalic); 
DeleteObject(hfntBold); 

В этом примере функция GetTextExtentPoint32 инициализирует члены структуры SIZE длиной и высотой указанной строки. Функция GetTextMetrics извлекает навес для текущего шрифта. Так как навес равен нулю, если шрифт является шрифтом TrueType, значение навеса не изменяет размещение строк. Однако для растровых шрифтов важно использовать значение свеса.

Навес вычитается из полужирной строки один раз, чтобы привести последующие символы ближе к концу строки, если шрифт является растровым. Так как навес влияет как на начало, так и на конец курсивной строки в растровом шрифте, глифы начинаются справа от указанного расположения и заканчиваются слева от конечной точки последней символьной ячейки. (Функция GetTextExtentPoint32 извлекает экстент символьных ячеек, а не экстент глифов.) Чтобы учесть свес в строке курсивного растра, пример вычитает свес перед размещением строки и вычитает его снова, прежде чем помещать последующие символы.

Функция SetTextJustification добавляет дополнительное пространство к символам разрыва в строке текста. Вы можете использовать функцию GetTextExtentPoint , чтобы определить экстент строки, а затем вычесть этот экстент из общего объема пространства, занимаемого строкой, и использовать функцию SetTextJustification для распределения дополнительного пространства между символами разрыва в строке. Функция SetTextCharacterExtra добавляет дополнительное пространство для каждой символьной ячейки выбранного шрифта, включая символ разрыва. (Функцию GetTextCharacterExtra можно использовать для определения текущего объема дополнительного пространства, добавляемого в символьные ячейки; значение по умолчанию равно нулю.)

Вы можете размещать символы с большей точностью с помощью функции GetCharWidth32 или GetCharABCWidths для получения ширины отдельных символов в шрифте. Функция GetCharABCWidths более точная, чем функция GetCharWidth32 , но ее можно использовать только со шрифтами TrueType.

Междустрочный интервал ABC также позволяет приложению выполнять очень точное выравнивание текста. Например, если приложение справа выравнивает шрифт растрового романа без использования интервала ABC, ширина заранее вычисляется как ширина символа. Это означает, что пробел справа от глифа на растровом рисунке выравнивается, а не сам глиф. Благодаря использованию ширины ABC приложения обладают большей гибкостью в размещении и удалении пробелов при выравнивании текста, так как они содержат сведения, позволяющие им точно управлять межсимвейным интервалом.