2010-08-16 123 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