potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

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;
    }
}