bwidgets/src/core/font.cpp

146 lines
4.3 KiB
C++

#include <functional>
#include <memory>
#include <stack>
#include <stdexcept>
#include <fontconfig/fontconfig.h>
#include <basic_widgets/core/error_helper.hpp>
#include <basic_widgets/core/font.hpp>
#include <basic_widgets/core/type/fc_error.hpp>
using namespace bwidgets;
const Color Font::default_color_bg {255, 255, 255, SDL_ALPHA_OPAQUE};
const Color Font::default_color_fg {0, 0, 0, SDL_ALPHA_OPAQUE};
Font::Font(TTF_Font* f)
: _data {ptr_or_throw<SDLError>(f)},
ascent {TTF_FontAscent(f)},
descent {TTF_FontDescent(f)},
faces {TTF_FontFaces(f)},
family_name {TTF_FontFaceFamilyName(f)},
fixed_width {TTF_FontFaceIsFixedWidth(f) != 0},
height {TTF_FontHeight(f)},
line_skip {TTF_FontLineSkip(f)},
style_name {TTF_FontFaceStyleName(f)}
{}
Font::Font(const std::string_view file, const int size)
: Font {TTF_OpenFont(file.data(), size)}
{}
auto Font::hinting() const noexcept -> Font::Hinting
{
return static_cast<Hinting>(TTF_GetFontHinting(_data.get()));
}
auto Font::hinting(const Font::Hinting h) noexcept -> Font*
{
TTF_SetFontHinting(_data.get(), static_cast<int>(h));
return this;
}
auto Font::kerning() const noexcept -> bool
{
return TTF_GetFontKerning(_data.get()) != 0;
}
auto Font::kerning(const bool allowed) noexcept -> Font*
{
TTF_SetFontKerning(_data.get(), static_cast<int>(allowed));
return this;
}
auto Font::outline() const noexcept -> int
{
return TTF_GetFontOutline(_data.get());
}
auto Font::outline(const int size) noexcept -> Font*
{
TTF_SetFontOutline(_data.get(), size);
return this;
}
auto Font::render(const RenderMode m, const std::string_view str, const Color fg,
const Color bg) -> std::unique_ptr<SDL_Surface, Deleter>
{
const char* const c_str = str.empty() ? " " : str.data();
auto renderer = [bg, c_str, fg, m, this]() -> std::function<SDL_Surface*()> {
switch (m) {
case RenderMode::BLENDED:
return [fg, c_str, this]() {
return TTF_RenderUTF8_Blended(_data.get(), c_str, fg());
};
case RenderMode::SHADED:
return [bg, fg, c_str, this]() {
return TTF_RenderUTF8_Shaded(_data.get(), c_str, fg(), bg());
};
case RenderMode::SOLID:
return [fg, c_str, this]() {
return TTF_RenderUTF8_Solid(_data.get(), c_str, fg());
};
default:
throw std::logic_error("missing switch case.");
}
}();
return std::unique_ptr<SDL_Surface, Deleter>(ptr_or_throw<SDLError>(renderer()));
}
auto Font::style() const noexcept -> uint8_t
{
return TTF_GetFontStyle(_data.get());
}
auto Font::style(const uint8_t s) noexcept -> Font*
{
TTF_SetFontStyle(_data.get(), s);
return this;
}
auto Font::text_size(const std::string_view str) const noexcept -> Size
{
Size s {};
TTF_SizeUTF8(_data.get(), str.data(), &s.w, &s.h);
return s;
}
auto Font::find(const std::string_view pat) -> std::string
{
const auto conf = std::unique_ptr<FcConfig, Deleter>(
ptr_or_throw<FCError>(FcInitLoadConfigAndFonts(), "fontconfig init failed."));
const auto pattern = [&conf, pat]() {
auto pattern = std::unique_ptr<FcPattern, Deleter>(ptr_or_throw<FCError>(
FcNameParse(reinterpret_cast<const FcChar8*>(pat.data())),
"pattern parsing failed"));
success_or_throw<FCError, FcBool>(
FcConfigSubstitute(conf.get(), pattern.get(), FcMatchPattern),
"FcConfigSubstitute failed", [](auto code) { return code == FcTrue; });
return pattern;
}();
FcDefaultSubstitute(pattern.get());
std::string file_path = [&conf, &pattern]() {
FcResult res {};
const auto font = std::unique_ptr<FcPattern, Deleter>(ptr_or_throw<FCError>(
FcFontMatch(conf.get(), pattern.get(), &res), "no font found."));
if (FcChar8* file = nullptr;
FcPatternGetString(font.get(), FC_FILE, 0, &file) == FcResultMatch)
{
// "I know what I'm doing"
// NOLINTNEXTLINE
std::string _file_path {reinterpret_cast<char*>(file)};
return _file_path;
}
throw FCError("no font found.");
}();
return file_path;
}