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