You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MFC生成PDF嵌入TrueType字体:如何获取Glyph Widths?

嘿,我正好处理过类似的PDF字体嵌入问题,咱们来一步步解决你的困惑:

核心问题拆解

你提到的Adobe公式是完全正确的:(advance * 1000) / unitsPerEm,这个公式是为了把字体设计空间的原始宽度值转换成PDF要求的「千分比宽度」(PDF字体默认以1000为基准单位)。现在的问题是怎么拿到正确的advance(字形前进宽度)和unitsPerEm,以及理解缩放的逻辑。

第一步:获取字体的unitsPerEm

unitsPerEm是TrueType字体的核心设计参数,代表字体设计空间的基准单位大小(比如大部分字体是1000)。你可以用WinAPI的GetOutlineTextMetrics直接获取:

// 示例代码:获取目标字体的unitsPerEm
HDC hdc = GetDC(NULL);
HFONT hOriginalFont = (HFONT)SelectObject(hdc, yourFontHandle); // 替换成你的字体句柄

// 先获取所需缓冲区大小
DWORD otmBufferSize = GetOutlineTextMetrics(hdc, 0, NULL);
OUTLINETEXTMETRIC* pOtm = (OUTLINETEXTMETRIC*)malloc(otmBufferSize);

if (GetOutlineTextMetrics(hdc, otmBufferSize, pOtm)) {
    UINT unitsPerEm = pOtm->otmEMSquare; // 这就是我们要的unitsPerEm
    // 可以把这个值保存下来供后续计算使用
}

// 清理资源
SelectObject(hdc, hOriginalFont);
ReleaseDC(NULL, hdc);
free(pOtm);

第二步:获取字体设计空间的Advance Width

你之前用的GetCharWidthFloat返回的是当前DC字体字号下的设备无关宽度,不是字体原始设计空间的宽度。要拿到设计空间的advance,有两种可靠的方法:

方法一:用GetGlyphOutline(推荐,直接拿原始设计值)

GetGlyphOutline可以直接获取字体设计空间的字形度量,指定GGO_METRICS参数即可:

// 示例:获取字符'A'的设计空间advance宽度
GLYPHMETRICSFLOAT gmf = {0};
MAT2 identityMatrix = {0};
// 设置单位矩阵,不做任何缩放
identityMatrix.eM11.value = 1; identityMatrix.eM11.fract = 0;
identityMatrix.eM22.value = 1; identityMatrix.eM22.fract = 0;

HDC hdc = GetDC(NULL);
SelectObject(hdc, yourFontHandle);

DWORD result = GetGlyphOutline(hdc, 'A', GGO_METRICS, &gmf, 0, NULL, &identityMatrix);
if (result != GDI_ERROR) {
    float designAdvance = gmf.gmfCellIncX; // 字体设计空间的前进宽度
    float pdfWidth = (designAdvance * 1000.0f) / unitsPerEm; // 转换为PDF要求的千分比宽度
    // 这个pdfWidth就是你要写入PDF字体对象的Glyph Width值
}

ReleaseDC(NULL, hdc);

方法二:用GetCharWidthFloat(需先创建设计单位字体)

如果你更习惯用GetCharWidthFloat,可以先创建一个以字体设计单位为基准的字体(把字体高度设为-unitsPerEm,负数代表使用设计单位),再调用函数:

// 示例:创建设计单位字体并获取宽度
HFONT hDesignFont = CreateFont(
    -unitsPerEm, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
    DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS,
    CLEARTYPE_QUALITY, VARIABLE_PITCH | FF_DONTCARE,
    yourFontName // 替换成你的字体名称
);

HDC hdc = GetDC(NULL);
HFONT hOldFont = (HFONT)SelectObject(hdc, hDesignFont);

float charWidth;
GetCharWidthFloat(hdc, 'A', 'A', &charWidth);
float pdfWidth = (charWidth * 1000.0f) / unitsPerEm;

// 清理资源
SelectObject(hdc, hOldFont);
DeleteObject(hDesignFont);
ReleaseDC(NULL, hdc);

有没有直接返回PDF宽度的WinAPI?

很遗憾,没有直接返回PDF所需千分比宽度的WinAPI,但上面两种方法组合起来已经非常高效了,而且完全依赖系统原生API,稳定性有保障。

另外补充一点:如果你的PDF需要处理完整字符集,记得循环遍历所需的字符(或字形索引),用GetCharIndex把字符编码转换成字形索引(避免字符和字形不一一对应的情况),再批量获取宽度值。

内容的提问来源于stack exchange,提问作者Penachia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:04:46