Is there a chance to call GetFontUnicodeRanges
without a window? For example, it could be a Windows service not permitted to interact with desktop.
Currently I am testing this with console application:
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.
In Windows GDI, you can create a device context and select a font into it without needing a handle to a window. E.g.,
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));
Note that GDI text functions use the UTF-16 encoding, and all of them were created before Unicode had assigned any supplementary-plane characters. As a result, functions that take or return lists of characters that are not a string, such as GetFontUnicodeRanges, don't work well for much of Unicode today. GetFontUnicodeRanges returns a pointer to a GLYPHSET, which has an array of WCRANGE structs. That has a single WCHAR to represent a Unicode character. As a result, GetFontUnicodeRanges has no way to report any Unicode supplementary-plane characters. In some fonts, that might be the majority of characters supported in the font.
In this regard, GDI is not just ancient, but also obsolete. For what you're doing, DirectWrite is a better option: all of its APIs support all Unicode characters.
The DWrite method you want is IDWriteFontFace1::GetUnicodeRanges. Many DWrite APIs, including this, can be used without a window or even a device context. You'll probably want to obtain the IDWriteFontFace1 object by calling IDWriteFont::CreateFontFace, IDWriteFactory::CreateFontFace or IDWriteFontFaceReference::CreateFontFace depending upon the source of the font you're interested in—could be an installed font, a custom font set, a memory blob, or a font file.