2010-08-16 83 views
2

我已经在Delphi中编写了一个Windows程序,它使用GetCharWidth和Em-Square非常精确地放置和包装文本到屏幕和打印机。这与ANSI文本很好地协作,你只需要检索和计算255个字符的宽度,但是当你使用65535个字符的Unicode时,它太慢了。由于必须创建两个宽度数组,一个用于正常,一个用于粗体,问题变得更糟。所见即所得编码

//Setup a reference canvas for measuring purposes 
    RefDC := CreateCompatibleDC (FCanvas.Handle) ; 
    DPI := GetDeviceCaps (RefDC , LOGPIXELSY) ; 

    //find EmSquare 
    GetOutlineTextMetrics (RefDC , sizeof(otm) , @otm[0]) ; 
    EmSq := otm[0].otmEmSquare ; 

    //calc NORMAL char sizes 
    GetObject (FCanvas.Font.Handle , SizeOf (lf) , @lf) ; 

    lf.lfHeight := -EmSq ; 
    lf.lfWidth := 0 ; 
    lf.lfWeight := FW_NORMAL ; 

    hf := CreateFontIndirect (lf) ; 
    hold := SelectObject (RefDC , hf) ; 

    GetCharWidth (RefDC , 0 , $FFFF , nCharWidth) ; 
    for a := 0 to $FFFF do 
    fCharWidth[a] := nCharWidth[a]* PixelSize/EmSq ; 

    SelectObject (RefDC , hold) ; 
    DeleteObject (hf) ; 

    //calculate line height 
    PixelSize := abs (fCanvas.Font.Size * DPI/72) ; 
    GetOutlineTextMetrics (RefDC , sizeof(otm) , @otm[0]) ; 
    LineHt := round ((otm[0].otmTextMetrics.tmHeight + 
         otm[0].otmTextMetrics.tmExternalLeading) * 
         PixelSize/EmSq) ; 

    //calculate Bold char sizes 
    lf.lfWeight := FW_BOLD ; 
    hf := CreateFontIndirect (lf) ; 
    hold := SelectObject (RefDC , hf) ; 

    GetCharWidth (RefDC , 0 , $FFFF , nCharWidth) ; 
    for a := 0 to $FFFF do 
    fBoldWidth[a] := nCharWidth[a] * PixelSize/EmSq ; 

    SelectObject (RefDC , hold) ; 
    DeleteObject (hf) ; 

    DeleteDC (RefDC) ;` 
+3

Unicode没有65,535个字符。它通过分配码点来定义(撰写本文时)约107,000个字符。你可能在谈论UTF-16,这是一种以16位单位明确表示所有这些代码点的方法(http://en.wikipedia.org/wiki/UTF-16)。它是一种可变长度编码,因为某些字符可能占用多个16位单元,但其设计使基本多语言平面中的所有代码点都“适合”在一个16位单元中。 – 2010-08-16 21:29:34

+0

那么你的问题是什么? – 2010-08-16 21:40:39

+0

我需要更快的方式来做到这一点。必须有其他所见即所得的策略可以更有效地处理UNICODE。 – Mitch 2010-08-16 21:46:50

回答

8

计算单个字符宽度并在Unicode中添加它们不仅非常缓慢,而且错误并且无法正常工作。 Unicode将字符标记组合在一起,有时以复杂的方式。

使用Unicode时,正确的方法是将整个字符串与您的行的宽度一起传递给windows API函数GetTextExtentExPoint,它会计算出适合您的行数。

an example of its use here

+0

谢谢,这看起来很有前途,但我认为像其他Windows文本扩展功能一样,当我将图形缩放到打印机分辨率时,它不会产生相同的结果。另外,我需要一种方式来分解混合风格的部分(粗体,斜体等)。我会试一试。 – Mitch 2010-08-17 14:48:45

+1

这不仅仅是有希望的。这是Windows做WYSIWYG文字换行的方式。这是API的一切呼吁:IE,Word,你的名字。 – lkessler 2010-08-17 15:24:28

+1

这就是我想听到的! – Mitch 2010-08-17 15:46:37

0

您是否使用了一个分析器来实际查看瓶颈的位置?
使用查找表时,一个常见的想法,他们似乎过于昂贵的动态构建是建立一次,并将它们存储为一个资源,例如...

+0

在这个例程中,大约有7秒的初始化命中。它可以被优化,但可能不会获得太多。调用一次是可以接受的,但实际上我需要多次调用其他文本块来使用不同的字体和/或文本大小。 – Mitch 2010-08-17 14:41:40

1

除了质疑问题本身的前提,你可以切我认为通过使用单独的数组和字体对象等获得两个nCharWidth数组并将它们一起处理,可以将两个0..65535循环缩减为单个循环。

此外,您可以从循环中排除范围$ D800- $ DFFF,因为它们不能代表它们自己的字符(作为代理对的第一个,您的代码看起来并没有设计用于处理) 。

2

我认为,在典型的会话中不太可能使用数千个字符。如果是这样,在第一轮计算只有前128个字符的宽度,并把f.i. -1到其余的。当查找宽度为-1时,如果只是计算该字符的宽度,高度等,则进行测试。

+0

基本上只计算你需要知道的第一次,你需要知道它。 – 2010-08-17 00:37:51

+0

我想过只做ASCII范围,然后只在需要时(很少)才做范围之外的字符,但似乎惩罚任何使用非ASCII字符的人,并且性能打击很大。 – Mitch 2010-08-17 14:37:52

+0

@Mitch - 我有点吃惊,得知只有一个字符的宽度等是一个很大的性能打击,但无论如何,所以它是.. – 2010-08-17 15:51:25