CreateFont() 创建的字体规格和大小不正确
Incorrect metrics and sizes of font created by CreateFont()
我尝试使用 WinAPI 将字体渲染为位图,但无法达到所需的字体大小。
字体的初始化方式如下:
HDC dc = ::CreateCompatibleDC(NULL);
::SetMapMode(dc, MM_TEXT);
::SetTextAlign(dc, TA_LEFT | TA_TOP | TA_UPDATECP);
int size_in_pixels = 18;
HFONT font = ::CreateFontA(-size_in_pixels, ..., "Arial");
::SelectObject(dc, font);
::TEXTMETRICW tm = { 0 };
GetTextMetricsW(dc, &tm);
但之后我在 GetGlyphOutlineW
和 GetTextMetricsW
中得到了不正确的值,这不是我作为参数传递的大小
我知道它期望值以逻辑单位表示,但在 MM_TEXT 中 1 个单位应该是 1 个像素,不是吗?
我希望 CreateFontA
在传递负值时接受磅值(如此处 https://i.stack.imgur.com/tEt8J.png),但实际上这是错误的。
我尝试了暴力破解值,并找到了一些尺寸的合适参数:
18px = -19; 36px = -39; 73px = -78;
我也试过微软提供的公式:
nHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
但它也给我一个错误的结果,渲染的文本(使用 GetGlyphOutlineW
)如果测量它会更大(例如 'j' 的高度应该有我传递的确切大小)
GetTextMetricsW
的指标也是错误的,例如 tmAscent
。我知道在 Windows 上它包括内部领先,但即使从 tmAscent
中减去 tmInternalLeading
它仍然是不正确的。
顺便说一下,来自 GetCharABCWidthsW
的值是正确的,所以 a+b+c 是以像素为单位的字形宽度(而文档说它应该是逻辑单位)。
我还应该说一下 DPI,通常我在 Windows 10 比例的设置中使用 125%,但我什至尝试使用 100%,有趣的是 ::GetDeviceCaps(dc, LOGPIXELSY)
不随我使用的比例变化,总是 96
这是 CreateFontA(-128, ...)
的示例,其中包含最终图集和指标:
rendered atlas
问题 #1:我应该怎么做才能传递所需的点大小(以像素为单位)并接收具有正确尺寸(以像素为单位)的正确大小的字形?
问题 #2:所有这些函数使用的奇怪单位是什么?
当您使用 ::SetMapMode(dc, MM_TEXT);
时,字体大小以设备像素为单位指定。负值不包括内部行距,因此对于相同的绝对值,负值会产生视觉上更大的字体。如果你想为不同的字体从 GetTextExtentPoint32
获得相同的高度,请使用正值。
在您的高度为 -128
的示例中,您请求的字体在排除内部前导后高度为 128 像素。字体映射器选择 143
,这对于 15
像素 (128+15=143) 的内部引导是正确的。 tmAscent
+ tmDescent
也是正确的(115+28=143)。你得到你指定的。
您应该考虑到文本度量中的值没有规定硬界限。设计师可以设计字体,使其字形有时超出引导线或达不到引导线。
for example height of 'j' should have exact size that I passed
j
上的点可以超出或达不到顶线,如果设计师认为这样设计在视觉上是合理的。
interesting that ::GetDeviceCaps(dc, LOGPIXELSY)
not changing with scale I using, it's always 96
除非您注销再登录,否则系统 DPI 不会改变。对于每个监视器 DPI 感知应用程序,您必须从 WM_DPICHANGED
.
给出的监视器参数或缓存值中获取 DPI
Question #1: What should I do to pass wanted point size in pixels and receive glyphs in proper size with correct metrics in pixels?
我认为您想获得顶线和底线之间的特定距离,这正是您创建字体的方式 HFONT font = ::CreateFontA(-size_in_pixels, ..., "Arial");
。问题在于您假设字体设计线是每个字形的硬边界,但字体设计者不必将字形严格对齐到这些线。如果你想要字形严格对齐,可能没有办法得到它。也许检查不同的字体。
Question #2: What the strange units all these functions are using?
当模式设置为 WM_TEXT
时,使用原始设备像素。正高度指定高度包括tmInternalLeading
,负高度不包括它。
对于正值:
tmAscent + tmDescent = requestedHeight
对于负值:
tmAscent + tmDescent - tmInternalLeading = requestedHeight
下面我粘贴了不同字体的屏幕截图,显示根据所选字体的字形可以设计成不会达到顶线或超出顶线,并且在大多数情况下也不会达到底线。
似乎 Arial Unicode MS 更适合您的要求(但 j 仍然没有到达您想要的位置)。
宋体:
Arial Unicode MS
输入单声道
Trebuched MS
我尝试使用 WinAPI 将字体渲染为位图,但无法达到所需的字体大小。
字体的初始化方式如下:
HDC dc = ::CreateCompatibleDC(NULL);
::SetMapMode(dc, MM_TEXT);
::SetTextAlign(dc, TA_LEFT | TA_TOP | TA_UPDATECP);
int size_in_pixels = 18;
HFONT font = ::CreateFontA(-size_in_pixels, ..., "Arial");
::SelectObject(dc, font);
::TEXTMETRICW tm = { 0 };
GetTextMetricsW(dc, &tm);
但之后我在 GetGlyphOutlineW
和 GetTextMetricsW
中得到了不正确的值,这不是我作为参数传递的大小
我知道它期望值以逻辑单位表示,但在 MM_TEXT 中 1 个单位应该是 1 个像素,不是吗?
我希望 CreateFontA
在传递负值时接受磅值(如此处 https://i.stack.imgur.com/tEt8J.png),但实际上这是错误的。
我尝试了暴力破解值,并找到了一些尺寸的合适参数:
18px = -19; 36px = -39; 73px = -78;
我也试过微软提供的公式:
nHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
但它也给我一个错误的结果,渲染的文本(使用 GetGlyphOutlineW
)如果测量它会更大(例如 'j' 的高度应该有我传递的确切大小)
GetTextMetricsW
的指标也是错误的,例如 tmAscent
。我知道在 Windows 上它包括内部领先,但即使从 tmAscent
中减去 tmInternalLeading
它仍然是不正确的。
顺便说一下,来自 GetCharABCWidthsW
的值是正确的,所以 a+b+c 是以像素为单位的字形宽度(而文档说它应该是逻辑单位)。
我还应该说一下 DPI,通常我在 Windows 10 比例的设置中使用 125%,但我什至尝试使用 100%,有趣的是 ::GetDeviceCaps(dc, LOGPIXELSY)
不随我使用的比例变化,总是 96
这是 CreateFontA(-128, ...)
的示例,其中包含最终图集和指标:
rendered atlas
问题 #1:我应该怎么做才能传递所需的点大小(以像素为单位)并接收具有正确尺寸(以像素为单位)的正确大小的字形?
问题 #2:所有这些函数使用的奇怪单位是什么?
当您使用 ::SetMapMode(dc, MM_TEXT);
时,字体大小以设备像素为单位指定。负值不包括内部行距,因此对于相同的绝对值,负值会产生视觉上更大的字体。如果你想为不同的字体从 GetTextExtentPoint32
获得相同的高度,请使用正值。
在您的高度为 -128
的示例中,您请求的字体在排除内部前导后高度为 128 像素。字体映射器选择 143
,这对于 15
像素 (128+15=143) 的内部引导是正确的。 tmAscent
+ tmDescent
也是正确的(115+28=143)。你得到你指定的。
您应该考虑到文本度量中的值没有规定硬界限。设计师可以设计字体,使其字形有时超出引导线或达不到引导线。
for example height of 'j' should have exact size that I passed
j
上的点可以超出或达不到顶线,如果设计师认为这样设计在视觉上是合理的。
interesting that
::GetDeviceCaps(dc, LOGPIXELSY)
not changing with scale I using, it's always 96
除非您注销再登录,否则系统 DPI 不会改变。对于每个监视器 DPI 感知应用程序,您必须从 WM_DPICHANGED
.
Question #1: What should I do to pass wanted point size in pixels and receive glyphs in proper size with correct metrics in pixels?
我认为您想获得顶线和底线之间的特定距离,这正是您创建字体的方式 HFONT font = ::CreateFontA(-size_in_pixels, ..., "Arial");
。问题在于您假设字体设计线是每个字形的硬边界,但字体设计者不必将字形严格对齐到这些线。如果你想要字形严格对齐,可能没有办法得到它。也许检查不同的字体。
Question #2: What the strange units all these functions are using?
当模式设置为 WM_TEXT
时,使用原始设备像素。正高度指定高度包括tmInternalLeading
,负高度不包括它。
对于正值:
tmAscent + tmDescent = requestedHeight
对于负值:
tmAscent + tmDescent - tmInternalLeading = requestedHeight
下面我粘贴了不同字体的屏幕截图,显示根据所选字体的字形可以设计成不会达到顶线或超出顶线,并且在大多数情况下也不会达到底线。
似乎 Arial Unicode MS 更适合您的要求(但 j 仍然没有到达您想要的位置)。
宋体:
Arial Unicode MS
输入单声道
Trebuched MS