如何检查字体支持哪些字符集(代码页)?(有哪些字母可以显示)?

8

我需要展示系统字体列表,但需要过滤掉不支持20种预定义语言(硬编码)的字体,并仅显示支持这些语言的字体。

我可以通过调用Vcl.Forms.Screen.Fonts获得可用字体列表。
从列表中只知道字体名称,我如何检查该字体支持哪些字符集(代码页)(拥有实际字母)?

例如,常见字体如Arial或Times New Roman几乎包含所有欧洲语言的字符,包括西里尔文(以及中文等)。然而,许多不太常见的字体通常只包含英文字母。

这个应用程序是为内部使用而设计的,因此具有一个查询字体是否具有某个特定字符集/代码页的某个字母的功能(例如ФЎξ),并且不会被另一个通用字体的字母替代(或某个占位符)就足够了。


问题在于,人们无法确信。一种字体可能拥有各种各样的字形,而恰好你最需要的那个字形可能会缺失。因此,在我看来,这确实需要逐个字符进行测试。到目前为止,我能找到的唯一的东西是 ScriptGetCMap,但我不确定这是否是你想要使用的。 - JensG
2个回答

11

GetGlyphIndices函数可用于确定字体中是否存在字形。

引用MSDN文档:

DWORD GetGlyphIndices(
  _In_  HDC     hdc,
  _In_  LPCTSTR lpstr,
  _In_  int     c,
  _Out_ LPWORD  pgi,
  _In_  DWORD   fl
);

参数[...]

fl [输入]:指定如果不支持字形应如何处理。此参数可以是以下值。

GGI_MARK_NONEXISTING_GLYPHS -- 将不支持的字形标记为十六进制值0xffff。

备注部分再次链接到Uniscribe函数,例如ScriptGetCMap

此函数尝试为lpstr所指向的字符串中的每个字符标识一个单一的字形表示。虽然这对于某些低级别目的(如操作字体文件)很有用,但希望将字符串映射到字形的高级应用程序通常希望使用Uniscribe函数。

由于两种API都从Win2k开始受支持,因此使用哪种可能只是个人喜好问题。

(编辑:刚刚注意到导入已经在Windows.pas中)

示例代码

procedure Test( dc : HDC);
var str : UnicodeString;
    buf : array of WORD;
    len,i : Integer;
    count : DWORD;
begin
  str := 'abc'+WideChar($0416)+'äöü';
  len := Length(str);
  SetLength( buf, len);
  count := GetGlyphIndicesW( dc, PWideChar(str), len, @buf[0], GGI_MARK_NONEXISTING_GLYPHS);
  if count > 0 then begin
    for i := 0 to count-1 do begin
      Write('index ',i,': ');
      if buf[i] = $FFFF
      then Writeln('glyph missing')
      else Writeln('ok');
    end;
  end;
end;
产出。
index 0: ok
index 1: ok
index 2: ok
index 3: glyph missing
index 4: ok
index 5: ok
index 6: ok

2
你正在泄露 GetDC(0) 返回的 HDC。在使用完后,你需要释放它。dc := GetDC(0); GetGlyphIndicesW(dc,...); ReleaseDC(0, dc); - Remy Lebeau
1
不再是了。这只是一个样本。谁在乎呢。 - JensG
@JensG 我还要补充一点,DC 需要分配字体,然后它才能适用于该字体。否则,代码示例会有点误导 - 检查 DC 是否具有字符,但没有在任何地方使用字体名称 ;-) - Kromster

2
如果您想检查整个字符集的支持情况,可以使用Windows API中的EnumFontFamiliesEx - 这不允许您查询单个字体,而是返回支持给定字符集(或具有任何其他可查询特征)的已安装字体列表。
您需要一个适当类型的回调函数
function EnumFontCallback(lpelfe : PLogFont;
                          lpntme : PNewTextMetricEX;
                          FontType : DWORD;
                          lp : LPARAM) : integer; stdcall;
begin
  TMemo(lp).Lines.Add(lpelfe^.lfFaceName);
  result := 1;  // return zero to end enumeration
end;

然后称之为:

procedure TForm1.Button1Click(Sender: TObject);
var
  lf : TLogFont;
begin
  ZeroMemory(@lf,SizeOf(TLogFont));

  lf.lfCharSet := CHINESEBIG5_CHARSET;

  if not EnumFontFamiliesEx(Canvas.Handle,      // HDC
                            lf,                 // TLogFont
                            @EnumFontCallback,  // Callback Pointer
                            NativeInt(Memo1),   // user supplied pointer
                            0) then             // must be zero
  begin
    // function call failed.
  end;
end;

通过 TLogFont (MSDN) 结构中的各个字段,您可以查询各种字体特性。在本例中,我仅限制了字符集(在上面的示例中为中文大五码)。
回调函数将为从查询返回的每个结果字体触发一次。您需要管理收集返回的信息。要为多个字符集添加限制,您需要针对每个感兴趣的字符集调用 EnumFontFamiliesEx 一次。以下常量在 RTL Windows 单元中定义:
ANSI_CHARSET
BALTIC_CHARSET
CHINESEBIG5_CHARSET
DEFAULT_CHARSET      // depends on system locale
EASTEUROPE_CHARSET
GB2312_CHARSET
GREEK_CHARSET
HANGUL_CHARSET
MAC_CHARSET
OEM_CHARSET          // depends on OS
RUSSIAN_CHARSET
SHIFTJIS_CHARSET
SYMBOL_CHARSET
TURKISH_CHARSET
VIETNAMESE_CHARSET
JOHAB_CHARSET
ARABIC_CHARSET
HEBREW_CHARSET
THAI_CHARSET 

交叉引用将由您来完成 - 使用一个 TDictionary 似乎是管理这个任务的明智工具。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接