Files
asarium 43e540cc4c Add true Unicode support
This adds proper Unicode support by allowing the parse system to read
UTF-8 encoded files. The new mode is controlled by a mod option which
allows older mods to continue using the legacy text encoding.

That option changes the behavior of several systems in FSO. It disallows
the usage of Volition fonts since they don't support Unicode encoding
and changes a few usages of special characters to their unicode
counterparts of generic rendering solutions.

This fixes #1415.
2017-11-30 13:26:20 +01:00

252 lines
5.0 KiB
C++

#include "graphics/software/NVGFont.h"
#include "graphics/paths/PathRenderer.h"
#include "mod_table/mod_table.h"
#include "localization/localize.h"
#include <limits>
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(const_cast<char*>(string), '\0');
const char *nextToken = strpbrk(const_cast<char*>(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 seperately
return i;
}
}
}
return length;
}
NVGFont::NVGFont() : m_handle(-1), m_letterSpacing(0.0f), m_size(12.0f), m_tabWidth(20.0f)
{
}
NVGFont::~NVGFont()
{
}
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::setTabWidth(float tabWidth)
{
Assertion(tabWidth >= 0.0f, "Invalid tab width passed!");
m_tabWidth = tabWidth;
}
void NVGFont::setSpecialCharacterFont(font* fontData)
{
Assertion(fontData != nullptr, "Invalid font data pointer passed!");
m_specialCharacters = fontData;
}
float NVGFont::getTextHeight() const
{
return m_lineHeight;
}
extern int get_char_width_old(font* fnt, ubyte c1, ubyte c2, int *width, int* spacing);
void NVGFont::getStringSize(const char *text, size_t textLen, int resize_mode, float *width, float *height) const
{
using namespace graphics::paths;
auto path = PathRenderer::instance();
path->saveState();
path->resetState();
path->fontFaceId(m_handle);
path->fontSize(m_size);
path->textLetterSpacing(m_letterSpacing);
path->textAlign(static_cast<TextAlign>(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();
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();
lineWidth = 0.0f;
break;
case '\t':
specialChar = true;
lineWidth += this->getTabWidth();
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<ubyte>(*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();
}
}