#include "graphics/software/NVGFont.h" #include "graphics/software/font_internal.h" #include "graphics/paths/PathRenderer.h" #include "mod_table/mod_table.h" #include "options/Option.h" #include "localization/localize.h" #include namespace { const char* const TOKEN_SEPARATORS = "\n\t\r"; } namespace font { size_t NVGFont::getTokenLength(const char *string, size_t maxLength) { Assert(string != NULL); size_t length = 0; if (maxLength <= 0) return 0; if (!Unicode_text_mode) { // If we are not in unicode mode then we need to handle special characters // Otherwise we just assume that the text is UTF-8 encoded if (*string >= Lcl_special_chars || *string < 0) return 1; } const char *nullPtr = strchr(string, '\0'); const char *nextToken = strpbrk(string, TOKEN_SEPARATORS); // WOHOO! Pointer arithmetic!!! if (nullPtr != NULL && (nextToken == NULL || nullPtr < nextToken)) { length = nullPtr - string; } else if (nextToken != NULL) { if (nextToken == string) { length = 1; } else { length = nextToken - string; } } else { length = strlen(string); } if (length > maxLength) { length = maxLength; } if (!Unicode_text_mode) { // Same as above for (size_t i = 0; i < length; i++) { if (string[i] >= Lcl_special_chars || string[i] < 0) { // Special character needs to be handled separately return i; } } } return length; } void NVGFont::setHandle(int handle) { Assertion(handle >= 0, "Invalid font handle passed!"); m_handle = handle; } void NVGFont::setSize(float size) { Assertion(size > 0.f, "Invalid size %f passed!", size); m_size = size; } void NVGFont::setLetterSpacing(float spacing) { Assertion(spacing >= 0.0f, "Invalid letter spacing passed!"); m_letterSpacing = spacing; } void NVGFont::setSpecialCharacterFont(font* fontData) { Assertion(fontData != nullptr, "Invalid font data pointer passed!"); m_specialCharacters = fontData; } float NVGFont::getTextHeight() const { return m_lineHeight; } void NVGFont::getStringSize(const char *text, size_t textLen, int resize_mode, float *width, float *height, float scaleMultiplier) const { using namespace graphics::paths; auto path = PathRenderer::instance(); path->saveState(); path->resetState(); float scale_factor = (canScale && !Fred_running) ? get_font_scale_factor() : 1.0f; scale_factor *= scaleMultiplier; path->fontFaceId(m_handle); path->fontSize(m_size * scale_factor); path->textLetterSpacing(m_letterSpacing); path->textAlign(static_cast(ALIGN_TOP | ALIGN_LEFT)); if (resize_mode != -1) { float scaleX = 1.0f; float scaleY = 1.0f; gr_resize_screen_posf(nullptr, nullptr, &scaleX, &scaleY, resize_mode); path->scale(scaleX, scaleY); } float w = 0.0f; float h = this->getHeight() * scale_factor; size_t tokenLength; const char *s = text; bool specialChar = false; float lineWidth = 0.0f; while ((tokenLength = getTokenLength(s, textLen)) > 0) { if (tokenLength > textLen) { tokenLength = textLen; } if (tokenLength == 1) { // We may have encountered a special character switch (*s) { case '\n': specialChar = true; h += this->getHeight() * scale_factor; lineWidth = 0.0f; break; case '\t': specialChar = true; lineWidth += this->getTabWidth() * scale_factor; break; default: if (!Unicode_text_mode) { // Same as before, this code is only needed in non-unicode mode if (*s >= Lcl_special_chars || *s < 0) { specialChar = true; int charWidth; int spacing; if (m_specialCharacters == nullptr) { Error(LOCATION, "Font %s has no special characters font! This is usually caused by ignoring a font table parsing warning.", getName().c_str()); } get_char_width_old(m_specialCharacters, static_cast(*s), '\0', &charWidth, &spacing); lineWidth += i2fl(spacing); } } break; } } if (!specialChar) { lineWidth += path->textBounds(0.f, 0.f, s, s + tokenLength, nullptr); } w = MAX(w, lineWidth); specialChar = false; s = s + tokenLength; textLen -= tokenLength; // see above, tokenLength is at most equal to textLen so this == 0 comparision should be safe if (textLen == 0) { break; } } if (height) *height = h; if (width) *width = w; path->restoreState(); } void NVGFont::computeFontMetrics() { auto path = graphics::paths::PathRenderer::instance(); path->saveState(); path->resetState(); path->fontFaceId(m_handle); path->fontSize(m_size); path->textLetterSpacing(m_letterSpacing); path->textMetrics(&_ascender, &_descender, &m_lineHeight); path->restoreState(); _height = m_lineHeight + this->offsetTop + this->offsetBottom; checkFontMetrics(); } }