【问题标题】:GetFontUnicodeRanges without a windowGetFontUnicodeRanges 没有窗口
【发布时间】:2022-01-19 20:04:51
【问题描述】:

有没有机会在没有窗口的情况下拨打GetFontUnicodeRanges?例如,它可能是不允许与桌面交互的 Windows 服务。

目前我正在使用控制台应用程序对此进行测试:

program UnicodeConsoleOutput;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows;

var
  NumWritten: DWORD;
  Text: WideString;
  u8s: UTF8String;

procedure Add(AStart, AEnd: Word);
var
  i: Word;
begin
  Text := Text + WideFormat('[%x...%x]:'#13#10, [AStart, AEnd]);
  for i := AStart to AEnd do
    Text := Text + WideChar(i);
  Text := Text + WideString(#13#10#13#10);
end;

//Actually I want to get glyph ranges for "Consolas" font
procedure GetFontRanges();
type
  TRangesArray = array[0..(MaxInt div SizeOf(TWCRange)) - 1] of TWCRange;

  PRangesArray = ^TRangesArray;
const
  ConsoleTitle = '{A46DD332-0D57-4310-B91E-A68957C20429}';
var
  GS: PGlyphSet;
  GSSize: LongWord;
  i: Integer;
  rng: TWCRange;
  hConsole: HWND;
  hDev: HDC;
begin
  //A dirty hack to get console window handle suggested by Microsoft
  SetConsoleTitle(PChar(ConsoleTitle));
  hConsole := FindWindow(nil, PChar(ConsoleTitle));
  
  hDev := GetDC(hConsole);
  try

    GSSize := GetFontUnicodeRanges(hDev, nil);
    GetMem(Pointer(GS), GSSize);
    try
      GS.cbThis := GSSize;
      GS.flAccel := 0;
      GS.cGlyphsSupported := 0;
      GS.cRanges := 0;
      if GetFontUnicodeRanges(hDev, GS) <> 0 then begin
        for i := 0 to GS.cRanges - 1 do begin
          rng := PRangesArray(@GS.ranges)[i];
          Add(Word(rng.wcLow), Word(rng.wcLow) + rng.cGlyphs - 1);
        end;
      end;
    finally
      FreeMem(Pointer(GS), GSSize);
    end;
  finally
    ReleaseDC(hConsole, hDev);
  end;
end;

begin
  try
    GetFontRanges();
    SetConsoleOutputCP(CP_UTF8);
    u8s := UTF8Encode(Text);
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), PChar(u8s), Length(u8s),
      NumWritten, nil);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  ReadLn;
end.

【问题讨论】:

  • 有 Python 实用程序可以解析并提供有关 otf(以及 ttf)字体的信息。 (显然还有一个 unicode range 函数。但它在 python 中。
  • 对不起,这里是 WinAPI 和 Delphi。你的评论完全断章取义。我在问具体的 WinAPI 功能。
  • 那是相当古老的技术。您可以通过 API 没有将调用线程的最后一个错误设置为失败这一事实来判断。据推测,您需要传递 DC 的唯一原因是因为这就是您告诉 API 使用哪种字体的方式。在这种情况下,可能只需 CreateDC 一个并选择您的字体。或者,使用更现代的 API。 DirectWrite 可能有你要找的东西。
  • 附带说明,您可以使用GetConsoleWindow(),而不是使用SetConsoleTitle()+FindWindow()
  • @IInspectable "API 没有设置调用线程失败时的最后一个错误" - 大多数 GDI 函数没有。

标签: winapi unicode fonts delphi-6


【解决方案1】:

在 Windows GDI 中,您可以创建设备上下文并在其中选择字体,而无需窗口句柄。例如,

        HDC hdc = CreateDC(L"DISPLAY", NULL, NULL, NULL);
        //CreateCompatibleDC(NULL) also works

        HFONT hFont = CreateFont(
            -20, 0, 0, 0,
            FW_REGULAR,
            FALSE, FALSE, FALSE, DEFAULT_CHARSET,
            OUT_DEFAULT_PRECIS,
            CLIP_DEFAULT_PRECIS,
            DEFAULT_QUALITY,
            DEFAULT_PITCH || FF_DONTCARE,
            L"Arial"
        );

        HFONT oldFont = static_cast<HFONT>(SelectObject(hdc, hFont));

请注意,GDI 文本函数使用 UTF-16 编码,并且它们都是在 Unicode 分配任何补充平面字符之前创建的。因此,获取或返回非字符串字符列表的函数(例如 GetFontUnicodeRanges)在当今的大部分 Unicode 中都无法正常工作。 GetFontUnicodeRanges 返回指向 GLYPHSET 的指针,该指针具有 WCRANGE 结构的数组。它有一个 WCHAR 来表示一个 Unicode 字符。因此,GetFontUnicodeRanges 无法报告任何 Unicode 补充平面字符。在某些字体中,这可能是该字体支持的大多数字符。

在这方面,GDI 不仅古老,而且已经过时。对于您正在做的事情,DirectWrite 是一个更好的选择:它的所有 API 都支持所有 Unicode 字符。

你想要的 DWrite 方法是IDWriteFontFace1::GetUnicodeRanges。许多 DWrite API,包括这个,都可以在没有窗口甚至设备上下文的情况下使用。您可能希望通过调用 IDWriteFont::CreateFontFaceIDWriteFactory::CreateFontFaceIDWriteFontFaceReference::CreateFontFace 来获取 IDWriteFontFace1 对象,具体取决于您感兴趣的字体来源 — 可能是已安装字体、自定义字体集、内存 blob ,或字体文件。

【讨论】:

  • 谢谢,抱歉,Windows 7 是我的目标平台。我没有计划升级到 Windows 8/10/11 只是为了使用 IDWriteFontFace1。
  • 如果您需要继续使用 Windows 7,您可以使用 Platform Update for Windows 7 获得 DirectWrite。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-16
  • 2011-07-06
  • 2020-05-07
  • 2016-12-20
相关资源
最近更新 更多