Windows获取字体文件

cpp 复制代码
// 包含必要的头文件
#include <string>
#include <set>
#include <vector>
#include <iostream>
#include <filesystem>
#include <windows.h>
#include <fstream> // 用于文件操作
#include <algorithm> // 确保包含 std::min 的声明

using namespace std;

// 函数声明
std::unique_ptr<std::string> GetWinFontData(std::wstring font_name); // 获取指定字体的字节数据
std::unique_ptr<std::string> GetWinFontData(const LOGFONTW& infont); // 获取指定字体的字节数据,并返回一个指向该数据的 `std::unique_ptr<std::string>`
bool GetWinFontData(std::string& buffer, HDC hdc, HFONT hf); // 辅助函数,用于从指定的字体对象中提取字体数据
void GetFontDataTTC(std::string& buffer, const std::string& file_buffer, const std::string& ttc_buffer); // 处理 TrueType 集合(TTC)数据,提取并重组字体表
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 结构体
    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>`。
std::unique_ptr<std::string> GetWinFontData(const LOGFONTW& infont)
{
    bool success = false;
    std::string buffer;
    buffer.clear();

    // 创建兼容设备上下文 (HDC)
    HDC hdc = ::CreateCompatibleDC(nullptr);

    // 根据字体描述创建字体对象 (HFONT)
    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; // TTC 文件标识符

    // 获取字体数据的长度
    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) // 如果不是 TTC 文件
        {
            buffer.resize(fileLen); // 分配缓冲区
            sucess = GetFontData(hdc, 0, 0, const_cast<char*>(buffer.data()), (DWORD)fileLen) != GDI_ERROR;
        }
        else // 如果是 TTC 文件
        {
            std::string fileBuffer;
            fileBuffer.resize(fileLen);

            // 提取 TTC 文件的基本信息
            if (GetFontData(hdc, 0, 0, const_cast<char*>(fileBuffer.data()), fileLen) == GDI_ERROR)
            {
                sucess = false;
                goto Exit;
            }

            std::string ttcBuffer;
            ttcBuffer.resize(ttcLen);

            // 提取 TTC 文件的具体数据
            if (GetFontData(hdc, ttcf_const, 0, const_cast<char*>(ttcBuffer.data()), ttcLen) == GDI_ERROR)
            {
                sucess = false;
                goto Exit;
            }

            // 调用函数处理 TTC 数据
            GetFontDataTTC(buffer, fileBuffer, ttcBuffer);
            sucess = true;
        }
    }

Exit:
    SelectObject(hdc, oldFont); // 恢复原始字体
    return sucess;
}

// 处理 TrueType 集合(TTC)数据,提取并重组字体表。
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; // 对齐到 4 字节边界
        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; // 对齐到 4 字节边界

        *(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"); // 提取 Arial 字体数据
    if (data1)
        SaveFontToFile(*data1, L"Arial"); // 保存 Arial 字体数据

    auto data2 = GetWinFontData(L"Batang"); // 提取 Batang 字体数据
    if (data2)
        SaveFontToFile(*data2, L"Batang"); // 保存 Batang 字体数据

    return 0;
}
相关推荐
点灯小铭2 小时前
基于单片机的多路热电偶温度监测与报警器
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
Davina_yu3 小时前
Windows 下升级 R 语言至最新版
开发语言·windows·r语言
豆是浪个5 小时前
Linux(Centos 7.6)命令详解:ps
linux·windows·centos
故事不长丨6 小时前
C#集合:解锁高效数据管理的秘密武器
开发语言·windows·c#·wpf·集合·winfrom·字典
tianyue1006 小时前
STM32G431 ADC 多个channel 采集
stm32·单片机·嵌入式硬件
清风6666668 小时前
基于单片机的水泵效率温差法测量与报警系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
不染尘.8 小时前
进程切换和线程调度
linux·数据结构·windows·缓存
love530love8 小时前
EPGF 新手教程 12在 PyCharm(中文版 GUI)中创建 Poetry 项目环境,并把 Poetry 做成“项目自包含”(工具本地化为必做环节)
开发语言·ide·人工智能·windows·python·pycharm·epgf
z20348315209 小时前
定时器练习报告
单片机·嵌入式硬件
zk009 小时前
内容分类目录
单片机·嵌入式硬件