#include #include #include #include #define RAPIDJSON_ASSERT(x) if (!(x)) throw ::psemek::util::exception("Error parsing font description: " BOOST_PP_STRINGIZE(x)); #define RAPIDJSON_NOEXCEPT_ASSERT(x) #include #include namespace psemek::fonts { 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)).string(); rapidjson::Document document; document.ParseInsitu(description_str.data()); if (document.HasParseError()) throw util::exception(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"].GetFloat(); } { 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"].GetFloat(); data.start_y = charInfo["y"].GetFloat(); data.size_x = charInfo["width"].GetFloat(); data.size_y = charInfo["height"].GetFloat(); data.offset_x = charInfo["xoffset"].GetFloat(); data.offset_y = charInfo["yoffset"].GetFloat(); data.advance = charInfo["xadvance"].GetFloat(); data.offset_y = - data.offset_y - data.size_y + result.size[1]; } } return result; } }