Windows DirectWrite接口 遍历字体信息

FontInfo.h

cpp 复制代码
#pragma once
#include <string>
#include <vector>
#include <dwrite.h>
#include <comdef.h>
#include <vector>
#include <iostream>
#include <dwrite_3.h>
#include <limits> 
#pragma comment(lib, "dwrite.lib")
class FontInfo {
public:
	FontInfo();
	~FontInfo();

	// 字体基本信息
	std::wstring familyName;       // 字体家族名称
	std::wstring faceName;         // 字体面名称
	std::wstring postscriptName;   // PostScript名称
	std::wstring style;            // 样式 (常规、斜体、粗体等)
	std::wstring localizedName;    // 本地化名称

	// 字体度量信息
	float ascent;                  // 基线到最高点的距离
	float descent;                 // 基线到最低点的距离
	float lineGap;                 // 行间距
	float capHeight;               // 大写字母高度
	float xHeight;                 // 小写字母x高度
	float underlinePosition;       // 下划线位置
	float underlineThickness;      // 下划线粗细
	float strikethroughPosition;   // 删除线位置
	float strikethroughThickness;  // 删除线粗细

	// 字体设计信息
	DWRITE_FONT_WEIGHT weight;     // 字体粗细
	DWRITE_FONT_STRETCH stretch;   // 字体拉伸
	DWRITE_FONT_STYLE styleEnum;   // 字体样式枚举

	// 字体支持特性
	bool hasKerning;               // 是否支持字距调整
	bool hasVerticalGlyphs;        // 是否支持垂直排版

	// 字符集信息
	//std::vector<UINT32> codePoints; // 支持的Unicode码点
	 // 改为存储 Unicode 范围而非单个码点
	std::vector<DWRITE_UNICODE_RANGE> unicodeRanges;

	// 文件信息
	std::wstring filePath;         // 字体文件路径
	UINT32 index;                  // 字体集合中的索引

	// 其他元数据
	std::wstring designer;         // 设计师
	std::wstring copyright;        // 版权信息
	std::wstring version;          // 版本信息
	std::wstring trademark;        // 商标信息

	// 打印字体信息
	void PrintInfo() const;
};

FontInfo.cpp

cpp 复制代码
#include "FontInfo.h"
#include <iostream>

FontInfo::FontInfo()
	: ascent(0), descent(0), lineGap(0), capHeight(0), xHeight(0),
	underlinePosition(0), underlineThickness(0),
	strikethroughPosition(0), strikethroughThickness(0),
	weight(DWRITE_FONT_WEIGHT_NORMAL),
	stretch(DWRITE_FONT_STRETCH_NORMAL),
	styleEnum(DWRITE_FONT_STYLE_NORMAL),
	hasKerning(false), hasVerticalGlyphs(false), index(0) {
}

FontInfo::~FontInfo() {}

void FontInfo::PrintInfo() const {
	std::wcout << L"Font Information:" << std::endl;
	std::wcout << L"  Family Name: " << familyName << std::endl;
	std::wcout << L"  Face Name: " << faceName << std::endl;
	std::wcout << L"  Style: " << style << std::endl;
	std::wcout << L"  PostScript Name: " << postscriptName << std::endl;
	std::wcout << L"  Localized Name: " << localizedName << std::endl;

	std::wcout << L"\nMetrics:" << std::endl;
	std::wcout << L"  Ascent: " << ascent << std::endl;
	std::wcout << L"  Descent: " << descent << std::endl;
	std::wcout << L"  Line Gap: " << lineGap << std::endl;
	std::wcout << L"  Cap Height: " << capHeight << std::endl;
	std::wcout << L"  x Height: " << xHeight << std::endl;

	std::wcout << L"\nDesign:" << std::endl;
	std::wcout << L"  Weight: " << weight << std::endl;
	std::wcout << L"  Stretch: " << stretch << std::endl;
	std::wcout << L"  Style: " << styleEnum << std::endl;

	std::wcout << L"\nFile Info:" << std::endl;
	std::wcout << L"  File Path: " << filePath << std::endl;
	std::wcout << L"  Font Index: " << index << std::endl;

	std::wcout << L"\nMetadata:" << std::endl;
	std::wcout << L"  Designer: " << designer << std::endl;
	std::wcout << L"  Copyright: " << copyright << std::endl;
	std::wcout << L"  Version: " << version << std::endl;
	std::wcout << L"  Trademark: " << trademark << std::endl;
}

FontEnumerator.h

cpp 复制代码
// FontEnumerator.h
#pragma once
#include "FontInfo.h"
#include <string>
#include <vector>
#include <dwrite.h>
#include <dwrite_3.h>
//
//class FontInfo {
//public:
//    // 字体基本信息
//    std::wstring familyName;       // 字体家族名称
//    std::wstring faceName;         // 字体面名称
//    std::wstring postscriptName;   // PostScript名称
//    std::wstring style;            // 样式 (常规、斜体、粗体等)
//    std::wstring localizedName;    // 本地化名称
//
//    // 字体度量信息
//    float ascent = 0;              // 基线到最高点的距离
//    float descent = 0;             // 基线到最低点的距离
//    float lineGap = 0;             // 行间距
//    float capHeight = 0;           // 大写字母高度
//    float xHeight = 0;             // 小写字母x高度
//    float underlinePosition = 0;   // 下划线位置
//    float underlineThickness = 0;  // 下划线粗细
//    float strikethroughPosition = 0;   // 删除线位置
//    float strikethroughThickness = 0;  // 删除线粗细
//
//    // 字体设计信息
//    DWRITE_FONT_WEIGHT weight = DWRITE_FONT_WEIGHT_NORMAL;     // 字体粗细
//    DWRITE_FONT_STRETCH stretch = DWRITE_FONT_STRETCH_NORMAL;  // 字体拉伸
//    DWRITE_FONT_STYLE styleEnum = DWRITE_FONT_STYLE_NORMAL;   // 字体样式枚举
//
//    // 字体支持特性
//    bool hasKerning = false;               // 是否支持字距调整
//    bool hasVerticalGlyphs = false;        // 是否支持垂直排版
//
//    // 字符集信息
//    std::vector<UINT32> codePoints;        // 支持的Unicode码点
//
//    // 文件信息
//    std::wstring filePath;                // 字体文件路径
//    UINT32 index = 0;                     // 字体集合中的索引
//
//    // 其他元数据
//    std::wstring designer;                // 设计师
//    std::wstring copyright;               // 版权信息
//    std::wstring version;                // 版本信息
//    std::wstring trademark;               // 商标信息
//
//    void PrintInfo() const;
//};

class FontEnumerator {
public:
    FontEnumerator();
    ~FontEnumerator();

    bool Initialize();
    std::vector<FontInfo> EnumerateFonts();

private:
    IDWriteFactory* pDWriteFactory;
    IDWriteFontCollection* pSystemFontCollection;

    // 核心功能方法
    void GetFontInfo(IDWriteFont* pFont, FontInfo& fontInfo);
    void GetBasicFontProperties(IDWriteFont* pFont, FontInfo& fontInfo);
    void GetFontFaceInfo(IDWriteFontFace* pFontFace, FontInfo& fontInfo);

    // 详细信息获取方法
    void GetFontFilePath(IDWriteFontFace3* pFontFace3, FontInfo& fontInfo);
    void GetFontMetadata(IDWriteFontFace3* pFontFace3, FontInfo& fontInfo);
    void GetSupportedCharacters(IDWriteFontFace3* pFontFace3, FontInfo& fontInfo);

    // 辅助方法
    static std::wstring GetLocalizedStringSafe(IDWriteLocalizedStrings* pStrings,
        const wchar_t* preferredLocale = L"en-us");

    // COM对象安全释放
    template<class T> static void SafeRelease(T** ppT) {
        if (*ppT) {
            (*ppT)->Release();
            *ppT = nullptr;
        }
    }
};

FontEnumerator.cpp

cpp 复制代码
#include "FontEnumerator.h"
#include <comdef.h>
#include <vector>
#include <iostream>
#include <dwrite_3.h>
#include <limits> 
#pragma comment(lib, "dwrite.lib")

// 辅助函数:安全获取本地化字符串
std::wstring FontEnumerator::GetLocalizedStringSafe(IDWriteLocalizedStrings* pStrings, const wchar_t* preferredLocale ) {
    if (!pStrings) return L"";

    UINT32 index = 0;
    BOOL exists = FALSE;

    // 区域查找优先级
    const std::vector<std::wstring> localePriority = {
        preferredLocale,
        L"en-us",    // 英语(美国)
        L"zh-cn",    // 中文(简体)
        L""          // 默认第一个
    };

    HRESULT hr = S_OK;
    for (const auto& locale : localePriority) {
        if (locale.empty()) {
            index = 0;
            exists = (pStrings->GetCount() > 0);
            break;
        }

        hr = pStrings->FindLocaleName(locale.c_str(), &index, &exists);
        if (SUCCEEDED(hr) && exists) break;
    }

    if (!exists) return L"";

    UINT32 length = 0;
    hr = pStrings->GetStringLength(index, &length);
    if (FAILED(hr) || length == 0) return L"";

    std::wstring buffer(length + 1, L'\0');
    hr = pStrings->GetString(index, &buffer[0], length + 1);
    if (SUCCEEDED(hr)) {
        buffer.resize(length);
        return buffer;
    }

    return L"";
}

FontEnumerator::FontEnumerator()
    : pDWriteFactory(nullptr), pSystemFontCollection(nullptr) {
}

FontEnumerator::~FontEnumerator() {
    SafeRelease(&pSystemFontCollection);
    SafeRelease(&pDWriteFactory);
}

bool FontEnumerator::Initialize() {
    HRESULT hr = DWriteCreateFactory(
        DWRITE_FACTORY_TYPE_SHARED,
        __uuidof(IDWriteFactory),
        reinterpret_cast<IUnknown**>(&pDWriteFactory)
    );

    if (FAILED(hr)) {
        _com_error err(hr);
        std::wcerr << L"Failed to create DirectWrite factory: " << err.ErrorMessage() << std::endl;
        return false;
    }

    hr = pDWriteFactory->GetSystemFontCollection(&pSystemFontCollection);
    if (FAILED(hr)) {
        _com_error err(hr);
        std::wcerr << L"Failed to get system font collection: " << err.ErrorMessage() << std::endl;
        return false;
    }

    return true;
}

std::vector<FontInfo> FontEnumerator::EnumerateFonts() {
    std::vector<FontInfo> fonts;

    if (!pSystemFontCollection && !Initialize()) {
        return fonts;
    }

    UINT32 familyCount = pSystemFontCollection->GetFontFamilyCount();

    for (UINT32 familyIndex = 0; familyIndex < familyCount; ++familyIndex) {
        IDWriteFontFamily* pFontFamily = nullptr;
        HRESULT hr = pSystemFontCollection->GetFontFamily(familyIndex, &pFontFamily);
        if (FAILED(hr)) continue;

        UINT32 fontCount = pFontFamily->GetFontCount();

        for (UINT32 fontIndex = 0; fontIndex < fontCount; ++fontIndex) {
            IDWriteFont* pFont = nullptr;
            hr = pFontFamily->GetFont(fontIndex, &pFont);
            if (FAILED(hr)) {
                SafeRelease(&pFontFamily);
                continue;
            }

            FontInfo fontInfo;
            GetFontInfo(pFont, fontInfo);
            fonts.push_back(fontInfo);

            SafeRelease(&pFont);
        }

        SafeRelease(&pFontFamily);
    }

    return fonts;
}

void FontEnumerator::GetFontInfo(IDWriteFont* pFont, FontInfo& fontInfo) {
    // 获取基本字体信息
    GetBasicFontProperties(pFont, fontInfo);

    // 获取字体文件信息
    IDWriteFontFace* pFontFace = nullptr;
    HRESULT hr = pFont->CreateFontFace(&pFontFace);
    if (SUCCEEDED(hr)) {
        GetFontFaceInfo(pFontFace, fontInfo);
        SafeRelease(&pFontFace);
    }
}

void FontEnumerator::GetBasicFontProperties(IDWriteFont* pFont, FontInfo& fontInfo) {
    // 获取字体度量
    DWRITE_FONT_METRICS fontMetrics;
    pFont->GetMetrics(&fontMetrics);

    fontInfo.ascent = fontMetrics.ascent;
    fontInfo.descent = fontMetrics.descent;
    fontInfo.lineGap = fontMetrics.lineGap;
    fontInfo.capHeight = fontMetrics.capHeight;
    fontInfo.xHeight = fontMetrics.xHeight;
    fontInfo.underlinePosition = fontMetrics.underlinePosition;
    fontInfo.underlineThickness = fontMetrics.underlineThickness;
    fontInfo.strikethroughPosition = fontMetrics.strikethroughPosition;
    fontInfo.strikethroughThickness = fontMetrics.strikethroughThickness;

    fontInfo.weight = pFont->GetWeight();
    fontInfo.stretch = pFont->GetStretch();
    fontInfo.styleEnum = pFont->GetStyle();

    // 获取字体名称信息
    IDWriteFontFamily* pFontFamily = nullptr;
    if (SUCCEEDED(pFont->GetFontFamily(&pFontFamily))) {
        IDWriteLocalizedStrings* pNames = nullptr;

        // 家族名称
        if (SUCCEEDED(pFontFamily->GetFamilyNames(&pNames))) {
            fontInfo.familyName = GetLocalizedStringSafe(pNames);
            SafeRelease(&pNames);
        }

        // 字体面名称
        if (SUCCEEDED(pFont->GetFaceNames(&pNames))) {
            fontInfo.faceName = GetLocalizedStringSafe(pNames);
            fontInfo.style = fontInfo.faceName; // 样式通常与面名相同
            SafeRelease(&pNames);
        }

        // PostScript名称
        BOOL exists = FALSE;
        if (SUCCEEDED(pFont->GetInformationalStrings(
            DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME, &pNames, &exists)) && exists) {
            fontInfo.postscriptName = GetLocalizedStringSafe(pNames);
            SafeRelease(&pNames);
        }

        // 本地化全名
        if (SUCCEEDED(pFont->GetInformationalStrings(
            DWRITE_INFORMATIONAL_STRING_FULL_NAME, &pNames, &exists)) && exists) {
            fontInfo.localizedName = GetLocalizedStringSafe(pNames);
            SafeRelease(&pNames);
        }

        SafeRelease(&pFontFamily);
    }
}

void FontEnumerator::GetFontFaceInfo(IDWriteFontFace* pFontFace, FontInfo& fontInfo) {
    IDWriteFontFace3* pFontFace3 = nullptr;
    HRESULT hr = pFontFace->QueryInterface(__uuidof(IDWriteFontFace3), (void**)&pFontFace3);
    if (FAILED(hr)) return;

    // 使用智能指针确保资源释放
    auto fontFaceDeleter = [](IDWriteFontFace3* p) { if (p) p->Release(); };
    std::unique_ptr<IDWriteFontFace3, decltype(fontFaceDeleter)> fontFaceHolder(pFontFace3, fontFaceDeleter);

    // 获取字体文件路径
    GetFontFilePath(pFontFace3, fontInfo);

    // 获取字体元数据
    GetFontMetadata(pFontFace3, fontInfo);

    // 获取支持的字符集
    GetSupportedCharacters(pFontFace3, fontInfo);
}

void FontEnumerator::GetFontFilePath(IDWriteFontFace3* pFontFace3, FontInfo& fontInfo) {
    UINT32 numFiles = 0;
    HRESULT hr = pFontFace3->GetFiles(&numFiles, nullptr);
    if (FAILED(hr) || numFiles == 0) return;

    IDWriteFontFile* pFontFile = nullptr;
    hr = pFontFace3->GetFiles(&numFiles, &pFontFile);
    if (FAILED(hr)) return;

    const void* referenceKey = nullptr;
    UINT32 referenceKeySize = 0;
    hr = pFontFile->GetReferenceKey(&referenceKey, &referenceKeySize);
    if (FAILED(hr)) {
        SafeRelease(&pFontFile);
        return;
    }

    IDWriteFontFileLoader* pFontFileLoader = nullptr;
    hr = pFontFile->GetLoader(&pFontFileLoader);
    if (FAILED(hr)) {
        SafeRelease(&pFontFile);
        return;
    }

    IDWriteLocalFontFileLoader* pLocalFileLoader = nullptr;
    hr = pFontFileLoader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), (void**)&pLocalFileLoader);
    if (SUCCEEDED(hr)) {
        UINT32 filePathLength = 0;
        hr = pLocalFileLoader->GetFilePathLengthFromKey(referenceKey, referenceKeySize, &filePathLength);
        if (SUCCEEDED(hr) && filePathLength > 0) {
            std::wstring filePath(filePathLength + 1, L'\0');
            hr = pLocalFileLoader->GetFilePathFromKey(referenceKey, referenceKeySize, &filePath[0], filePathLength + 1);
            if (SUCCEEDED(hr)) {
                filePath.resize(filePathLength);
                fontInfo.filePath = filePath;
            }
        }
        SafeRelease(&pLocalFileLoader);
    }

    SafeRelease(&pFontFileLoader);
    SafeRelease(&pFontFile);
}

void FontEnumerator::GetFontMetadata(IDWriteFontFace3* pFontFace3, FontInfo& fontInfo) {
    // 设计师信息
    IDWriteLocalizedStrings* pStrings = nullptr;
    BOOL exists = FALSE;
    if (SUCCEEDED(pFontFace3->GetInformationalStrings(
        DWRITE_INFORMATIONAL_STRING_DESIGNER, &pStrings, &exists)) && exists) {
        fontInfo.designer = GetLocalizedStringSafe(pStrings);
        SafeRelease(&pStrings);
    }

    // 版权信息
    if (SUCCEEDED(pFontFace3->GetInformationalStrings(
        DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE, &pStrings, &exists)) && exists) {
        fontInfo.copyright = GetLocalizedStringSafe(pStrings);
        SafeRelease(&pStrings);
    }

    // 版本信息
    if (SUCCEEDED(pFontFace3->GetInformationalStrings(
        DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS, &pStrings, &exists)) && exists) {
        fontInfo.version = GetLocalizedStringSafe(pStrings);
        SafeRelease(&pStrings);
    }

    // 商标信息
    if (SUCCEEDED(pFontFace3->GetInformationalStrings(
        DWRITE_INFORMATIONAL_STRING_TRADEMARK, &pStrings, &exists)) && exists) {
        fontInfo.trademark = GetLocalizedStringSafe(pStrings);
        SafeRelease(&pStrings);
    }
}void FontEnumerator::GetSupportedCharacters(IDWriteFontFace3* pFontFace3, FontInfo& fontInfo) {
    fontInfo.unicodeRanges.clear();

    if (!pFontFace3) {
        std::cerr << "错误: pFontFace3 是空指针" << std::endl;
        return;
    }

    // 第一次调用:获取所需范围数量
    UINT32 rangeCount = 0;
    HRESULT hr = pFontFace3->GetUnicodeRanges(0, nullptr, &rangeCount); //第一次调用不能获取返回值
    

    if (rangeCount == 0) {
        std::cout << "警告: 字体未声明任何Unicode范围" << std::endl;
        return;
    }

    // 分配足够大的缓冲区
    std::vector<DWRITE_UNICODE_RANGE> ranges(rangeCount);

    // 第二次调用:获取实际数据
    UINT32 actualRangeCount = 0;
    hr = pFontFace3->GetUnicodeRanges(rangeCount, ranges.data(), &actualRangeCount);
    if (FAILED(hr)) {
        _com_error err(hr);
        std::cerr << "获取Unicode范围数据失败: " << err.ErrorMessage()
            << " (实际需要大小: " << actualRangeCount
            << ", 分配大小: " << rangeCount << ")" << std::endl;
        return;
    }

    // 检查实际返回数量是否匹配
    if (actualRangeCount > rangeCount) {
        std::cerr << "警告: 实际范围数量(" << actualRangeCount
            << ")超过预期(" << rangeCount << ")" << std::endl;
        ranges.resize(actualRangeCount);
        hr = pFontFace3->GetUnicodeRanges(actualRangeCount, ranges.data(), &actualRangeCount);
        if (FAILED(hr)) return;
    }

    // 保存有效数据
    fontInfo.unicodeRanges.assign(ranges.begin(), ranges.begin() + actualRangeCount);
}
// 安全释放COM对象
template<class T> void SafeRelease(T** ppT) {
    if (*ppT) {
        (*ppT)->Release();
        *ppT = nullptr;
    }
}
相关推荐
程序员Bears3 小时前
IntelliJ IDEA 2025系列通用软件安装教程(Windows版)
ide·windows·pycharm·webstorm
只可远观3 小时前
Flutter Dart 集合类型List Set Map详解军 以及循环语句 forEaclh map where any every
windows·flutter·list
yangshuo12815 小时前
风车邮箱系统详细使用指南:Windows与Ubuntu双平台解析
linux·windows·ubuntu
技术liul5 小时前
如何在iStoreOS DHCP中排除特定IP地址
网络·windows·tcp/ip
补三补四9 小时前
操作系统:计算机世界的基石与演进
开发语言·windows·计算机外设
chuhx14 小时前
Stream API 对两个 List 进行去重操作
数据结构·windows·list
爱编程的鱼20 小时前
C# 枚举(Enum)声明与使用详解
java·windows·c#
进取星辰1 天前
Windows 10 上运行 Ollama 时遇到 llama runner process has terminated: exit status 2
windows·llama
sukalot1 天前
Windows同步技术-使用命名对象
windows