bwidgets/src/core/font.cpp

157 lines
4.3 KiB
C++

#include <functional>
#include <stack>
#include <fontconfig/fontconfig.h>
#include <basic_widgets/core/font.hpp>
#include <basic_widgets/core/type/fc_error.hpp>
#include <basic_widgets/core/type/sdl_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, __FILE__, __FUNCTION__, __LINE__),
[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& file, int size) : Font(TTF_OpenFont(file.c_str(), size)) {}
auto Font::hinting() const noexcept -> Font::Hinting
{
return (Hinting)TTF_GetFontHinting(_data());
}
auto Font::hinting(const Font::Hinting h) noexcept -> Font*
{
TTF_SetFontHinting(_data(), (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& str, const Color& fg,
const Color& bg) -> SDL_Surface*
{
std::function<SDL_Surface*()> renderer;
const char* c_str = str.empty() ? " " : str.c_str();
switch (m) {
case RenderMode::BLENDED:
renderer = [&fg, c_str, this]() {
return TTF_RenderUTF8_Blended(_data(), c_str, fg());
};
break;
case RenderMode::SHADED:
renderer = [&bg, &fg, c_str, this]() {
return TTF_RenderUTF8_Shaded(_data(), c_str, fg(), bg());
};
break;
case RenderMode::SOLID:
renderer = [&fg, c_str, this]() {
return TTF_RenderUTF8_Solid(_data(), c_str, fg());
};
break;
}
return ptr_or_throw<SDLError>(renderer(), __FILE__, __FUNCTION__, __LINE__);
}
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& str) const noexcept -> Size
{
Size s {};
TTF_SizeUTF8(_data(), str.c_str(), &s.w, &s.h);
return s;
}
auto Font::find(const std::string& pat) -> std::string
{
std::stack<std::function<void()>> cleaners;
FcConfig* conf = ptr_or_throw<FCError>(FcInitLoadConfigAndFonts(), __FILE__,
__FUNCTION__, __LINE__, "init failed");
cleaners.emplace([conf]() { FcConfigDestroy(conf); });
FcPattern* pattern = nullptr;
try {
pattern =
ptr_or_throw<FCError>(FcNameParse((FcChar8*)pat.c_str()), __FILE__,
__FUNCTION__, __LINE__, "pattern parsing failed");
cleaners.emplace([pattern]() { FcPatternDestroy(pattern); });
if (FcConfigSubstitute(conf, pattern, FcMatchPattern) == FcFalse)
throw FCError {__FILE__, __FUNCTION__, __LINE__,
"FcConfigSubstitute failed"};
} catch (const std::exception& e) {
while (!cleaners.empty()) {
cleaners.top()();
cleaners.pop();
}
throw e;
}
FcDefaultSubstitute(pattern);
std::string file_path;
FcResult res {};
FcPattern* font = FcFontMatch(conf, pattern, &res);
if (font != nullptr) {
FcChar8* file = nullptr;
if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) {
file_path = (char*)file;
}
FcPatternDestroy(font);
}
while (!cleaners.empty()) {
cleaners.top()();
cleaners.pop();
}
if (file_path.empty())
throw FCError {__FILE__, __FUNCTION__, __LINE__, "no font found"};
return file_path;
}