实现遍历Windows所有字体的基本属性

参考podofo

cpp 复制代码
#include <windows.h>
#include <string>
#include <memory>
#include <set>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <codecvt>
#include <locale>

using namespace std;
#include <windows.h>
#include <string>
#include <memory>
#include <set>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <codecvt>

using namespace std;

#if defined(_WIN32) || defined(_WIN64)
#define IS_WINDOWS_PLATFORM 1
#else
#define IS_WINDOWS_PLATFORM 0
#endif

#if IS_WINDOWS_PLATFORM
using CharNameType = const wchar_t*;
using StringNameType = std::wstring;
#else
using CharNameType = const char*;
using StringNameType = std::string;
#endif

class Font {
public:
    // 基本属性访问器
    int GetBaseFont() const { return base_font_; }
    void SetBaseFont(int value) { base_font_ = value; }

    int GetWidth() const { return width_; }
    void SetWidth(int value) { width_ = value; }

    int GetHeight() const { return height_; }
    void SetHeight(int value) { height_ = value; }

    int GetWeight() const { return weight_; }
    void SetWeight(int value) { weight_ = value; }

    bool IsItalic() const { return italic_; }
    void SetItalic(bool value) { italic_ = value; }

    bool IsUnderline() const { return underline_; }
    void SetUnderline(bool value) { underline_ = value; }

    bool IsStrikeOut() const { return strike_out_; }
    void SetStrikeOut(bool value) { strike_out_ = value; }
    int GetOrientation() const { return orientation_; }
    void SetOrientation(int value) { orientation_ = value; }

    int GetEscapement() const { return escapement_; }
    void SetEscapement(int value) { escapement_ = value; }

    DWORD GetPitchAndFamily() const { return pitch_and_family_; }
    void SetPitchAndFamily(DWORD value) { pitch_and_family_ = value; }

    // 字符集操作方法
   /* const std::set<int>& GetCharsets() const { return charsets_; }
    void AddCharset(int charset) { charsets_.insert(charset); }*/
    // 修复字符集存储方式
    void AddCharset(BYTE charset) {
        charsets_.insert(static_cast<int>(charset));
    }
    const set<int>& GetCharsets() const { return charsets_; }
    void MergeCharsets(const std::set<int>& other) {
        charsets_.insert(other.begin(), other.end());
    }

    // 名称处理方法
    void SetFaceName(const wchar_t* name) {
        wcsncpy_s(face_name_, name, _TRUNCATE);
        UpdateNormalizedName();
    }
    const wchar_t* GetFaceName() const { return face_name_; }

    void SetPostscriptName(const wstring& name) { postscript_name_ = name; }
    const wstring& GetPostscriptName() const { return postscript_name_; }

#ifdef _WIN32
    void SetLogFont(const LOGFONTW& lf) {
        SetFaceName(lf.lfFaceName);
        AddCharset(lf.lfCharSet);
        width_ = lf.lfWidth;
        height_ = lf.lfHeight;
        weight_ = lf.lfWeight;
        italic_ = lf.lfItalic;
        underline_ = lf.lfUnderline;
        strike_out_ = lf.lfStrikeOut;
        SetOrientation(lf.lfOrientation);
        SetEscapement(lf.lfEscapement);
        SetPitchAndFamily(lf.lfPitchAndFamily);
    }
#endif

private:
    void UpdateNormalizedName() {
        normalized_name_.clear();
        for (const wchar_t* p = face_name_; *p; ++p) {
            if (!wcschr(L" -_", *p)) {
                normalized_name_ += towlower(*p);
            }
        }
    }

    int base_font_ = 0;
    int width_ = 0;
    int height_ = 0;
    int weight_ = 0;
    bool italic_ = false;
    bool underline_ = false;
    bool strike_out_ = false;
    wchar_t face_name_[LF_FACESIZE] = { 0 };
    // 新增属性成员
    int orientation_ = 0;
    int escapement_ = 0;
    DWORD pitch_and_family_ = 0;
    wstring postscript_name_;
    wstring normalized_name_;
    std::set<int> charsets_;
};

wstring ToLowerWithoutSpace(const wstring& str) {
    wstring result;
    result.reserve(str.size());
    for (auto ch : str) {
        if (!wcschr(L" _-", ch)) {
            result += towlower(ch);
        }
    }
    return result;
}

class TTFontParser {
public:
    static wstring GetPostScriptName(HDC hdc) {
        vector<BYTE> table = GetFontTable(hdc, 'eman');
        return table.empty() ? L"" : ParseNameTable(table, 6);
    }

private:
    static vector<BYTE> GetFontTable(HDC hdc, DWORD tag) {
        DWORD size = ::GetFontData(hdc, tag, 0, nullptr, 0);
        if (size == GDI_ERROR) return {};

        vector<BYTE> buffer(size);
        return (::GetFontData(hdc, tag, 0, buffer.data(), size) == size) ? buffer : vector<BYTE>();
    }

    static wstring ParseNameTable(const vector<BYTE>& table, WORD targetID) {
        if (table.size() < 6) return L"";

        auto readU16 = [&](size_t offset) {
            return static_cast<WORD>((table[offset] << 8) | table[offset + 1]);
            };

        const WORD count = readU16(2);
        const WORD strOffset = readU16(4);

        for (WORD i = 0; i < count; ++i) {
            size_t record = 6 + i * 12;
            if (readU16(record + 6) != targetID) continue;

            const WORD platformID = readU16(record);
            const WORD encodingID = readU16(record + 2);
            const WORD length = readU16(record + 8);
            const WORD offset = readU16(record + 10);

            if (IsValidEncoding(platformID, encodingID)) {
                return DecodeString(&table[strOffset + offset], length, platformID);
            }
        }
        return L"";
    }

    static bool IsValidEncoding(WORD platform, WORD encoding) {
        return (platform == 1 && encoding == 0) || (platform == 3 && encoding == 1);
    }

    static wstring DecodeString(const BYTE* data, size_t len, WORD platform) {
        wstring result;
        if (platform == 1) {
            result.resize(len);
            transform(data, data + len, result.begin(),
                [](BYTE c) { return static_cast<wchar_t>(c); });
        }
        else if (platform == 3) {
            result.resize(len / 2);
            for (size_t i = 0; i < len / 2; ++i) {
                result[i] = (data[i * 2] << 8) | data[i * 2 + 1];
            }
        }
        return result;
    }
};

class FontManager {
public:
    static FontManager& Instance() {
        static FontManager instance;
        return instance;
    }

    void LoadFonts() {
        if (loaded_) return;

        HDC hdc = CreateCompatibleDC(nullptr);
        LOGFONTW lf = { 0 };
        lf.lfCharSet = DEFAULT_CHARSET; // 必须显式设置才能枚举所有字符集

        EnumFontFamiliesExW(hdc, &lf, EnumProc, (LPARAM)this, 0);
        ReleaseDC(nullptr, hdc);

        BuildIndex();
        loaded_ = true;
    }
#if 0
    void ExportReport(const wstring& filename) const {
        wofstream file(filename);
        file.imbue(locale(""));

        file << L"系统字体报告 (" << fonts_.size() << L" 款)\n";
        file << L"================================\n";

        for (const auto& font : fonts_) {
            file << L"名称: " << font->GetFaceName() << L"\n"
                << L"PostScript: " << font->GetPostscriptName() << L"\n"
                << L"字符集: ";

            for (int cs : font->GetCharsets()) {
                file << cs << L" ";
            }
            file << L"\n--------------------------------\n";
        }
    }
#else

    void ExportReport(const wstring& filename) const {
        wofstream file(filename);
        file.imbue(locale(""));

        file << L"完整的系统字体报告\n";
        file << L"================================\n";
        file << L"总字体数: " << fonts_.size() << L"\n\n";

        for (const auto& font : fonts_) {
            file << L"字体名称: " << font->GetFaceName() << L"\n"
                << L"PostScript名称: " << font->GetPostscriptName() << L"\n"
                << L"--------------------------------\n"
                << L"基本属性:\n"
                << L"  基准字号: " << font->GetBaseFont() << L"\n"
                << L"  宽度: " << font->GetWidth() << L"\n"
                << L"  高度: " << font->GetHeight() << L"\n"
                << L"  字重: " << font->GetWeight() << L"\n"
                << L"  方向: " << font->GetOrientation() << L"\n"
                << L"  倾斜角: " << font->GetEscapement() << L"\n"
                << L"  字符间距: " << (font->GetPitchAndFamily() & 0x3) << L"\n"
                << L"  字体系列: " << ((font->GetPitchAndFamily() & 0xF0) >> 4) << L"\n"
                << L"样式属性:\n"
                << L"  斜体: " << (font->IsItalic() ? L"是" : L"否") << L"\n"
                << L"  下划线: " << (font->IsUnderline() ? L"是" : L"否") << L"\n"
                << L"  删除线: " << (font->IsStrikeOut() ? L"是" : L"否") << L"\n"
                << L"字符集 (" << font->GetCharsets().size() << L" 种): ";

            // 打印字符集详细信息
            const auto& charsets = font->GetCharsets();
            for (auto cs : charsets) {
                file << cs << L" ";
            }

            file << L"\n================================\n";
        }
    }

#endif
    const Font* FindFont(const wstring& name) const {
        wstring key = ToKey(name);
        auto it = index_.find(key);
        return (it != index_.end()) ? it->second : nullptr;
    }

private:
    static int CALLBACK EnumProc(const LOGFONTW* lf, const TEXTMETRICW*, DWORD, LPARAM param) {
        if (lf->lfFaceName[0] == L'@') return TRUE;

        FontManager* self = reinterpret_cast<FontManager*>(param);
        auto font = make_shared<Font>();
        font->SetLogFont(*lf);

        HDC hdc = GetDC(nullptr);
        HFONT hFont = CreateFontIndirectW(lf);
        HGDIOBJ old = SelectObject(hdc, hFont);

        font->SetPostscriptName(TTFontParser::GetPostScriptName(hdc));
    
        SelectObject(hdc, old);
        DeleteObject(hFont);
        ReleaseDC(nullptr, hdc);

        self->AddFont(font);
        return TRUE;
    }

    void AddFont(shared_ptr<Font> newFont) {
        auto it = find_if(fonts_.rbegin(), fonts_.rend(), [&](const auto& f) {
            return ToKey(f->GetFaceName()) == ToKey(newFont->GetFaceName());
            });

        if (it != fonts_.rend()) {
            (*it)->MergeCharsets(newFont->GetCharsets());
        }
        else {
            fonts_.push_back(newFont);
        }
    }

    void BuildIndex() {
        for (const auto& font : fonts_) {
            index_[ToKey(font->GetFaceName())] = font.get();
        }
    }

    static wstring ToKey(const wstring& name) {
        return ToLowerWithoutSpace(name);
    }

    vector<shared_ptr<Font>> fonts_;
    unordered_map<wstring, Font*> index_;
    bool loaded_ = false;
};

int main() {
    FontManager::Instance().LoadFonts();
    FontManager::Instance().ExportReport(L"D:/fonts_report.txt");

    if (auto font = FontManager::Instance().FindFont(L"Arial")) {
        wcout << L"找到字体: " << font->GetFaceName() << endl;
    }

    return 0;
}
相关推荐
最爱是生活23 分钟前
STM32之影子寄存器
stm32·单片机·嵌入式硬件
今天也想快点毕业2 小时前
windows安装mysql
windows·mysql·adb
驱动起爆大师x_x3 小时前
STM32F1学习——WDG看门狗
笔记·stm32·单片机·嵌入式硬件·学习
扛枪的书生3 小时前
Windows 提权-UAC 绕过
windows·渗透·kali·提权
weixin_462901973 小时前
ESP 32控制无刷电机2
stm32·单片机·嵌入式硬件
ceffans4 小时前
PDF文档中表格以及形状解析
开发语言·c++·windows·pdf
╰⋛⋋⊱⋋翅膀⋌⊰⋌⋚╯5 小时前
单片机死机跑飞的原因
单片机·嵌入式硬件
猫猫的小茶馆5 小时前
【项目管理】基于 C 语言的 QQ 聊天室实现(TCP + 多线程 + SQLite3)后续部分代码优化
linux·c语言·网络·单片机·tcp/ip·sqlite·嵌入式软件
天才测试猿5 小时前
Python测试框架Pytest的参数化
自动化测试·软件测试·windows·python·测试工具·职场和发展·pytest