当前位置: 首页 > news >正文

Windows获取字体文件

// 包含必要的头文件
#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;
}

相关文章:

  • R语言安装教程(附安装包)R语言4.3.2版本安装教程
  • deepseek 清华大学[1-5版]全集
  • 【PX4日志解析报错】pyulog工具解析日志出错
  • 【管道 】
  • STM32的HAL库开发---ADC采集内部温度传感器
  • 大疆激光雷达录制的bag包无法解析出topic怎么办?
  • 【Blender】二、建模篇--07,置换修改器
  • 第14篇:Vue Router 高级用法与路由守卫
  • 2025教育与科研领域实战全解析:DeepSeek赋能细分场景深度指南(附全流程案例与资源)
  • Android 实现 RTMP 推流:快速集成指南
  • PyTorch 是如何进行机器学习的
  • stm32108键C-B全调性_动态可视化乐谱钢琴
  • conda 基本命令
  • HttpWatch 9.4.17 Pro网页调试与性能优化 资源工具分享
  • Leetcode-42. Trapping Rain Water [C++][Java]
  • 能否在delete一个指针后继续使用它以及原因
  • MQTT实现智能家居------3、源码分析(超详细)
  • JavaScript中的call方法相关知识点
  • ZLG嵌入式笔记 | 为什么你的网卡工作会不正常?(上
  • 鸿蒙学习-
  • 哪里可以做虚拟货币网站/新手做销售怎么开发客户
  • 国企门户网站建设方案/优化大师百科
  • 网站建设入驻/网站关键词排名服务
  • 做网站有个名字叫小廖/我想学做互联网怎么入手
  • 移动网站 案例/郑州网络营销推广公司
  • 网站吗/网站推广的6个方法是什么