本节首先介绍Windows提供的几个文本输出函数、一些会影响文本输出的设备环境属性和如何使用备用字体。
本节必须掌握的知识点:
文本输出函数
文本的设备环境属性
使用库存字体
16.1.1文本输出函数
■常见的文本输出函数
●TextOut 函数
函数调用:
TextOut(hdc,xStart,yStart,pString,iCount)
TextOut 函数用于在设备上下文(DC)中绘制文本的函数。它可以在指定的位置上绘制单行文本。
BOOL TextOut(
HDC hdc, // 设备上下文句柄
int x, // 起始位置的横坐标
int y, // 起始位置的纵坐标
LPCTSTR lpString, // 要绘制的文本字符串
int c // 要绘制的字符数,-1 表示绘制整个以 null 终止的字符串
);
返回值:如果函数成功绘制文本,则返回非零值;如果失败,则返回零。
xStart和yStart使用的是逻辑坐标,TextOut并不以NULL来做字符串的结束,需指定字符的个数iCount的值。
使用 TextOut 函数绘制文本的示例代码如下:
HDC hdc; // 假设已有设备上下文句柄
// 设置文本属性(可选)
SetTextColor(hdc, RGB(255, 0, 0)); // 设置文本颜色
SetBkMode(hdc, TRANSPARENT); // 设置背景透明
int x = 100; // 起始位置的横坐标
int y = 100; // 起始位置的纵坐标
LPCTSTR text = _T("Hello, World!"); // 要绘制的文本
TextOut(hdc, x, y, text, -1); // 绘制文本
在上述示例中,我们首先设置了文本的属性,例如颜色和背景透明度。然后,我们指定了文本绘制的位置和要绘制的文本内容,并调用 TextOut 函数进行绘制。
●SetTextAlign函数
SetTextAlign 函数用于设置文本对齐方式的函数。它可以控制文本在绘制时相对于指定点的对齐方式。
以下是 SetTextAlign 函数的语法:
UINT SetTextAlign(
HDC hdc, //设备上下文(DC)的句柄
UINT fMode //指定文本对齐方式的标志
);
返回值:
函数调用成功时,返回以前的文本对齐方式的标志。
函数调用失败时,返回 GDI_ERROR。
SetTextAlign函数会改变起始位置xStart和yStart的含义。
SetTextAlign | 坐标值的含义 |
TA_LEFT | xStart:第一个字符的左侧坐标 |
TA_RIGHT | xStart:最后一个字符的右侧坐标 |
TA_CENTER | xStart:字符串正中间点的坐标 |
TA_TOP | yStart:所有字符的最高点,即所有字符在yStart的位置之下 |
TA_BOTTOM | yStart:所有字符都在yStart位置之上 |
TA_BASELINE | yStart:表示字符串在基线位置,即小写字母q或y下伸部分在基线之下。 |
TA_UPDATECP | 1、忽略xStart和yStart,而是由如MoveToEx或LineTo等一些会改变当前坐标的函数指定的当前值。CP==Current Position? 2、此时TextOut函数也会改变坐标当前值,对于TA_LEFT,新坐标值在字符串结尾位置;对于TA_RIGHT,当前坐标值在新字符串开始位置;对于TA_CENTER,则仍在中间位置,不会改变。 |
●TabbedTextOut函数
TabbedTextOut 函数用于在设备上下文(DC)中绘制具有制表位对齐的文本的函数。它允许在文本中使用制表符并指定每个制表位的宽度,从而实现对齐的效果。
以下是 TabbedTextOut 函数的语法:
LONG TabbedTextOut(
HDC hdc, // 设备上下文句柄
int x, // 起始位置的横坐标
int y, // 起始位置的纵坐标
LPCTSTR lpString, // 要绘制的文本字符串
int cchString, // 要绘制的字符数,-1 表示绘制整个以 null 终止的字符串
int nTabPositions, // 制表位位置数组的元素数目
const INT *lpnTabStopPositions, // 指向整型数组的指针,包含制表位的位置信息
int nTabOrigin // 制表位的起始位置(原点)
);
返回值:
如果函数成功绘制文本,则返回绘制文本的宽度(以像素为单位)。
如果函数失败,则返回 -1。
使用 TabbedTextOut 函数绘制具有制表位对齐的文本的示例代码如下:
HDC hdc; // 假设已有设备上下文句柄
int x = 100; // 起始位置的横坐标
int y = 100; // 起始位置的纵坐标
LPCTSTR text = _T("Item1\tItem2\tItem3"); // 要绘制的文本,包含制表符
int nTabPositions = 3; // 制表位位置数组的元素数目
// 制表位的位置信息,相对于 nTabOrigin 参数指定的原点
const INT tabPositions[] = {150, 250, 350};
int nTabOrigin = x; // 制表位的起始位置(原点)
// 绘制具有制表位对齐的文本
TabbedTextOut(hdc, x, y, text, -1, nTabPositions, tabPositions, nTabOrigin);
在上述示例中,我们指定了文本绘制的起始位置和要绘制的文本内容,其中文本中包含了制表符。我们还定义了制表位的位置数组和制表位的起始位置。然后,我们调用 TabbedTextOut 函数进行绘制。
【注意】TabbedTextOut 函数会根据制表位的位置信息对文本进行对齐。制表位的位置信息以像素为单位,并相对于制表位的起始位置进行指定。
函数调用:
TabbedTextOut(hdc,xStart,yStart,pString,iCount,iNumTabs,piTabStops,xTabOrigin);
1.当遇到字符串包含制表符(‘\t’或0x09)时,会被转成空白位置和个数由xTabOrign和piTabStops数组里元素共同决定。
2.iNumTabs指定制表符位置数组piTabStops中元素的个数。
3.piTabStops :TabStop位置的数组(逻辑单位)。表示每个制表符开始的位置,元素必须按升序存储。注意,元素表示的是个相对位置,即相对于xTagOrigin的位置偏移,比如元素元素分别为{40,80,120,160,200}表示第1个tab从xTagOrgin+40像素的地方开始输出,第2个从xTagOrgin+80,第3个从xTagOrgin+120开始输出,依此类推…
4.如果iNumTabs值为0,且piTabStops值为NULL则制表符将会按平均字符宽度的8倍来扩展。
5.iCount为-1时,遇\0自动结束。
总结
先依次输出字符串中的每个字符。遇到第n个\t时,从piTabStops数组依次比较,进而找到这个tab的开始位置(找到的元素索引为i。注意,i不一定等于n),就可以确定tab的位置为xTabOrigin+piTabStops[i]。如果在数组中找不到,就按默认的字符宽度的8倍,去计算出这个tab的位置。若这个tab位置前的字符不够时,则用空格补全。
【实例分析1】——xTabStop==0时(本例用系统等宽字符,每个字符的平均宽度为8像素)
TCHAR szText[] = TEXT("abcdfg\thi\tjklmnopqrstuvwxyz\th");//3个\t
TabbedTextOut(hdc,0, 0, szText, -1,0, NULL,0);//默认Tab位置是字符宽度的8倍。
1.第1个Tab位为刻度8的位置(1*字符宽度的8倍),因刻度8之前只有6个字符,所以补充2个空格字符,然后输出hi两个字符,然后遇第2个Tab。
2.第2个Tab为16(2*字符宽度的8倍)的位置(即刻度16)处开始,由于字符数不够,得再补充6个空格字符。然后输出jklmnopqrstuvwxyz,此时再遇第3个字符。
3.第3个Tab开始的位置应为8的倍数,即8、16、24、32或40等,但因8、16、24、32处要么被字符占用,要么被以前的两个Tab占用,所以第3个Tab从40开始,然后输出h字符。
【实例分析2】——xTabStop==10*8时,即Tab位的原点在图中刻度10的位置。
TCHAR szText[] = TEXT("abcdfg\thi\tjklmnopqrstuvwxyz\th");//3个\t
TabbedTextOut(hdc,0, 0, szText, -1,0, NULL,10*8);//与上例唯一的不同,xTabStop
1.第1个Tab位为10+8的位置(其中10为xTabStop,8与例一样),即上图中刻度的18开始,因刻度18之前只有6个字符,所以补充12个空格字符,然后输出hi两个字符,然后遇第2个Tab。
2.第2个Tab为(10+16)的位置(其中10为xTabStop)处开始,由于字符数不够得再补充6个空格字符。然后输出jklmnopqrstuvwxyz,此时再遇第3个字符。
3.第3个Tab开始的位置应为10+(8的倍数),即18、26、34、42或50等,但因8、18、26、34、42处要么被字符占用,要么被以前的两个Tab占用,所以第3个Tab从50开始,然后输出h字符。
【实例分析3】——讨论xTabStop==0时
TCHAR szText[] = TEXT("abcdfg\thi\tjklmnopqrstuvwxyz\th");//3个\t
//每个字符宽度是8,元素的含义表示Tab位在第5、6、15、30、45刻度处
int iTabStops[] = { 5 * 8, 6 * 8, 15 * 8, 30 * 8,45 * 8 };
TabbedTextOut(hdc,0, 0, szText, -1,iLen, iTabStops,0);
1.第1个Tab位为15的位置(iTabStops的第3个元素),即上图中刻度的15开始,
因为第1个元素5,但其位置己被字符f占用,第2个元素6,其位置己被g占用,所以第1个Tab从第3个元素,即刻度15的位置开始,因前面只有6个字符,得补9个空格。
2.第2个Tab为刻度30的位置(即第4个元素)处开始,由于字符数不够得再补充13个空格字符。然后输出jklmnopqrstuvwxyz,此时再遇第3个字符。
3.第3个Tab本应从刻度45开始,但被占用,而此时数组中的元素己经用尽,所以按默认的8的倍数,即刻度48的位置开始,所以补1个0,然后输出h。
【实例分析4】——讨论xTabStop==10*8时,即xTabStop从10的刻度开始
TCHAR szText[] = TEXT("abcdfg\thi\tjklmnopqrstuvwxyz\th");//3个\t
//每个字符宽度是8
//元素的含义表示Tab位为xTabStop加5、6、15、30、45这些偏移开始
int iTabStops[] = { 5 * 8, 6 * 8, 15 * 8, 30 * 8, 45 * 8 };
TabbedTextOut(hdc,0, 0, szText, -1,iLen, iTabStops,10*8);
1.第1个Tab位为10+5的位置(其中10为xTabStop,5为iTabStops的第1个元素),即上图中刻度的15开始,因a到g只有6个字符,得补9个空格。然后输出hi
2.第2个Tab为10+15的位置(即第3个元素),而不是10+6(因为16位置被h占用),由于字符数不够得再补充8个空格字符。然后输出jklmnopqrstuvwxyz,此时再遇第3个字符。
3.第3个Tab本应从刻度10+45开始(因为10+30被占用),所以补13个0,然后输出h。
●ExtTextOut函数
ExtTextOut 函数用于在设备上下文(DC)中绘制文本的函数。它提供了更灵活的文本绘制选项,包括对齐方式、字符间距、字体和背景等的控制。
以下是 ExtTextOut 函数的语法:
BOOL ExtTextOut(
HDC hdc, // 设备上下文句柄
int x, // 起始位置的横坐标
int y, // 起始位置的纵坐标
UINT fuOptions,// 绘制文本的选项
const RECT *lprc, // 文本绘制的矩形区域
LPCTSTR lpString, // 要绘制的文本字符串
UINT cbCount, // 要绘制的字符数,-1 表示绘制整个以 null 终止的字符串
const INT *lpDx // 字符间距数组的指针
);
返回值:
如果函数成功绘制文本,则返回非零值。
如果函数失败,则返回零。
函数调用:
ExtTextOut(hdc,xStart,yStart,iOptions,&rect,pString,iCount,pxDistance);
1.rect参数:当iOption==ETO_CLIPPED时,rect为剪切框;当iOption==ETO_OPAQUE时,rect为文本的背景框。这两个选项可以同时使用,也可以一个都不用。
2.pxDistance是指向一个整型数组,指定了字符串中相邻字符间的像素间距。如果为NULL,则为默认字符间距(可自由定制紧凑或宽松的字符间距)。
【注意】数组元素的个数要大于等于字符间隔的个数,即数组元素个数≥字符个数-1。
●DrawText函数
DrawText 函数用于在设备上下文(DC)中绘制格式化文本的函数。它提供了更高级的文本绘制功能,可以自动换行、对齐文本、计算文本绘制区域等。
以下是 DrawText 函数的语法:
int DrawText(
HDC hdc, // 设备上下文句柄
LPCTSTR lpchText, // 要绘制的文本字符串
int nCount, // 要绘制的字符数,-1 表示绘制整个以 null 终止的字符串
LPRECT lprc, // 文本绘制的矩形区域
UINT uFormat // 绘制文本的格式选项
);
返回值:
如果函数成功绘制文本,则返回绘制的文本内容的高度(以逻辑单位为准)。
如果函数失败,则返回零。
使用 DrawText 函数绘制文本的示例代码如下:
HDC hdc; // 假设已有设备上下文句柄
LPCTSTR text = _T("Hello, World!"); // 要绘制的文本
RECT rect = {100, 100, 300, 200}; // 绘制文本的矩形区域
// 绘制文本
DrawText(hdc, text, -1, &rect, DT_CENTER | DT_VCENTER | DT_WORDBREAK);
在上述示例中,我们指定了要绘制的文本字符串和一个矩形区域,用于限制文本的绘制范围。然后,我们调用 DrawText 函数进行文本绘制,并通过 uFormat 参数指定了对齐和格式选项,如居中对齐、自动换行等。
函数调用:
DrawText(hdc,pString,iCount,&rect,iFormat);
1.iCount为-1时,自动计算字符串长度。
2.iFormat
iForamt参数 | 含义 |
0 | 将以回车\r(或0x0D)及换行\n(或0x0A)分隔成多行文本。 |
DT_LEFT、 DT_RIGHT、 DT_CENTER DT_VCENTER等 | 文本对齐标志 |
DT_SINGLELINE | 单行文本,此时回车换行符无效。 |
DT_WORDBREAK | 在临近边框的两个单词中换行 |
DT_EXTERNALLEADING | 多行文本时,字符高度不含字体的外部间距,如果希望行间距包括这个外部间距,就要使用该标志。 |
DT_EXPANDTABS | 默认两个制表位之间为八个字符宽度 |
●DrawTextEx函数
DrawTextEx 函数提供了比 DrawText 函数更高级的文本绘制功能,包括支持自定义字体、字间距、文本效果等。
以下是 DrawTextEx 函数的语法:
int DrawTextEx(
HDC hdc, // 设备上下文句柄
LPTSTR lpchText, // 要绘制的文本字符串
int cchText, // 要绘制的字符数,-1 表示绘制整个以 null 终止的字符串
LPRECT lprc, // 文本绘制的矩形区域
UINT dwDTFormat, // 绘制文本的格式选项
LPDRAWTEXTPARAMS lpDTParams // 绘制文本的参数
);
返回值:
如果函数成功绘制文本,则返回绘制的文本内容的高度(以逻辑单位为准)。
如果函数失败,则返回零。
DRAWTEXTPARAMS 结构的定义如下:
typedef struct tagDRAWTEXTPARAMS {
UINT cbSize; // 结构的大小,用于指定结构的版本
int iTabLength; // 定义制表符的宽度,以逻辑单位为准
int iLeftMargin; // 定义文本的左边距,以逻辑单位为准
int iRightMargin; // 定义文本的右边距,以逻辑单位为准
UINT uiLengthDrawn; // 返回实际绘制的文本字符数
} DRAWTEXTPARAMS, *LPDRAWTEXTPARAMS;
使用 DrawTextEx 函数绘制文本的示例代码如下:
HDC hdc; // 假设已有设备上下文句柄
LPTSTR text = _T("Hello, World!"); // 要绘制的文本
RECT rect = {100, 100, 300, 200}; // 绘制文本的矩形区域
DRAWTEXTPARAMS dtParams;
dtParams.cbSize = sizeof(DRAWTEXTPARAMS);
dtParams.iTabLength = 4;
dtParams.iLeftMargin = 10;
dtParams.iRightMargin = 10;
// 绘制文本
DrawTextEx(hdc, text, -1, &rect, DT_CENTER | DT_VCENTER | DT_WORDBREAK, &dtParams);
16.1.2文本的设备环境属性
除了前面讨论过的SetTextAlign函数,还有一些其他的设备环境属性也会影响文本的 显示。使用默认的设备环境时,文字颜色设定为黑色。
■设置文字颜色
●SetTextColor(hdc,rgbColor)或GetTextColor(hdc);//设置或获取文字颜色
■设置背景模式
OPAQUE时表示不透明;TRANSPARENT(透明)
●SetBkMode(hdc,iMode);
当为TRANSPARENT时,会用背景色给虚线或点划线中间的空白部分着色。
■设置背景颜色
SetBkColor(hdc,rgbColor)
举例
1.调用当前系统颜色设定文本颜色或背景
SetTextColor(hdc,GetSysColor(COLOR_WINDOWTEXT));
SetBkColor(hdc,GetSysColor(COLOR_WINDOW));
2.响应WM_SYSCOLORCHANGE消息:——随系统颜色的更改自动更改文本和背景色。
Case WM_SYSCOLORCHANGE:InvalidateRect(hwnd,NULL,TRUE);break;
■设置字符间距
SetTextCharacterExtra(hdc,iExtra);——字符间距,默认为0,否则为iExtra
1.iExtra为负数时,会被自动取绝对值,所以不能利用该函数让字符紧缩一点。
2.iExtra是个逻辑单位。可以通过GetTextCharacterExtra取得当前字符间距。
16.1.3使用库存字体
调用 TextOut,TabbedTextOut,ExtTextOut,DrawText 或者 DrawTextEx 来输出文本时, Windows会使用在设备环境中的选定的字体。文字的字样(typeface)和字号(size)定义了一 种特定的字体(font)。使用不同字体显示文本的最简单方法是利用Windows提供的库存字体。
■获取特定库存字体
hFont= GetStockObject(iFont);//iFont为系统预定义标识符
这里iFont可以在Window预定义的一些标识符中取值。然后可以使用以下函数指定某个设备环境来使用这个字体。
默认的设备环境中设定的字体被称为系统字体,你可以在调用GetStockObject函数时使用SYSTEM_FONT参数来获得系统字体。这是一个包含ANSI字符集的比例字体(proportional font)。在调用 GetStockObject 函数时使用 SYSTEM_FIXED_FONT可以获得一个指向等宽字体的控点,该等宽字体和Windows 3.0之前版本中的系统字体兼容。如果希望所有字符的宽度都相等,直接调用这个函数就可以了。
库存字体 OEM_FIXED_FONT,又称为终端字体(terminal font),是 Windows 在 MS-DOS 命令提示窗口中使用的字体。它含有的字符集是同IBM PC扩展字符集相兼容的。Windows 的窗口标题列、菜单和对话框的文本中使用的是DEFAULT_GUI_FONT库存字体。
虽然GetStockObject函数提供了使用不同字体的最简便的方式,但它不能充分利用 Windows提供的字体。接下来,我们来看如何指定文本的字样和字号。
■选入设备环境
SelectObject(hdc,hFont);
字体作为一个GDI对象存在的,获取特定字体之后,需要调用SelectObject函数将其选入到设备环境中。或者可以把这两步合并,使用以下一条语句来实现:
SelectObject (hdc, GetStockObject (iFont));
■获取当前字体信息
利用GetTextMetrics函数获取当前字体信息。在设备环境中指定新字体时,必须调用GetTextMetrixs函数来计算该字体中字符高度和字符平均宽度。如果你使用的是一个比例字体,注意字符平均宽度只是一个平均值,某些字符会更宽些,某些则会窄些。本章的后面会介绍如何确定一个由不同宽度字符组成的字符串总宽度。