问题
I've written a Windows program in Delphi that places and wraps text very precisely to both the screen and printer using GetCharWidth and Em-Square. This has worked well with ANSI text where you only need to retrieve and calculate the widths of 255 characters but when you go to Unicode with 65535 characters its too slow. The problem is made worse by having to create 2 arrays of width, one for normal and one for bold.
//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 ) ;`
回答1:
Calculating individual character widths and adding them up in Unicode is not only very slow, but it's wrong and will not work correctly. Unicode combines character marks together, sometimes in complex ways.
With Unicode, the correct way to do it is to pass your whole string to the windows API function GetTextExtentExPoint along with the width of your line, and it will figure out how many characters will fit on the line for you.
There is an example of its use here.
回答2:
I take it, it is unlikely to use thousands of characters in a typical session. If so, in the first round calculate only the first 128 characters width, and put f.i. -1 to all the rest. When a lookup is made test if width is -1, if it is only then calculate the width, height etc. for that character.
回答3:
Apart from questioning the premise of the question itself, you could cut the processing significantly I think by obtaining the two nCharWidth arrays using separate arrays and font objects etc and processing them together, reducing your two 0..65535 loops to a single loop.
Also, you can exclude the range $D800-$DFFF from your loops since these can never represent characters on their own (being the first of a surrogate pair, which your code doesn't appear to be designed to handle).
回答4:
Have you used a profiler to actually see where the bottleneck might be?
One common idea when using lookup tables and they seem too costly to build dynamically is to build them once and store them as a resource for instance...
来源:https://stackoverflow.com/questions/3497471/wysiwig-with-unicode