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

如何在Lazarus/Delphi中判断字体是否含指定Unicode字符字形?

我明白你在Lazarus/Delphi里判断字体是否包含特定Unicode字形的需求——之前用C#、PHP的方案没法直接套用确实头疼。下面给你几个更可靠的方法,不管是原生API调用还是改进版的迂回方案都有:


一、Windows平台(Delphi/Lazarus通用):用原生API精准判断

Windows提供了直接的API来检查字体对字符的支持,这是最准确的方案,没有迂回方法的不确定性。

1. 核心方法:结合GetGlyphIndicesW和GetFontUnicodeRanges

GetGlyphIndicesW可以获取字符在字体中的字形索引,GetFontUnicodeRanges能列出字体支持的所有Unicode区间,两者结合可以避免误判(比如有些字体用默认字形替代缺失字符的情况)。

示例代码:

uses
  Windows, Graphics;

// 检查字符是否在字体支持的Unicode区间内
function IsCharInFontUnicodeRange(ACanvas: TCanvas; AChar: WideChar): Boolean;
var
  BufferSize: DWORD;
  pRanges: PUnicodeRanges;
  i: Integer;
  CharCode: DWORD;
begin
  Result := False;
  CharCode := DWORD(AChar);
  
  // 获取存储字体Unicode区间所需的缓冲区大小
  BufferSize := GetFontUnicodeRanges(ACanvas.Handle, nil);
  if BufferSize = 0 then Exit;
  
  GetMem(pRanges, BufferSize);
  try
    pRanges^.cRanges := BufferSize div SizeOf(TUnicodeRange);
    if GetFontUnicodeRanges(ACanvas.Handle, pRanges) = 0 then Exit;
    
    // 遍历所有区间,检查字符是否在范围内
    for i := 0 to pRanges^.cRanges - 1 do
    begin
      if (CharCode >= pRanges^.ranges[i].wcLow) and
         (CharCode <= pRanges^.ranges[i].wcLow + pRanges^.ranges[i].cGlyphs - 1) then
      begin
        Result := True;
        Break;
      end;
    end;
  finally
    FreeMem(pRanges);
  end;
end;

// 最终判断:字符是否有对应的字形
function IsCharHasGlyphInFont(ACanvas: TCanvas; AChar: WideChar): Boolean;
var
  GlyphIndex: DWORD;
begin
  // 先确认字符在字体的Unicode支持范围内,再检查字形索引
  Result := IsCharInFontUnicodeRange(ACanvas, AChar);
  if Result then
  begin
    // GGO_GLYPHINDEX参数表示只返回原始字形索引,不使用系统替代字符
    GlyphIndex := GetGlyphIndicesW(ACanvas.Handle, PWideChar(@AChar), 1, GGO_GLYPHINDEX);
    Result := GlyphIndex <> 0;
  end;
end;

使用方式:

比如要检查你的TLabel字体是否支持某个箭头字符:

var
  ArrowChar: WideChar;
begin
  ArrowChar := WideChar($2192); // 右箭头字符
  if IsCharHasGlyphInFont(Label1.Canvas, ArrowChar) then
    Label1.Caption := ArrowChar
  else
    Label1.Caption := '字体不支持该箭头';
end;

二、Lazarus跨平台方案(Linux/macOS/Windows):用Pango API

如果你的项目需要跨平台运行,Lazarus基于GTK的环境可以直接调用Pango的字体检查接口,这是Linux/macOS下的标准方案。

示例代码:

uses
  Pango, PangoFc, Gtk2, Graphics;

function IsCharSupportedInFontPango(AFont: TFont; AChar: WideChar): Boolean;
var
  PangoFont: PPangoFont;
  PangoFcFont: PPangoFcFont;
  FcCharSet: PFcCharSet;
  Context: PPangoContext;
begin
  Result := False;
  
  // 获取当前控件的Pango上下文
  Context := PangoWidgetGetContext(Widget); // Widget为你的控件实例,比如Label1.Handle转换
  if not Assigned(Context) then Exit;
  
  // 获取对应字体的Pango对象
  PangoFont := PangoContextGetFont(Context, nil);
  if Assigned(PangoFont) then
  begin
    PangoFcFont := PPangoFcFont(PangoFont);
    FcCharSet := PangoFcFontGetCharSet(PangoFcFont);
    if Assigned(FcCharSet) then
    begin
      // 检查字符是否在字体的字符集中
      Result := FcCharSetHasChar(FcCharSet, DWORD(AChar));
    end;
    g_object_unref(PangoFont);
  end;
end;

三、改进版迂回方案(替代你当前的尺寸对比)

如果因为某些限制没法用API,你可以优化现有的方案,避免误判:

  1. 不用U+0001(部分字体可能对这个控制字符有特殊处理),改用几乎所有字体都不支持的生僻字符,比如U+10FFFF(Unicode最大码点)作为参照。
  2. 若尺寸相同,进一步做像素级对比,彻底排除“缺失字形尺寸恰好和目标字符一致”的极端情况。

示例代码:

uses
  Windows, Graphics;

function IsCharHasGlyphWithFallback(ACanvas: TCanvas; AChar: WideChar): Boolean;
const
  // 选择一个几乎所有字体都不支持的参照字符
  UnsupportChar = WideChar($10FFFF);
var
  TargetSize, UnsupportSize: TSize;
  MemDC: HDC;
  Bmp: HBITMAP;
  OldBmp: HGDIOBJ;
  TargetPixels, UnsupportPixels: array of Byte;
begin
  ACanvas.Font := ACanvas.Font; // 确保字体设置生效
  
  // 第一步:对比尺寸
  GetTextExtentPoint32W(ACanvas.Handle, PWideChar(@AChar), 1, TargetSize);
  GetTextExtentPoint32W(ACanvas.Handle, PWideChar(@UnsupportChar), 1, UnsupportSize);
  Result := (TargetSize.cx <> UnsupportSize.cx) or (TargetSize.cy <> UnsupportSize.cy);
  
  // 第二步:尺寸相同时,做像素级对比
  if not Result then
  begin
    MemDC := CreateCompatibleDC(ACanvas.Handle);
    Bmp := CreateCompatibleBitmap(ACanvas.Handle, TargetSize.cx, TargetSize.cy);
    OldBmp := SelectObject(MemDC, Bmp);
    try
      // 绘制目标字符并提取像素
      FillRect(MemDC, Rect(0,0,TargetSize.cx,TargetSize.cy), GetStockObject(WHITE_BRUSH));
      SetTextColor(MemDC, clBlack);
      SetBkColor(MemDC, clWhite);
      DrawTextW(MemDC, PWideChar(@AChar), 1, Rect(0,0,TargetSize.cx,TargetSize.cy), DT_LEFT or DT_TOP);
      SetLength(TargetPixels, TargetSize.cx * TargetSize.cy * 4);
      GetDIBits(MemDC, Bmp, 0, TargetSize.cy, @TargetPixels[0], nil, DIB_RGB_COLORS);
      
      // 绘制参照字符并提取像素
      FillRect(MemDC, Rect(0,0,TargetSize.cx,TargetSize.cy), GetStockObject(WHITE_BRUSH));
      DrawTextW(MemDC, PWideChar(@UnsupportChar), 1, Rect(0,0,TargetSize.cx,TargetSize.cy), DT_LEFT or DT_TOP);
      SetLength(UnsupportPixels, TargetSize.cx * TargetSize.cy * 4);
      GetDIBits(MemDC, Bmp, 0, TargetSize.cy, @UnsupportPixels[0], nil, DIB_RGB_COLORS);
      
      // 对比像素数据
      Result := not CompareMem(@TargetPixels[0], @UnsupportPixels[0], Length(TargetPixels));
    finally
      SelectObject(MemDC, OldBmp);
      DeleteObject(Bmp);
      DeleteDC(MemDC);
    end;
  end;
end;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:00:22