我的问题来自于我过去几天处理的一个相当有趣的问题。我最近问了一个关于Writing a custom property inspector – How to handle inplace editor focus when validating values?的问题
我已经做了一些很好的进展,我的控制,如添加一个分隔符在中间分隔名称和值行之间,重要的是,分隔符可以用来调整两列的大小。
这里是我的问题开始的地方,在调整大小的分隔线时,内置编辑器可见,导致我的控制轻微的减慢。所以我进一步改变了代码,只有当分隔符未被调整大小时才显示inplace编辑器。所以本质上,我使用Canvas.TextOut来绘制我的值作为字符串,如果选择一行,则Inplace编辑器如上所示。如果分隔符已调整大小,则内部编辑器将变为隐藏,一旦调整大小操作已完成,inplace编辑器将再次可见。
虽然这解决了我提到的轻微的放缓问题,但我遇到了一个新的问题,即来自inplace编辑器(基本上是TEdit)的文本与使用Canvas.TextOut绘制的文本略有不同
实施例1
区别是非常微妙的,但如果你看起来足够近,你可以看到它:
图1 Canvas.TextOut
图2 DrawText
您可能需要使用屏幕放大镜看起来更加接近,但是对于SomeText行,更为明显的是,Some和Text之间的间距以及Text中的T和e之间的间距稍有不同。
实施例2
一个更好的例子可能是将Canvas.TextOut和DrawText与Inplace编辑器(TEdit)文本进行比较:
图3比较
正如你可以看到,这里的区别更加突出。当使用Canvas.TextOut时,字符串True清楚地显示文本字符之间的间距,其中,作为DrawText和inplace编辑器渲染文本完全一样。
当我使用Canvas.TextOut时,在调整我的检查器分隔符和显示和隐藏内置编辑器之间,我得到了各种可怕的文本不匹配。如果我没有尝试和尝试替代文本绘图方法,我不认为我会意识到差异,并找到一个解决方案。重要的是要知道,当我将文本绘制到画布中时,我正在使用完全相同的字体设置,就像我为内置编辑器定义的字体一样。
现在我使用的是DrawText而不是Canvas.TextOut,所有内容都与Inplace编辑器一致,正是我想要的。
题
我的问题是什么使Canvas.TextOut渲染文本与DrawText不同?从我的例子和处理我当前的问题,很明显,Canvas.TextOut不会以与使用相同Font设置的TEdit相同的方式呈现文本,但是DrawText会使文本看起来是正确的。
这让我质疑Canvas.TextOut的使用,如果它不正确地呈现文本,我应该总是使用DrawText代替?
测试演示
您可以使用以下代码自行测试:
type TForm1 = class(TForm) Edit1: TEdit; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormPaint(Sender: TObject); private FFont: TFont; FRect: TRect; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin FFont := TFont.Create; FFont.Color := clNavy; FFont.Name := 'Segoe UI'; FFont.Size := 9; FFont.Style := []; FRect := Rect(10,30,100,100); Canvas.Font.Assign(FFont); Edit1.Font.Assign(FFont); end; procedure TForm1.FormDestroy(Sender: TObject); begin FFont.Free; end; procedure TForm1.FormPaint(Sender: TObject); begin Canvas.TextOut(10,10,'Canvas.TextOut: [True]'); DrawText(Canvas.Handle,PChar('DrawText: [True]'),Length('DrawText: [True]'),FRect,DT_LEFT); end;
随着上述运行在一个全新的VCL项目,我得到的结果如下:
图4测试演示
再次注意到使用Canvas.TextOut时,字符串中的间距为True,从我的目的来看,它与DrawText和TEdit绘制文本的方式截然不同。
下图是与图4相同的图像,但放大到400%
图5测试演示放大400%
文本中的T和e之间可以看到显着的差异,而T和r也是True。
图6“文字”一词以400%的指标放大
你可以看到T和e之间的字距是DrawText的一个像素,而不是Canvas.TextOut(使用ExtTextOut)。
fig.7“True”字以700%的指标放大
您可以看到T和r之间的字距与DrawText和Inplace Editor(TEdit)相比,与Canvas.TextOut(使用ExtTextOut)相比更接近一个像素。
我已经测试了几种不同的字体,这里是我的发现:
好:
Arial,Cambria,Candara,Comic Sans MS,Consolas,Courier,Courier New,
Fixedsys,Georgia,Lucida Console,Lucida Sans Unicode,Microsoft Sans
Serif,Tahoma,Terminal and Times New Roman.
坏:
Calibri,Corbel,Myriad Pro,Segoe UI,Trebuchet MS and Verdana.
好的字体是与DrawText和InEdit Editor(TEdit)控件使用Canvas.TextOut相同的方式呈现文本。坏的显示Canvas.TextOut呈现与其他方法略有不同的文本。
这里可能有一些线索,虽然我不太确定,但我反正补充说,以防万一。
解决方法
In typography,kerning (less commonly mortising) is the process of
adjusting the spacing between characters in a proportional font,
usually to achieve a visually pleasing result. Kerning adjusts the
space between individual letter forms,while tracking (letter-spacing)
adjusts spacing uniformly over a range of characters.
> DrawText
The DrawText function draws formatted text in the specified rectangle.
It formats the text according to the specified method (expanding tabs,
justifying characters,breaking lines,and so forth).
> ExtTextOut(由Canvas.TextOut使用)
ExtTextOut声明:
BOOL ExtTextOut( _In_ HDC hdc,_In_ int X,_In_ int Y,_In_ UINT fuOptions,_In_ const RECT *lprc,_In_ LPCTSTR lpString,_In_ UINT cbCount,_In_ const INT *lpDx );
If the lpDx parameter is NULL,the ExtTextOut function uses the
default spacing between characters. The character-cell origins and the
contents of the array pointed to by the lpDx parameter are specified
in logical units. A character-cell origin is defined as the upper-left
corner of the character cell.
基本上DrawText将自动绘制格式化的文本,包括调整字符间距(字距),而ExtTextOut默认使用字符之间的默认间距(无字距)。如果要调整字符之间的间距,则必须计算并提供字距数组(lpDx)参数。
这些差异特别可见,一些字符组合,如T和小写字母,视觉上适合T或AV,其中一个V适合A。不同的字体也有不同的默认的核心,这就是为什么一些字体具有视觉上相同的渲染使用这两个功能有些不是。字距也取决于字体大小。例如,使用Arial在9 pt渲染的字符AV将具有相同的输出与两个功能,而Arial在12 pt将导致不同的输出。
使用ExtTextOut绘制无缝角度的第一行,并使用DrawText进行自动字距调整。