我的问题来自于过去几天我一直在处理的一个相当有趣的问题。我最近问了一个关于Writing a custom property inspector - How to handle inplace editor focus when validating values?
的问题我已经在我的控件上取得了一些不错的进展,比如在中间添加一个分隔符来分隔Name和Value行,重要的是分隔符可以用来调整两列的大小。
这是我的问题开始的地方,让inplace编辑器可见,同时调整分隔符的大小导致我的控制稍微减慢。所以我进一步改变了代码,只显示了inplace编辑器,如果没有调整分隔符的大小。基本上,我使用Canvas.TextOut
将我的值绘制为字符串,如果选择了一行,则上面显示了Inplace编辑器。如果已调整分隔符大小,则inplace编辑器将变为隐藏状态,一旦调整大小操作完成,就位编辑器将再次可见。
虽然这解决了我提到的轻微减速问题,但我遇到了一个新问题,因为来自内部编辑器(基本上是TEdit)的文本与我使用Canvas.TextOut
绘制的文本略有不同
差异非常微妙但如果你看得足够近,你可以看到它:
图1 Canvas.TextOut
图2 DrawText
您可能需要使用屏幕放大镜来更贴近,但使用SomeText行时,Some
和Text
之间以及T
之间的间距会更明显e
中的Text
略有不同。
稍微好一点的例子可能是在Canvas.TextOut
和DrawText
之间与就地编辑器(TEdit)文本进行比较:
图3比较
正如您所看到的,这里的区别更加突出。使用True
时,字符串Canvas.TextOut
清楚地显示文本字符之间的间距要大得多,其中DrawText
和inplace editor
呈现的文字完全相同。
当我使用Canvas.TextOut
时,在调整检查器分隔符的大小以及显示和隐藏原位编辑器之间,我遇到了各种可怕的文本不匹配。如果我没有尝试并尝试替代的文字绘图方法,我不认为我会意识到差异并找到了解决方案。重要的是要知道我在将文本绘制到画布时使用完全相同的字体设置作为我为内置编辑器定义的字体。
现在我正在使用DrawText
而不是Canvas.TextOut
,所有内容都与内置编辑器一致,并且正是我想要的。
我的问题是什么使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测试演示
再次注意使用True
时字符串Canvas.TextOut
中的间距,从我的结尾看,它明显不同于DrawText
以及TEdit
绘制文字的方式。
以下是与图4相同的图像,但放大率为400%
图5测试演示缩放为400%
T
中的e
和Text
以及T
中的r
和True
之间存在明显的差异。
图6单词' Text'使用指南放大了400%
您可以看到T
和e
之间的字距调整与DrawText
的距离比使用Canvas.TextOut
ExtTextOut
的{{1}}更接近一个像素。)
图7单词True
以指南放大了700%
您可以看到T
和r
之间的字距调整与DrawText
和Inplace Editor(TEdit)比使用Canvas.TextOut
(使用{{}更接近一个像素。 1}}。)
我测试了几种不同的字体,这是我的发现:
好:
Arial,Cambria,Candara,Comic Sans MS,Consolas,Courier,Courier New, Fixedsys,Georgia,Lucida Console,Lucida Sans Unicode,Microsoft Sans Serif,Tahoma,Terminal和Times New Roman。
为:
Calibri,Corbel,Myriad Pro,Segoe UI,Trebuchet MS和Verdana。
好的字体看起来像ExtTextOut
一样呈现文本,而Inpace Editor(TEdit)控件使用DrawText
。坏的表明Canvas.TextOut
呈现的文本与其他方法略有不同。
虽然我不太确定,但这里可能有一些线索,但无论如何我都会添加它以防万一。
答案 0 :(得分:7)
观察到的差异是由于使用了不同的WinAPI文本呈现函数及其行为。特别是字符kerning
在排版中,字距调整(不太常见的榫眼)就是这个过程 调整比例字体中字符之间的间距, 通常是为了获得视觉上令人愉悦的结果。 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
会自动绘制格式化文本,包括调整字符间距(字距调整),而ExtTextOut
默认情况下会使用字符间的默认间距(no-kerning)。如果要调整字符间距,则必须计算并提供字距调整数组(lpDx
)参数。
这些差异特别适用于某些字符组合,例如T
和视觉上适合T
的小写字母,或AV
其中V
适合A
}。不同的字体也有不同的默认字距,这就是为什么有些字体使用两种功能在视觉上相同的渲染而有些不是。字距也取决于字体大小。例如,在AV
处使用Arial
呈现的字符9 pt
将同时具有两个函数的输出,而Arial
处的12 pt
将导致不同的输出。
以下图片中的第一行使用ExtTextOut
进行无字距调整,第二行使用DrawText
进行自动字距调整。