17.1 简单文本输出
17.1.1 文本输出函数
(1)TextOut(hdc,xStart,yStart,pString,iCount)
①xStart和yStart使用的是逻辑坐标,TextOut并不以NULL来做字符串的结束,需指定字符的个数iCount的值
②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,则仍在中间位置,不会改变。 |
(2)TabbedTextOut(hdc,xStart,yStart,pString,iCount,iNumTabs,piTabStops,xTabOrigin);
①当遇到字符串包含制表符(‘\t’或0x09)时,会被转成空白位置和个数由xTabOrign和piTabStops数组里元素共同决定。
②iNumTabs指定piTabStops中元素的个数
③piTabStops :TabStop位置的数组(逻辑单位),。表示每个制表符开始的位置,元素必须按升序存储。注意,元素表示的是个相对位置,即相对于xTagOrigin的位置偏移,比如元素元素分别为{40,80,120,160,200}表示第1个tab从xTagOrgin+40像素的地方开始输出,第2个从xTagOrgin+80,第3个从xTagOrgin+120开始输出,依此类推…
④如果iNumTabs值为0,且piTabStops值为NULL则制表符将会按平均字符宽度的8倍来扩展。
⑤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个Tab位为刻度8的位置(1*字符宽度的8倍),因刻度8之前只有6个字符,所以补充2个空格字符,然后输出hi两个字符,然后遇第2个Tab。
②第2个Tab为16(2*字符宽度的8倍)的位置(即刻度16)处开始,由于字符数不够,得再补充6个空格字符。然后输出jklmnopqrstuvwxyz,此时再遇第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个Tab位为10+8的位置(其中10为xTabStop,8与例一样),即上图中刻度的18开始,因刻度18之前只有6个字符,所以补充12个空格字符,然后输出hi两个字符,然后遇第2个Tab。
②第2个Tab为(10+16)的位置(其中10为xTabStop)处开始,由于字符数不够得再补充6个空格字符。然后输出jklmnopqrstuvwxyz,此时再遇第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
int iTabStops[] = { 5 * 8, 6 * 8, 15 * 8, 30 * 8,45 * 8 };//每个字符宽度是8
//元素的含义表示Tab位在第5、6、15、30、45刻度处
TabbedTextOut(hdc,0, 0, szText, -1,iLen, iTabStops,0);
①第1个Tab位为15的位置(iTabStops的第3个元素),即上图中刻度的15开始,
因为第1个元素5,但其位置己被字符f占用,第2个元素6,其位置己被g占用,所以第1个Tab从第3个元素,即刻度15的位置开始,因前面只有6个字符,得补9个空格。
②第2个Tab为刻度30的位置(即第4个元素)处开始,由于字符数不够得再补充13个空格字符。然后输出jklmnopqrstuvwxyz,此时再遇第3个字符。
③第3个Tab本应从刻度45开始,但被占用,而此时数组中的元素己经用尽,所以按默认的8的倍数,即刻度48的位置开始,所以补1个0,然后输出h。
【实例分析4】——讨论xTabStop==10*8时,即xTabStop从10的刻度开始
TCHAR szText[] = TEXT("abcdfg\thi\tjklmnopqrstuvwxyz\th");//3个\t
int iTabStops[] = { 5 * 8, 6 * 8, 15 * 8, 30 * 8, 45 * 8 };//每个字符宽度是8
//元素的含义表示Tab位为xTabStop加5、6、15、30、45这些偏移开始
TabbedTextOut(hdc,0, 0, szText, -1,iLen, iTabStops,10*8);
①第1个Tab位为10+5的位置(其中10为xTabStop,5为iTabStops的第1个元素),即上图中刻度的15开始,因a到g只有6个字符,得补9个空格。然后输出hi
②第2个Tab为10+15的位置(即第3个元素),而不是10+6(因为16位置被h占用),由于字符数不够得再补充8个空格字符。然后输出jklmnopqrstuvwxyz,此时再遇第3个字符。
③第3个Tab本应从刻度10+45开始(因为10+30被占用),所以补13个0,然后输出h。
(3)ExtTextOut(hdc,xStart,yStart,iOptions,&rect,pString,iCount,pxDistance);
①rect参数:当iOption==ETO_CLIPPED时,rect为剪切框;当iOption==ETO_OPAQUE时,rect为文本的背景框。这两个选项可以同时使用,也可以一个都不用。
②pxDistance是指向一个整型数组,指定了字符串中相邻字符间的像素间距。如果为NULL,则为默认字符间距。→可自由定制紧凑或宽松的字符间距。(注意:数组元素的个数要大于等于字符间隔的个数,即数组元素个数≥字符个数-1)。
(4)DrawText(hdc,pString,iCount,&rect,iFormat);
①iCount为-1时,自动计算字符串长度。
②iFormat
iForamt参数 |
含义 |
0 |
将以回车\r(或0x0D)及换行\n(或0x0A)分隔成多行文本。 |
DT_LEFT、 DT_RIGHT、 DT_CENTER DT_VCENTER等 |
文本对齐标志 |
DT_SINGLELINE |
单行文本,此时回车换行符无效。 |
DT_WORDBREAK |
在临近边框的两个单词中换行 |
DT_EXTERNALLEADING |
多行文本时,字符高度不含字体的外部间距,如果希望行间距包括这个外部间距,就要使用该标志。 |
DT_EXPANDTABS |
默认两个制表位之间为八个字符宽度 |
17.1.2 文本的设备环境属性
(1)SetTextColor(hdc,rgbColor)或GetTextColor(hdc);//设置或获取文字颜色
(2)SetBkMode(hdc,iMode);设置背景模式:OPAQUE时表示不透明;TRANSPARENT(透明)
★当为TRANSPARENT时,会用背景色给虚线或点划线中间的空白部分着色。
(3)SetBkColor(hdc,rgbColor)//设置背景颜色
★调用当前系统颜色设定文本颜色或背景
SetTextColor(hdc,GetSysColor(COLOR_WINDOWTEXT));
SetBkColor(hdc,GetSysColor(COLOR_WINDOW));
★响应WM_SYSCOLORCHANGE消息:——随系统颜色的更改自动更改文本和背景色。
Case WM_SYSCOLORCHANGE:InvalidateRect(hwnd,NULL,TRUE);break;
(4)SetTextCharacterExtra(hdc,iExtra);——字符间距,默认为0,否则为iExtra
①iExtra为负数时,会被自动取绝对值,所以不能利用该函数让字符紧缩一点。
②iExtra是个逻辑单位。可以通过GetTextCharacterExtra取得当前字符间距。
17.1.3 使用库存字体
(1)获取特定库存字体:hFont= GetStockObject(iFont);//iFont为系统预定义标识符
(2)选入设置环境SelectObject(hdc,hFont);
(3)利用GetTextMetrics函数获取当前字体信息。
【TabbedTextOut程序】
#include "stdafx.h" #include "TabbedTextOut.h" #define MAX_LOADSTRING 100 // 全局变量: HINSTANCE hInst; // 当前实例 TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本 TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 // 此代码模块中包含的函数的前向声明: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。 MSG msg; HACCEL hAccelTable; // 初始化全局字符串 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_TABBEDTEXTOUT, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // 执行应用程序初始化: if (!InitInstance(hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TABBEDTEXTOUT)); // 主消息循环: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int)msg.wParam; } // // 函数: MyRegisterClass() // // 目的: 注册窗口类。 // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDC_TABBEDTEXTOUT)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszMenuName = NULL;//MAKEINTRESOURCE(IDC_TEST); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex); } // // 函数: InitInstance(HINSTANCE, int) // // 目的: 保存实例句柄并创建主窗口 // // 注释: // // 在此函数中,我们在全局变量中保存实例句柄并 // 创建和显示主程序窗口。 // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } // // 函数: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目的: 处理主窗口的消息。 // // WM_COMMAND - 处理应用程序菜单 // WM_PAINT - 绘制主窗口 // WM_DESTROY - 发送退出消息并返回 // // int iTabStops[] = { 5 * 8, 6 * 8, 15 * 8, 30 * 8, 45 * 8 }; int iLen = sizeof(iTabStops) / sizeof(iTabStops[0]); LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; RECT rc; TEXTMETRIC tm; int yPos = 50; TCHAR szBuf[50]; int k = 0; static int cxClient, cyClient; static TCHAR szText[] = TEXT("abcdfg\thi\tjklmnopqrstuvwxyz\th"); switch (message) { case WM_CREATE: break; case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // 分析菜单选择: switch (wmId) { case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT)); GetTextMetrics(hdc, &tm); GetClientRect(hWnd, &rc); //画刻度尺 MoveToEx(hdc, 0, yPos, NULL); LineTo(hdc, rc.right, yPos); SetTextAlign(hdc, TA_CENTER); for (k = 0; k < 300; k++) { MoveToEx(hdc, k*tm.tmAveCharWidth, yPos, NULL); LineTo(hdc, k*tm.tmAveCharWidth, yPos + 10); if (k % 5 == 0) { TextOut(hdc, k*tm.tmAveCharWidth, yPos + 20, szBuf, wsprintf(szBuf, TEXT("%d"), k)); } } for (k = 0; k < 300; k++) { MoveToEx(hdc, 10 * k*tm.tmAveCharWidth, yPos, NULL); LineTo(hdc, 10 * k*tm.tmAveCharWidth, yPos + 20); if ((k % 5) == 0) { MoveToEx(hdc, k*tm.tmAveCharWidth, yPos, NULL); LineTo(hdc, k*tm.tmAveCharWidth, yPos + 16); } SetBkMode(hdc, TRANSPARENT); SetTextAlign(hdc, TA_LEFT); //TextOut(hdc, cxClient / 2, cyClient / 2, szText, lstrlen(szText)); //TabbedTextOut(hdc, 0, yPos - tm.tmHeight, szText, -1, 0, 0, 0); TabbedTextOut(hdc, 0, yPos - tm.tmHeight, szText, -1, iLen, iTabStops, 10 * 8); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } return 0; }
第17章 文本和字体_17.2 字体的背景知识
17.2 字体的背景知识
17.2.1 GDI字体分类
(1)点阵字体RasterFont(也称为位图字体)——不可缩放,但显示速度极快
字样名称 |
用途 |
System |
用于SYSTEM_FONT字体 |
FixedSys |
用于SYSTEM_FIXED_FONT字体 |
Terminal |
用于OEM_FIXED_FONT字体 |
Courier |
等宽的 |
MS Serif |
|
MS Sans Serif |
用于DEFAULT_GUI_FONT |
Small Fonts |
(2)笔画字体(StrokeFont)——有时也称为绘图仪字体
①由一系列的点和连接点的线段组成,可缩放
②当字号很小时,文本不清晰,很大时,笔画是单线条的,显得单薄。
③笔画字体的字样名称有:Modern、Roman、Script
17.2.2 TrueType字体
字样名称 |
字样名称 |
|
Courier New |
Times New Roman Bold Italic |
|
Courier New Bold |
Arial |
|
Courier New Italic |
Arial Bold |
|
Courier New Bold Italic |
Arial Italic |
|
Times New Roman |
Arial Bold Italic |
|
Times New Roman Bold |
Symbol |
|
Times New Roman Italic |
17.2.3 属性和样式的区别
(1)TrueType字体中,粗体、斜体的Courier、Times New Roman和Arial都有单独的名称,是独立的字体。
(2)但是一般认为粗体或斜体只是同一种字体的不同属性,而不是不同的字体。Windows并没有完全解决这一问题。也就是设置斜体或粗体时即可以用属性,也可以用字样名称。从而也导致了在枚举系统可用字体列表时,必须要进行双重处理,增加了难度。
17.2.4 行间距和字间距
(1)点值(磅):1磅 =1/72英寸。
10磅字号:指“bq”的完整高度(不含字母上的重音符号),即=tmHeight –tmInternalLeading;
(2)行间距示意图
17.2.5 逻辑英寸问题
(1)逻辑英寸:定义显示器上96个或120个像素占据的距离为一个“逻辑英寸”
(2)使用特定点(磅)值显示文本的方法——“逻辑缇”(每个逻辑单位为1/1440英寸)
SetMapMode(hdc,MM_ANSIOTROPIC);
SetWindowExtEx(hdc,1440,1440,NULL); //表示1440个单位到对应1逻辑英寸,即
//1个逻辑单位为1/1440英寸。
SetViewportExt(hdc,GetDeviceCaps(hdc,LOGPIXELSX), //1逻辑英寸
GetDeviceCaps(hdc,LOGPIXELSX),NULL)://1逻辑英寸
如12磅的字体 =12*1/72(英寸)=1440*12/72(逻辑单位)=240逻辑单位。即点值的20倍