问题很简单。
有一个字符串"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);

解决方案 »

  1.   

    对于其他的系统函数,
    如:
    GetTextMetrics
    GetTextExtentPoint32等,效果如上,各位可以自行测试
      

  2.   

    这个问题,我以前在这里回答过,现在找不着了。你可以用DrawText API,其最后一个参数用DT_CALCRECT,这样可以不输出字符串,而是精确计算出所需的像素,详细请看MSDN中这个API的说明。我用这种方法,试了很多字体,都没问题。不知道你所要的手写体行否,请将
    你的试验结果告诉大家。
      

  3.   

    只是取得字的宽度还比较简单(可以利用字模信息计算 GetOutlineTextMetrics ),但是要做到所见即所得的打印就困难了。
    无法保证 DrawText 绘制的多行文字在打印机上和屏幕上的换行位置一样(坐标之间的转换会有误差)。
    不知道 word 是怎么实现的。
      

  4.   

    nbb01() 
    这里需要获得输出的具体大小,而不是要输出和边框靠齐。
    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;
    }
      

  5.   

    上面的函数是参考Yuan Feng在他的《windows图形编程》中的例子改写的。
    你可以用Wordpad,字体改斜体,打一个f看看,是否左边少了一块f的角。
    即使是MS Word,相同步骤,你会发现f有一部分超出了页面。所以我觉得这个是Windows的Bug,只有偶们这些小兵写代码弥补了:(
      

  6.   

    你可以认为汉字都是等宽度的,所以只要取一个汉字的ABC宽度就可以了。
      

  7.   

    GetOutlineTextMetrics 可以取得字体的原始字模的信息,用这个高度创建一个字体,这个字体中高度宽度的比值是精确的,因为字符高度是精确的,所以可以通过高度计算宽度,这样可以得到每个字符的精确宽度(包括中文)。to Analyst() 
    汉字不一定都是等宽度(由字体决定),字体包括等款字体和非等款字体。
      

  8.   

    “当然,通过一些技巧解决部分问题,比如Yuan Feng在他的《windows图形编程》中将使用的字体先作为图片生成,然后求出图片大小,在需要求字符串长度的时候,查表累加。但是,这样只可以解决英文,如果是中文,不可能事先求出这个表(除非你的内存足够大,且用户有足够的耐心等待程序的启动)”
    ---------------
    你可以动态的计算每个汉字的宽度,不要预先计算好放在表中。
    不过全部的汉字也没有多少,常用的也就几千,使用表的方法 几十K 的表也够用了。
      

  9.   

    使用GetOutlineTextMetrics 可以取得字体的原始字模的信息
    没错,但那是正常的字体,不是斜体。
    另外,请教如何获得中文字符ABC宽度?
    GetCharABCWidths?不知道汉字如何对应里面的参数
    比如“中”,应该如何使用这个函数?
      

  10.   

    我以前写过,用这个函数就可以搞定。
    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.
      

  11.   

    csdn数据库出错了?留言只有2行了。。ft
      

  12.   

    用DrawText,格式中加上DT_CALCSIZE,在你提交的RECT中会返回此字符按指定的格式要占用的空间!
      

  13.   

    DT_CALCSIZE标志位同样没有考虑斜体的情况,有下面代码为证:
    ////////////////////复制到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);
    ///////////////////
      

  14.   

    虽然看不到DrawText::DT_CALCRECT源代码,但我相信它内部也是用
    GetTextExtent来求输出坐标的。所以不处理ABC宽度。
      

  15.   

    Analyst() 
    假设汉字等宽,但如何求汉字宽度?GetTextExtent求出的是汉字正常字体宽度。汉字的斜体和正常字体宽度不等。
      

  16.   

    从排版人员的角度来看,常用的汉字字体比如宋体根本没有斜体;
    仔细看,宋体只有一个文件,而其它英文字体(好比times new roman)是4个文件(type1的则是8个或其它,两倍);
    再在写字板或者word里输入中、英文,分别使用宋体和times new roman,然后设为斜体,可以发现英文字体的斜体是经过设计的(斜a与正a绝对不是拉出来的,字型很大的不同),而中文字体仅仅是拉斜的;不美观
    不知道微软怎么想出这个api来的;
    所以建议不要对中文应用斜体特性,需要区分可以使用黑体字