150 lines
4.4 KiB
C++
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;
|
|
}
|