Символ
?
фактически не поддерживается в шрифте Windows 10 Arial. Windows 10 использует "Segoe UI Emoji"
в качестве резервного шрифта для этой конкретной кодовой точки.
Итак, сначала мы должны выяснить, используется ли резервный шрифт. Затем проверьте индекс глифа, чтобы увидеть, является ли он символом тофу (обычно отображается в виде квадратного знака ▯
)
Мы можем использовать метафайл, чтобы определить, используется ли замена шрифта. Выберите этот шрифт в HDC
.
Используйте ScriptGetFontProperties
, чтобы найти значения для неподдерживаемых глифов.
Используйте GetCharacterPlacement
, чтобы найти индексы глифов для строки. Если индекс глифа совпадает с неподдерживаемыми глифами, то кодовая точка печатается как тофу ▯
.
Edit:
Если вы пытаетесь напечатать китайский и т. Д., То вам нужно выбрать соответствующий шрифт (SimSun для китайского)
Эта часть сделана IMLangFontLink
. Это другой тип замены шрифта.
В приведенном ниже примере будет проверяться наличие единой кодовой точки (ее можно расширить для обработки строки).
Если выбран шрифт Segoe UI
, то для китайского символа 请
он переключится с Segoe UI
на SimSun
.
Для смайликов переключится Segoe UI
на Segoe UI Emoji
См. Также эту статью в oldnewthing . Обратите внимание, что статья в OldNewThing не обрабатывает Emojis, она просто позволяет TextOut
обрабатывать его (что правильно обрабатывается в Windows 10, поэтому результат выглядит нормально)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <windows.h>
#include <usp10.h>
#include <AtlBase.h>
#include <AtlCom.h>
#include <mlang.h>
#pragma comment(lib, "Usp10.lib")
int CALLBACK metafileproc(HDC, HANDLETABLE*, const ENHMETARECORD *record,
int, LPARAM logfont)
{
if(record->iType == EMR_EXTCREATEFONTINDIRECTW)
{
auto ptr = (const EMREXTCREATEFONTINDIRECTW*)record;
*(LOGFONT*)logfont = ptr->elfw.elfLogFont;
}
return 1;
}
HFONT GetFallbackFont(const wchar_t *str, HFONT hfont_test)
{
//use metafile to find the fallback font
auto metafile_hdc = CreateEnhMetaFile(NULL, NULL, NULL, NULL);
auto metafile_oldfont = SelectObject(metafile_hdc, hfont_test);
SCRIPT_STRING_ANALYSIS ssa;
ScriptStringAnalyse(metafile_hdc, str, wcslen(str), 0, -1,
SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,
0, NULL, NULL, NULL, NULL, NULL, &ssa);
ScriptStringOut(ssa, 0, 0, 0, NULL, 0, 0, FALSE);
ScriptStringFree(&ssa);
SelectObject(metafile_hdc, metafile_oldfont);
auto hmetafile = CloseEnhMetaFile(metafile_hdc);
LOGFONT logfont = { 0 };
EnumEnhMetaFile(0, hmetafile, metafileproc, &logfont, NULL);
wprintf(L"Selecting fallback font: %s\n", logfont.lfFaceName);
HFONT hfont = CreateFontIndirect(&logfont);
DeleteEnhMetaFile(hmetafile);
return hfont;
}
//IsTofu is for testing emojis
//It accepts a Unicode string
bool IsTofuError(HDC hdc, HFONT hfont_test, const wchar_t *str)
{
if(wcsstr(str, L" "))
{
wprintf(L"*** cannot test strings containing blank space\n");
}
auto hfont = GetFallbackFont(str, hfont_test);
auto oldfont = SelectObject(hdc, hfont);
//find the characters not supported in this font
//note, blank space is blank, unsupported fonts can be blank also
SCRIPT_CACHE sc = NULL;
SCRIPT_FONTPROPERTIES fp = { sizeof(fp) };
ScriptGetFontProperties(hdc, &sc, &fp);
ScriptFreeCache(&sc);
wprintf(L"SCRIPT_FONTPROPERTIES:\n");
wprintf(L" Blank: %d, Default: %d, Invalid: %d\n",
fp.wgBlank, fp.wgDefault, fp.wgInvalid);
// Get glyph indices for the string
GCP_RESULTS gcp_results = { sizeof(GCP_RESULTS) };
gcp_results.nGlyphs = wcslen(str);
auto wstr_memory = (wchar_t*)calloc(wcslen(str) + 1, sizeof(wchar_t));
gcp_results.lpGlyphs = wstr_memory;
GetCharacterPlacement(hdc, str, wcslen(str), 0, &gcp_results, GCP_GLYPHSHAPE);
//check the characters against wgBlank...
bool istofu = false;
wprintf(L"Glyphs:");
for(UINT i = 0; i < gcp_results.nGlyphs; i++)
{
wchar_t n = gcp_results.lpGlyphs[i];
wprintf(L"%d,", (int)n);
if(n == fp.wgBlank || n == fp.wgInvalid || n == fp.wgDefault)
istofu = true;
}
wprintf(L"\n");
free(wstr_memory);
SelectObject(hdc, oldfont);
DeleteObject(hfont);
if (istofu)
wprintf(L"Tofu error\n\n");
return istofu;
}
//get_font_link checks if there is font substitution,
//this usually applies to Asian fonts
//Note, this function doesn't accept a unicode string
//it only takes a single code point. You can imrpove it to accept strings
bool get_font_link(const wchar_t *single_codepoint,
HDC hdc,
HFONT &hfont_src,
HFONT &hfont_dst,
CComPtr<IMLangFontLink> &ifont,
CComPtr<IMLangCodePages> &icodepages)
{
DWORD codepages_dst[100] = { 0 };
LONG codepages_count = 100;
DWORD codepages = 0;
if(FAILED(icodepages->GetStrCodePages(single_codepoint, wcslen(single_codepoint),
0, codepages_dst, &codepages_count)))
return false;
codepages = codepages_dst[0];
if(FAILED(ifont->MapFont(hdc, codepages_dst[0], hfont_src, &hfont_dst)))
return false;
SelectObject(hdc, hfont_dst);
wchar_t buf[100];
GetTextFace(hdc, _countof(buf), buf);
wprintf(L"get_font_link:\nSelecting a different font: %s\n", buf);
return true;
}
int main()
{
CoInitialize(NULL);
{
CComPtr<IMultiLanguage> imultilang;
CComPtr<IMLangFontLink> ifont;
CComPtr<IMLangCodePages> icodepages;
if(FAILED(imultilang.CoCreateInstance(CLSID_CMultiLanguage))) return 0;
if(FAILED(imultilang->QueryInterface(&ifont))) return 0;
if(FAILED(imultilang->QueryInterface(&icodepages))) return 0;
//const wchar_t *single_codepoint = L"a";
//const wchar_t *single_codepoint = L"请";
const wchar_t *single_codepoint = L"?";
auto hdc = GetDC(0);
auto memdc = CreateCompatibleDC(hdc);
auto hbitmap = CreateCompatibleBitmap(hdc, 1, 1);
auto oldbmp = SelectObject(memdc, hbitmap);
auto hfont_src = CreateFont(10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Segoe UI");
auto oldfont = SelectObject(hdc, hfont_src);
HFONT hfont_dst = NULL;
if(IsTofuError(hdc, hfont_src, single_codepoint))
{
if(!get_font_link(
single_codepoint, memdc, hfont_src, hfont_dst, ifont, icodepages))
wprintf(L"Can't find a substitution!\n");
}
SelectObject(memdc, oldbmp);
SelectObject(memdc, oldfont);
DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(0, hdc);
DeleteObject(hfont_src);
if(ifont && hfont_dst)
ifont->ReleaseFont(hfont_dst);
}
CoUninitialize();
return 0;
}
Выход:
IsTofu
- это false
для Windows 10.
Это будет true
для некоторых старых версий Windows. Но это не проверено в WinXP
Используя GetUniscribeFallbackFont
из эту ссылку
Обратите внимание, что документация Windows описывает GetCharacterPlacement
как устаревшую, она рекомендует использовать функции Uniscribe. Но я не знаю, какую замену использовать здесь.