psemek/libs/ui/source/bmfont.cpp

101 lines
3.4 KiB
C++

#include <psemek/ui/bmfont.hpp>
#include <psemek/util/to_string.hpp>
#include <boost/preprocessor/stringize.hpp>
#define RAPIDJSON_ASSERT(x) if (!(x)) throw ::std::runtime_error("Error parsing font description: " BOOST_PP_STRINGIZE(x));
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
namespace psemek::ui
{
namespace
{
std::string_view to_string(rapidjson::ParseErrorCode const & code)
{
using namespace rapidjson;
switch (code)
{
case kParseErrorNone: return "no error";
case kParseErrorDocumentEmpty: return "document is empty";
case kParseErrorDocumentRootNotSingular: return "document root must not be followed by other values";
case kParseErrorValueInvalid: return "invalid value";
case kParseErrorObjectMissName: return "missing a name for object member";
case kParseErrorObjectMissColon: return "missing a colon after a name of object member";
case kParseErrorObjectMissCommaOrCurlyBracket: return "missing a comma or '}' after an object member";
case kParseErrorArrayMissCommaOrSquareBracket: return "missing a comma or ']' after an array element";
case kParseErrorStringUnicodeEscapeInvalidHex: return R"(Incorrect hex digit after \\u escape in string)";
case kParseErrorStringUnicodeSurrogateInvalid: return "surrogate pair in string is invalid";
case kParseErrorStringEscapeInvalid: return "invalid escape character in string";
case kParseErrorStringMissQuotationMark: return "missing a closing quotation mark in string";
case kParseErrorStringInvalidEncoding: return "invalid encoding in string";
case kParseErrorNumberTooBig: return "number too big to be stored in double";
case kParseErrorNumberMissFraction: return "missing fraction part in number";
case kParseErrorNumberMissExponent: return "missing exponent in number";
case kParseErrorTermination: return "parsing was terminated";
case kParseErrorUnspecificSyntaxError: return "unspecific syntax error";
default: return "(unknown error)";
}
};
}
bmfont_data bmfont_data::parse(io::istream && stream)
{
auto description_str = io::read_full(std::move(stream));
description_str.push_back(0);
rapidjson::Document document;
document.ParseInsitu(description_str.data());
if (document.HasParseError())
throw std::runtime_error(util::to_string("Error msdf font description: ", to_string(document.GetParseError()), " at ", document.GetErrorOffset()));
bmfont_data result;
{
auto const & info = document["info"];
result.name = info["face"].GetString();
int size = info["size"].GetInt();
result.size = {size, size};
}
if (document.HasMember("distanceField"))
{
auto const & sdf = document["distanceField"];
result.sdf_scale = sdf["distanceRange"].GetFloat();
}
{
auto const & sdf = document["common"];
result.baseline = sdf["base"].GetInt();
}
{
auto chars = document["chars"].GetArray();
for (auto const & charInfo : chars)
{
char32_t id = charInfo["id"].GetUint();
auto & data = result.glyphs[id];
data.start_x = charInfo["x"].GetInt();
data.start_y = charInfo["y"].GetInt();
data.size_x = charInfo["width"].GetInt();
data.size_y = charInfo["height"].GetInt();
data.offset_x = charInfo["xoffset"].GetInt();
data.offset_y = charInfo["yoffset"].GetInt();
data.advance = charInfo["xadvance"].GetInt();
data.offset_y = - data.offset_y - data.size_y;
}
}
return result;
}
}