bwidgets/src/core/font.cpp

150 lines
4.4 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)
: Wrapper {ptr_or_throw<SDLError>(f), [f](TTF_Font*) noexcept { TTF_CloseFont(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()));
}
auto Font::hinting(const Font::Hinting h) noexcept -> Font*
{
TTF_SetFontHinting(_data(), static_cast<int>(h));
return this;
}
auto Font::kerning() const noexcept -> bool
{
return TTF_GetFontKerning(_data()) != 0;
}
auto Font::kerning(const bool allowed) noexcept -> Font*
{
TTF_SetFontKerning(_data(), static_cast<int>(allowed));
return this;
}
auto Font::outline() const noexcept -> int
{
return TTF_GetFontOutline(_data());
}
auto Font::outline(const int size) noexcept -> Font*
{
TTF_SetFontOutline(_data(), size);
return this;
}
auto Font::render(const RenderMode m, const std::string_view str, const Color fg,
const Color bg) -> OpaqueStruct<SDL_Surface>
{
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(), c_str, fg());
};
case RenderMode::SHADED:
return [bg, fg, c_str, this]() {
return TTF_RenderUTF8_Shaded(_data(), c_str, fg(), bg());
};
case RenderMode::SOLID:
return [fg, c_str, this]() {
return TTF_RenderUTF8_Solid(_data(), c_str, fg());
};
default:
throw std::logic_error("missing switch case.");
}
}();
return {ptr_or_throw<SDLError>(renderer()), [](auto* ptr) { SDL_FreeSurface(ptr); }};
}
auto Font::style() const noexcept -> uint8_t
{
return TTF_GetFontStyle(_data());
}
auto Font::style(const uint8_t s) noexcept -> Font*
{
TTF_SetFontStyle(_data(), s);
return this;
}
auto Font::text_size(const std::string_view str) const noexcept -> Size
{
Size s {};
TTF_SizeUTF8(_data(), str.data(), &s.w, &s.h);
return s;
}
auto Font::find(const std::string_view pat) -> std::string
{
auto conf = std::make_unique<OpaqueStruct<FcConfig>>(
ptr_or_throw<FCError>(FcInitLoadConfigAndFonts(), "init failed"),
[](auto* ptr) { FcConfigDestroy(ptr); });
auto pattern = [&conf, pat]() {
auto pattern = std::make_unique<OpaqueStruct<FcPattern>>(
ptr_or_throw<FCError>(
FcNameParse(reinterpret_cast<const FcChar8*>(pat.data())),
"pattern parsing failed"),
[](auto* ptr) { FcPatternDestroy(ptr); });
success_or_throw<FCError, FcBool>(
FcConfigSubstitute((*conf)(), (*pattern)(), FcMatchPattern),
"FcConfigSubstitute failed", [](auto code) { return code == FcTrue; });
return pattern;
}();
FcDefaultSubstitute((*pattern)());
std::string file_path = [&conf, &pattern]() {
FcResult res {};
auto font = std::make_unique<OpaqueStruct<FcPattern>>(
ptr_or_throw<FCError>(FcFontMatch((*conf)(), (*pattern)(), &res),
"no font found."),
[](auto* ptr) { FcPatternDestroy(ptr); });
if (FcChar8* file = nullptr;
FcPatternGetString((*font)(), FC_FILE, 0, &file) == FcResultMatch)
{
// "I know what I'm doing"
std::string _file_path {reinterpret_cast<char*>(file)};
FcPatternDestroy((*font)());
return _file_path;
}
throw FCError("no font found.");
}();
return file_path;
}