问题很简单。
有一个字符串"ffff",求用GDI函数DrawText/TextOut等输出到屏幕后这个字符串占用屏幕像素的精确大小。
-------------------------------
看起来很简单,1分钟就可以写出下面代码。
pdc->SelectObject(&font);//选择你需要的字体
CString strTest = _T("ffff");//字符串可以任意指定
CSize cs=pdc->GetTextExtent(strTest);//获得字符串长度
是不是很简单?cs里面就是答案。
但是,很不幸,这个结果只是在大多数情况下有效。在字体为斜体或者自体本身为手写体(script字体)是,它的精确度就会出现问题。
当然,通过一些技巧解决部分问题,比如Yuan Feng在他的《windows图形编程》中将使用的字体先作为图片生成,然后求出图片大小,在需要求字符串长度的时候,查表累加。但是,这样只可以解决英文,如果是中文,不可能事先求出这个表(除非你的内存足够大,且用户有足够的耐心等待程序的启动)===============
欢迎大家讨论
--------------
附:测试代码,copy到OnDraw内即可。输出后可以看到f有一部分超出了外接矩形框
CFont font;
font.CreateFont(
120, // height of font
0, // average character width
0, // angle of escapement
0, // base-line orientation angle
FW_NORMAL, // font weight
1, // italic attribute option
0, // underline attribute option
0, // strikeout attribute option
DEFAULT_CHARSET, // character set identifier
OUT_DEFAULT_PRECIS,// output precision
CLIP_DEFAULT_PRECIS,// clipping precision
DEFAULT_QUALITY, // output quality
DEFAULT_PITCH , // pitch and family
"宋体" // typeface name
);
pDC->SaveDC();
pDC->SelectObject(&font);//选择你需要的字体
CString strTest = _T("ffff");//字符串可以任意指定
CSize cs=pDC->GetTextExtent(strTest);//获得字符串长度
CRect rc(0,0,cs.cx,cs.cy);
pDC->SetBkMode(TRANSPARENT);
FrameRect(pDC->GetSafeHdc(),rc,(HBRUSH)GetStockObject(BLACK_BRUSH));
pDC->DrawText(strTest,rc,DT_LEFT|DT_NOCLIP);
pDC->RestoreDC(-1);
有一个字符串"ffff",求用GDI函数DrawText/TextOut等输出到屏幕后这个字符串占用屏幕像素的精确大小。
-------------------------------
看起来很简单,1分钟就可以写出下面代码。
pdc->SelectObject(&font);//选择你需要的字体
CString strTest = _T("ffff");//字符串可以任意指定
CSize cs=pdc->GetTextExtent(strTest);//获得字符串长度
是不是很简单?cs里面就是答案。
但是,很不幸,这个结果只是在大多数情况下有效。在字体为斜体或者自体本身为手写体(script字体)是,它的精确度就会出现问题。
当然,通过一些技巧解决部分问题,比如Yuan Feng在他的《windows图形编程》中将使用的字体先作为图片生成,然后求出图片大小,在需要求字符串长度的时候,查表累加。但是,这样只可以解决英文,如果是中文,不可能事先求出这个表(除非你的内存足够大,且用户有足够的耐心等待程序的启动)===============
欢迎大家讨论
--------------
附:测试代码,copy到OnDraw内即可。输出后可以看到f有一部分超出了外接矩形框
CFont font;
font.CreateFont(
120, // height of font
0, // average character width
0, // angle of escapement
0, // base-line orientation angle
FW_NORMAL, // font weight
1, // italic attribute option
0, // underline attribute option
0, // strikeout attribute option
DEFAULT_CHARSET, // character set identifier
OUT_DEFAULT_PRECIS,// output precision
CLIP_DEFAULT_PRECIS,// clipping precision
DEFAULT_QUALITY, // output quality
DEFAULT_PITCH , // pitch and family
"宋体" // typeface name
);
pDC->SaveDC();
pDC->SelectObject(&font);//选择你需要的字体
CString strTest = _T("ffff");//字符串可以任意指定
CSize cs=pDC->GetTextExtent(strTest);//获得字符串长度
CRect rc(0,0,cs.cx,cs.cy);
pDC->SetBkMode(TRANSPARENT);
FrameRect(pDC->GetSafeHdc(),rc,(HBRUSH)GetStockObject(BLACK_BRUSH));
pDC->DrawText(strTest,rc,DT_LEFT|DT_NOCLIP);
pDC->RestoreDC(-1);
如:
GetTextMetrics
GetTextExtentPoint32等,效果如上,各位可以自行测试
你的试验结果告诉大家。
无法保证 DrawText 绘制的多行文字在打印机上和屏幕上的换行位置一样(坐标之间的转换会有误差)。
不知道 word 是怎么实现的。
这里需要获得输出的具体大小,而不是要输出和边框靠齐。
DT_CALCRECT是采用改变DrawText参数中输出矩形大小办法来使文字在输出范围内的。在CAD等系统中,不允许这种改变。gboy(★)(★)
单个和多个其实是一样的,因为字符串宽度= 字符串首字符A宽度+字符串GetTextExtent()值+字符串末尾字符C宽度。
对于一个英文字符串,可以使用GetCharABCWidth来求的ABC宽度,而中文字符好像没办法求。开始我以为中文TRUETYPE没有ABC宽度,但是测试后发现还是存在的。谢谢上面2位参加。
我写了1个函数,就当抛砖引玉拉。
///////////////////////////////////////////////////
//功能:精确计算字符串宽度
//原理:计算开头/结尾的字符ABC宽度,作相应处理
//公式:字符串精确宽度 = 字符串宽度 + 第一个字符A宽度 + 最后一个字符C宽度
//
//bug:windows没有办法计算中文的字符ABC宽度,所以碰到斜体的中文,GetCharABCWidths()会
// 无效。所以只能手动增加f的字符宽度(因为f为英文中最大A,C宽度字符)BOOL CSCText::GetTextABCExtent(HDC hDC, LPCTSTR lpString, int cbString, long * pHeight, ABC * pABC)
{
SIZE size;
if ( ! GetTextExtentPoint32(hDC, lpString, cbString, & size) )
return FALSE;
* pHeight = size.cy;
pABC->abcB = size.cx;
ABC abc;
// first
if(!GetCharABCWidths(hDC, lpString[0], lpString[0], & abc))
GetCharABCWidths(hDC, 102, 102, & abc); pABC->abcB -= abc.abcA;
pABC->abcA = abc.abcA;
if(!GetCharABCWidths(hDC, lpString[cbString-1], lpString[cbString-1], & abc))
GetCharABCWidths(hDC, 102, 102, & abc);
pABC->abcB -= abc.abcC;
pABC->abcC = abc.abcC;
return TRUE;
}
你可以用Wordpad,字体改斜体,打一个f看看,是否左边少了一块f的角。
即使是MS Word,相同步骤,你会发现f有一部分超出了页面。所以我觉得这个是Windows的Bug,只有偶们这些小兵写代码弥补了:(
汉字不一定都是等宽度(由字体决定),字体包括等款字体和非等款字体。
---------------
你可以动态的计算每个汉字的宽度,不要预先计算好放在表中。
不过全部的汉字也没有多少,常用的也就几千,使用表的方法 几十K 的表也够用了。
没错,但那是正常的字体,不是斜体。
另外,请教如何获得中文字符ABC宽度?
GetCharABCWidths?不知道汉字如何对应里面的参数
比如“中”,应该如何使用这个函数?
CDC::GetTextExtent
CSize GetTextExtent( LPCTSTR lpszString, int nCount ) const;CSize GetTextExtent( const CString& str ) const;Return ValueThe dimensions of the string (in logical units) in a CSize object.
////////////////////复制到View的OnDraw内即可
CFont font;
font.CreateFont(
120, // height of font
0, // average character width
0, // angle of escapement
0, // base-line orientation angle
FW_NORMAL, // font weight
1, // italic attribute option
0, // underline attribute option
0, // strikeout attribute option
DEFAULT_CHARSET, // character set identifier
OUT_DEFAULT_PRECIS, // output precision
CLIP_DEFAULT_PRECIS,// clipping precision
DEFAULT_QUALITY, // output quality
DEFAULT_PITCH , // pitch and family
"宋体" // typeface name
);
pDC->SaveDC();
pDC->SelectObject(&font);//选择你需要的字体
CString strTest = _T("ffff");//字符串可以任意指定
CSize cs=pDC->GetTextExtent(strTest);//获得字符串长度
CRect rc(0,0,0,0);
pDC->SetBkMode(TRANSPARENT);
int retH = pDC->DrawText(strTest,rc,DT_LEFT|DT_NOCLIP|DT_CALCRECT);
rc.bottom = rc.top + retH;
FrameRect(pDC->GetSafeHdc(),rc,(HBRUSH)GetStockObject(BLACK_BRUSH));
pDC->DrawText(strTest,rc,DT_LEFT|DT_NOCLIP);
pDC->RestoreDC(-1);
///////////////////
GetTextExtent来求输出坐标的。所以不处理ABC宽度。
假设汉字等宽,但如何求汉字宽度?GetTextExtent求出的是汉字正常字体宽度。汉字的斜体和正常字体宽度不等。
仔细看,宋体只有一个文件,而其它英文字体(好比times new roman)是4个文件(type1的则是8个或其它,两倍);
再在写字板或者word里输入中、英文,分别使用宋体和times new roman,然后设为斜体,可以发现英文字体的斜体是经过设计的(斜a与正a绝对不是拉出来的,字型很大的不同),而中文字体仅仅是拉斜的;不美观
不知道微软怎么想出这个api来的;
所以建议不要对中文应用斜体特性,需要区分可以使用黑体字