引言
我的问题来自过去几天来我一直在处理的一个相当有趣的问题。我最近问了一个关于编写自定义属性检查器-在验证值时如何处理内部编辑器焦点?的问题
此后,我在控件方面取得了一些很好的进展,例如在中间添加了一个分隔符,以分隔名称和值行,更重要的是,可以使用分隔符来调整两列的大小。
这里是我的问题开始的地方,在调整分隔器大小的同时使内部编辑器可见,使我的控制稍微放慢了一些速度。因此,我进一步更改了代码,如果分隔符没有调整大小,则只显示内部编辑器。因此,从本质上讲,我使用Canvas.TextOut将我的值绘制为字符串,如果选择了一行,那么内部编辑器将在上面显示。如果调整了分隔符的大小,内部编辑器就会隐藏,一旦调整大小操作完成,内部编辑器就会再次可见。
虽然这解决了我提到的稍微放缓的问题,但我面临一个新的问题:来自内部编辑器(基本上是TEdit)的文本与我使用Canvas.TextOut绘制的文本略有不同。
示例1
这种差别是非常微妙的,但如果你看得足够近,你就能看到它:

图1 Canvas.TextOut

图2 DrawText
您可能需要使用屏幕放大镜来更近距离地查看,但是对于SomeText行,更明显的是,Some和Text之间以及Text中的T和e之间的间距略有不同。
示例2
一个稍微好一些的例子可能是将Canvas.TextOut和DrawText与内部编辑器(TEdit)文本进行比较:

图3比较
正如你所看到的,这里的区别更加突出。字符串True在使用Canvas.TextOut时清楚地显示了文本字符之间的较大间距,其中DrawText和inplace editor呈现的文本完全相同。
当我使用Canvas.TextOut时,我在调整我的检查器分配器大小和显示和隐藏内部编辑器之间出现了各种可怕的文本不匹配。如果我没有尝试和尝试替代文本绘制方法,我想我就不会意识到这种不同,并找到了一个解决方案。重要的是要知道,在将文本绘制到画布上时,我使用的字体设置与我为内部编辑器定义的字体完全相同。
现在我使用的是DrawText而不是Canvas.TextOut,一切都与内部编辑器以及我想要的方式一致工作。
问题
我的问题是,是什么使Canvas.TextOut呈现文本与DrawText如此不同?从我的示例和处理我当前的问题来看,很明显,Canvas.TextOut呈现文本的方式与具有相同字体设置的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测试演示
再次注意在使用True时字符串Canvas.TextOut中的间距,从我的末尾看,它与DrawText和TEdit绘制文本的方式明显不同。
下图与图4相同,但放大到400%

图5 400%的试验演示
T和e在Text中有明显的区别,在True中也有T和r。

图6“文本”一词在指南中以400%的速度放大
您可以看到,T和e之间的角化比Canvas.TextOut (使用ExtTextOut)更接近于DrawText。

图7 True这个词在指引下放大到700%
您可以看到,T和r之间的角化比使用Canvas.TextOut (使用ExtTextOut)更接近DrawText和内部编辑器(TEdit)。
我测试了几种不同的字体,下面是我的发现:
好:
公司简介: Arial,Cambria,Candara,Comic Sans MS,Consolas,Courier,Courier New,Fixedsys,乔治亚,Lucida Console,Lucida Sans Unicode,Microsoft Sans Serif,Tahoma,终端和Times New Roman。
坏:
卡利布里,科贝尔,Myriad Pro,Segoe UI,Trebuchet MS和Verdana。
好的字体显示文本的方式与DrawText和Inpace (TEdit)控件使用Canvas.TextOut的方式相同。坏的结果表明,Canvas.TextOut呈现的文本与其他方法略有不同。
这里可能有一些线索,虽然我不太确定,但我还是要添加它,以防万一。
发布于 2015-08-15 12:50:36
观察到的差异是由于使用了不同的WinAPI文本呈现函数及其行为。专用字符角化
在印刷术中,角化(较不常见的修饰)是在比例字体中调整字符间距的过程,通常是为了达到令人愉悦的效果。Kerning调整单个字母窗体之间的空格,而跟踪(字母间距)则在字符范围内统一调整间距。
DrawText函数在指定的矩形中绘制格式的文本。它根据指定的方法对文本进行格式化(展开制表符、对字符进行对齐、拆分行等等)。
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
);如果lpDx参数为NULL,则ExtTextOut函数使用字符之间的默认间距。lpDx参数所指向的数组的字符单元格来源和内容以逻辑单元指定。字符单元格起源定义为字符单元格的左上角.
基本上,DrawText将自动绘制格式化文本,这包括调整字符之间的间距(kerning),而ExtTextOut默认使用字符之间的默认间距(不加字符)。如果要调整字符之间的间距,则必须计算并提供kerning数组(lpDx)参数。
这些差异在一些字符组合中尤其明显,比如T和小号字母,它们在T下视觉上很适合,或者AV,其中一个V适合于A。不同的字体也有不同的默认内核,这就是为什么有些字体在视觉上使用两种功能都有相同的呈现,而有些则没有。角化也取决于字体大小。例如,与Arial在9 pt处呈现的字符9 pt具有相同的输出,而12 pt处的Arial将产生不同的输出。
下面的图像中的第一行是用ExtTextOut绘制的,第二行是用DrawText绘制的自动行。

https://stackoverflow.com/questions/31968771
复制相似问题