#include <string>
#include <set>
#include <vector>
#include <iostream>
#include <filesystem>
#include <windows.h>
#include <fstream>
#include <algorithm>
using namespace std;
std::unique_ptr<std::string> GetWinFontData(std::wstring font_name);
std::unique_ptr<std::string> GetWinFontData(const LOGFONTW& infont);
bool GetWinFontData(std::string& buffer, HDC hdc, HFONT hf);
void GetFontDataTTC(std::string& buffer, const std::string& file_buffer, const std::string& ttc_buffer);
void SaveFontToFile(const std::string& font_data, const std::wstring& font_name);
void ListInstalledFonts();
std::unique_ptr<std::string> GetWinFontData(std::wstring font_name)
{
if (font_name.length() >= LF_FACESIZE)
return nullptr;
LOGFONTW lf = {};
memset(lf.lfFaceName, 0, LF_FACESIZE * sizeof(wchar_t));
wcsncpy_s(lf.lfFaceName, font_name.c_str(), (std::min)(font_name.length(), (size_t)(LF_FACESIZE - 1)));
return GetWinFontData(lf);
}
std::unique_ptr<std::string> GetWinFontData(const LOGFONTW& infont)
{
bool success = false;
std::string buffer;
buffer.clear();
HDC hdc = ::CreateCompatibleDC(nullptr);
HFONT hf = CreateFontIndirectW(&infont);
if (hf != nullptr)
{
success = GetWinFontData(buffer, hdc, hf);
DeleteObject(hf);
}
ReleaseDC(0, hdc);
if (success)
return std::make_unique<std::string>(std::move(buffer));
else
return nullptr;
}
bool GetWinFontData(std::string& buffer, HDC hdc, HFONT hf)
{
HGDIOBJ oldFont = SelectObject(hdc, hf);
bool sucess = false;
constexpr DWORD ttcf_const = 0x66637474;
unsigned fileLen = GetFontData(hdc, 0, 0, nullptr, 0);
unsigned ttcLen = GetFontData(hdc, ttcf_const, 0, nullptr, 0);
if (fileLen != GDI_ERROR)
{
if (ttcLen == GDI_ERROR)
{
buffer.resize(fileLen);
sucess = GetFontData(hdc, 0, 0, const_cast<char*>(buffer.data()), (DWORD)fileLen) != GDI_ERROR;
}
else
{
std::string fileBuffer;
fileBuffer.resize(fileLen);
if (GetFontData(hdc, 0, 0, const_cast<char*>(fileBuffer.data()), fileLen) == GDI_ERROR)
{
sucess = false;
goto Exit;
}
std::string ttcBuffer;
ttcBuffer.resize(ttcLen);
if (GetFontData(hdc, ttcf_const, 0, const_cast<char*>(ttcBuffer.data()), ttcLen) == GDI_ERROR)
{
sucess = false;
goto Exit;
}
GetFontDataTTC(buffer, fileBuffer, ttcBuffer);
sucess = true;
}
}
Exit:
SelectObject(hdc, oldFont);
return sucess;
}
void GetFontDataTTC(std::string& buffer, const std::string& file_buffer, const std::string& ttc_buffer)
{
uint16_t numTables = _byteswap_ushort(*(uint16_t*)(file_buffer.data() + 4));
unsigned outLen = 12 + 16 * numTables;
const char* entry = file_buffer.data() + 12;
for (unsigned i = 0; i < numTables; i++)
{
uint32_t length = _byteswap_ulong(*(uint32_t*)(entry + 12));
length = (length + 3) & ~3;
entry += 16;
outLen += length;
}
buffer.resize(outLen);
memcpy(const_cast<char*>(buffer.data()), file_buffer.data(), 12 + 16 * numTables);
uint32_t dstDataOffset = 12 + 16 * numTables;
const char* srcEntry = file_buffer.data() + 12;
char* dstEntry = const_cast<char*>(buffer.data()) + 12;
for (unsigned i = 0; i < numTables; i++)
{
uint32_t offset = _byteswap_ulong(*(uint32_t*)(srcEntry + 8));
uint32_t length = _byteswap_ulong(*(uint32_t*)(srcEntry + 12));
length = (length + 3) & ~3;
*(uint32_t*)(dstEntry + 8) = _byteswap_ulong(dstDataOffset);
memcpy(const_cast<char*>(buffer.data()) + dstDataOffset, ttc_buffer.data() + offset, length);
dstDataOffset += length;
srcEntry += 16;
dstEntry += 16;
}
}
void SaveFontToFile(const std::string& font_data, const std::wstring& font_name)
{
try
{
wstring output_path = L"D:\\Unit_Test\\" + font_name + L".ttf";
CreateDirectoryW(L"output", NULL);
ofstream file(output_path, ios::binary);
if (!file.is_open())
{
wcerr << L"无法创建文件: " << output_path << endl;
return;
}
file.write(font_data.data(), font_data.size());
file.close();
wcout << L"字体已保存到: " << output_path << endl;
}
catch (const exception& e)
{
cerr << "Error saving font file: " << e.what() << endl;
}
}
void ListInstalledFonts()
{
HDC hdc = GetDC(NULL);
LOGFONTW lf = { 0 };
lf.lfCharSet = DEFAULT_CHARSET;
EnumFontFamiliesExW(hdc, &lf, [](const LOGFONTW* lf, const TEXTMETRICW*, DWORD, LPARAM lParam) -> int {
wcout << L"Font: " << lf->lfFaceName << endl;
return 1;
}, 0, 0);
ReleaseDC(NULL, hdc);
}
int main()
{
ListInstalledFonts();
auto data1 = GetWinFontData(L"Arial");
if (data1)
SaveFontToFile(*data1, L"Arial");
auto data2 = GetWinFontData(L"Batang");
if (data2)
SaveFontToFile(*data2, L"Batang");
return 0;
}