157 lines
4.3 KiB
C++
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;
|
|
}
|