有大量(几百字到几百万字之间)文本需要快速排版,其中绝大部分为中文,这些文本上有多种字体。要正确的进行排版,偶必须知道文本中每个字符的高宽。偶知道使用DrawText/DT_CALCRECT可以获得字符在特定字体下的高宽,不过可能出现的数百万次的系统调用显然无法满足快速排版的需求,而且获得字符高宽之后缓存它们(因为窗口resize的时候重排还需要这些数据)也是很大的内存消耗。为了解决以上问题,偶做了一个假定,偶假定在相同的字体/字号下,所有的汉字字符都是等宽等高的,比如在宋体五号下“一、二、三...”等字符全部都是相同高宽。这样,针对每种字体/字号,我只需要一次系统调用,以后遇到相同字体/字号的任何汉字的时候通通以第一次调用获得的高宽为准。如此一来,所需的系统调用次数以及数据缓存空间仅仅和字体/字号组合数有关,而一般情况下,一篇文档所应用的字体/字号组合局限在几种到几十种之间,很少会突破百种。系统调用和存储空间都得到了极大的节省(呵呵,成千上万倍啊)。并且由于缓存数据极少,两层一般不超过10的map,获取指定字体指定字符的高宽也极其迅速。看上去很完美。不过,现实是残酷的。虽然偶所做的假定在绝大多数情况下是正确的,但是也有极少数情况下不正确,比如编码为“A1A1”的字符使用int _ismbblead( unsigned int c )判断会返回它是一个多字节字符,也就是说它是个汉字(或者日文/韩文),但是它在“幼圆”字体下的宽度和一般字符不同,在宋体下好像还是相同的。偶的假定失效。这个字符偶现在也不知道是个什么东东,它在win2k和winxp下用DrawText显示出来效果还不同,汗。错误的假定导致了一个排版错误,现在仿佛有一堆候选解决方案,比如禁止这个字符,比如特殊处理这个字符,比如不再使用原来的假定。不过都有问题,前面两个方案是基于经验的,但是偶不知道这类字符有多少,只能碰见一个抓一个。采用最后一个方案的话,偶必须要获得另外一个高效且无错误的获取字符高宽的方法,偶不知道有什么方法可以做到这一点。各位大虾看看有没有什么解决方案,share一个给我。不胜感激。

解决方案 »

  1.   

    我的排版里行,段也都有类管理啊。偶有超过百个class和几十个interface,不是个简单的东西。并且第一版已经提供给上百万人使用过大约一年,目前做的是第二版,开发已接近尾声。这个问题在第一版里就存在,因为不符合假定的字符相对少,并且即使出现也只在选中的时候有轻微的显示问题,所以一只遗留在系统里。现在有些空余时间了,希望能探寻一下。呵呵,偶不是心血来潮跑来浪费大家的时间,希望大家能给多一些建议。
      

  2.   

    学习ing.....我给你指一个高手,你和他交流一下;
    但记得有好的方法了告诉大家一声哦.....http://www.fengyuan.com/
    呵呵,就是他,楼上各位应该都认识。
      

  3.   

    很久前的写的一段代码
    int FAR PASCAL _export GetStringWidth(LPSTR FontName,int FontSize,int Weight,LPSTR String)
    {
    HDC hDC;
    HFONT hFont,hOldFont;
    LOGFONT LogFont;
    int StringLength; hDC=GetDC(NULL); LogFont.lfHeight=-MulDiv(FontSize,GetDeviceCaps(hDC,LOGPIXELSX),72);
    LogFont.lfWidth =0;
    LogFont.lfEscapement=0;
    LogFont.lfOrientation=0;
    LogFont.lfWeight=(Weight)?FW_BOLD:FW_NORMAL;
    LogFont.lfItalic=0;
    LogFont.lfUnderline=0;
    LogFont.lfStrikeOut=0;
    LogFont.lfCharSet=DEFAULT_CHARSET;
    LogFont.lfOutPrecision=OUT_DEFAULT_PRECIS;
    LogFont.lfClipPrecision=CLIP_DEFAULT_PRECIS;
    LogFont.lfQuality=DEFAULT_QUALITY;
    LogFont.lfPitchAndFamily=FF_DONTCARE|VARIABLE_PITCH;
    lstrcpy(LogFont.lfFaceName,FontName); hFont=CreateFontIndirect(&LogFont);
    hOldFont=SelectObject(hDC,hFont);
    StringLength=LOWORD(GetTextExtent(hDC,String,lstrlen(String)));
    hFont=SelectObject(hDC,hOldFont);
    DeleteObject(hFont); ReleaseDC(NULL,hDC);   return StringLength;
    }
      

  4.   

    尝试过GetTextExtentExPoint系列了,在一万字左右效率和原来的方案就相差两到三个数量级了,如果更多字符就更慢了。不过,GetTextExtentExPoint系列比DrawText/DT_CALCRECT高效一些,一倍左右吧。希望各位大虾继续提出建议。
      

  5.   

    模式设计里有一种flyweight模式,好像就是用来解决这个问题的,不过他针对的是英文,只有26个字母,汉字的话可能要有5~6k,不知道还好不好用。
      

  6.   

    做过简单的Editor,这个的确是有些麻烦