potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。

C++20&Win32 API フォントの名前を列挙する

C++20とWin32 APIでフォントの名前を列挙するコードです。

フォント情報の列挙にはEnumFontFamiliesExW関数を使用します。これはHDCLOGFONTW構造体等を引数とするコールバック関数を受け取ります。HDCに使用するスクリーンのHDCGetDC関数で取得してReleaseDC関数で解放しますが、ReleaseDC関数はHDCの他にGetDC関数に与えたウィンドウハンドルを必要とします。std::unique_ptrの特殊化による対応が思いつかなかったため、"HDCForRelease.hpp"HDCForReleaseクラスを作成しています。

また、再利用可能な関数を"Utility.hpp"に定義しています。

HDCForRelease.hpp

// HDCForRelease.hpp

// #define STRICT
// #include <Windows.h>

#include <utility>
#include <tuple>

class HDCForRelease
{
private:
    HDC hdc;
    HWND hwnd;
public:
    constexpr HDCForRelease() noexcept
        : hdc(), hwnd()
    {
    }
    constexpr HDCForRelease(HDCForRelease&) = delete;
    constexpr HDCForRelease(HDCForRelease&& x) noexcept
        : hdc(x.hdc), hwnd(x.hwnd)
    {
        x.hdc = nullptr;
        x.hwnd = nullptr;
    }
    constexpr HDCForRelease(HWND hwnd, HDC hdc) noexcept
        : hdc(hdc), hwnd(hwnd)
    {
    }
    constexpr HDCForRelease(std::tuple<HWND, HDC>& x) noexcept
        : hdc(std::get<HDC>(x)), hwnd(std::get<HWND>(x))
    {
    }
    ~HDCForRelease()
    {
        reset();
    }
    constexpr HDC get_hdc() const noexcept { return hdc; }
    constexpr HWND get_hwnd() const noexcept { return hwnd; }
    constexpr std::tuple<HDC, HWND> get_tuple() const noexcept
    {
        return std::make_tuple(hdc, hwnd);
    }
    constexpr operator HDC() const noexcept { return hdc; }
    constexpr std::tuple<HDC, HWND> release() noexcept
    {
        std::tuple<HDC, HWND> data{ hdc, hwnd };
        hdc = nullptr;
        hwnd = nullptr;
        return data;
    }
    void reset() noexcept
    {
        reset(nullptr, nullptr);
    }
    void reset(HWND hwnd, HDC hdc) noexcept
    {
        ::ReleaseDC(hwnd, hdc);
        this->hdc = hdc;
        this->hwnd = hwnd;
    }
    constexpr void swap(HDCForRelease& x) noexcept
    {
        std::swap(x.hdc, this->hdc);
        std::swap(x.hwnd, this->hwnd);
    }
    constexpr operator bool() const noexcept
    {
        return hdc != nullptr;
    }
    HDCForRelease& operator=(HDCForRelease&) noexcept = delete;
    constexpr HDCForRelease& operator=(HDCForRelease&& x) noexcept
    {
        hdc = x.hdc;
        hwnd = x.hwnd;
        x.hdc = nullptr;
        x.hwnd = nullptr;
        return *this;
    }
    HDCForRelease& operator=(nullptr_t) noexcept
    {
        reset();
        return *this;
    }
public:
    static HDCForRelease GetDC(HWND hwnd) noexcept
    {
        return HDCForRelease(hwnd, ::GetDC(hwnd));
    }
    static HDCForRelease GetWindowDC(HWND hwnd) noexcept
    {
        return HDCForRelease(hwnd, ::GetWindowDC(hwnd));
    }
};

Utility.hpp

// Utility.hpp

#include <algorithm>
#include <iterator>
#include <iostream>
#include <set>
#include <string>
#include <vector>
#include <ranges>

using namespace std;

vector<LOGFONTW> GetFontFamiliesEx(
    HDC hdc,
    BYTE filterCharSet,
    LPCWSTR filterFaceName)
{
    LOGFONTW lf{};
    lf.lfCharSet = filterCharSet;
    if (filterFaceName != nullptr)
    {
        if (lstrlenW(filterFaceName) > LF_FACESIZE)
            throw invalid_argument(nullptr);
        lstrcpyW(lf.lfFaceName, filterFaceName);
    }

    vector<LOGFONTW> logFonts;
    ::EnumFontFamiliesEx(hdc, &lf,
        [](const LOGFONTW* plf, const TEXTMETRIC*, DWORD, LPARAM lParam) {
            auto& logFonts = *reinterpret_cast<vector<LOGFONTW>*>(lParam);
            logFonts.push_back(*plf);
            return 1;
        },
        reinterpret_cast<LPARAM>(&logFonts), 0);
    return move(logFonts);
}

template <ranges::range LOGFONTW_RANGE>
requires same_as<ranges::range_value_t<LOGFONTW_RANGE>, LOGFONTW>
set<wstring> GetFaceNameSetFromLOGFONTWRange(LOGFONTW_RANGE& logFonts)
{
    set<wstring> faceNames;
    for (const LOGFONTW& lf : logFonts)
        faceNames.emplace(lf.lfFaceName);
    return move(faceNames);
}

すべてのフォント名を列挙する

#define STRICT
#define NOMINMAX
#include <Windows.h>

#include "HDCForRelease.hpp"
#include "Utility.hpp"

int main()
{
    auto hdcScreen = HDCForRelease::GetDC(nullptr);
    auto logFonts = GetFontFamiliesEx(hdcScreen, DEFAULT_CHARSET, nullptr);
    auto faceNames = GetFaceNameSetFromLOGFONTWRange(logFonts);

    wcout.imbue(locale("Japanese", locale::ctype));
    for (const auto& faceName : faceNames)
    {
        wcout << faceName << endl;
    }
}

固定幅フォントの名前を列挙する

#define STRICT
#define NOMINMAX
#include <Windows.h>

#include "HDCForRelease.hpp"
#include "Utility.hpp"

int main()
{
    auto hdcScreen = HDCForRelease::GetDC(nullptr);
    auto logFonts = GetFontFamiliesEx(hdcScreen, DEFAULT_CHARSET, nullptr);
    auto fixedPicthLogFonts = logFonts
        | views::filter([](const LOGFONT& lf) { return (lf.lfPitchAndFamily & FIXED_PITCH) != 0; });
    auto faceNames = GetFaceNameSetFromLOGFONTWRange(fixedPicthLogFonts);

    wcout.imbue(locale("Japanese", locale::ctype));
    for (const auto& faceName : faceNames)
    {
        wcout << faceName << endl;
    }
}