void Canvas::text_transparent(PixelScalar x, PixelScalar y, const TCHAR *text) { assert(text != NULL); assert(ValidateUTF8(text)); #ifdef HAVE_GLES assert(x_offset == OpenGL::translate_x); assert(y_offset == OpenGL::translate_y); #endif if (font == NULL) return; GLTexture *texture = TextCache::Get(font, text); if (texture == NULL) return; GLEnable scope(GL_TEXTURE_2D); texture->Bind(); GLLogicOp logic_op(GL_AND_INVERTED); /* cut out the shape in black */ OpenGL::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); texture->Draw(x, y); if (text_color != COLOR_BLACK) { /* draw the text color on top */ OpenGL::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); logic_op.set(GL_OR); text_color.Set(); texture->Draw(x, y); } }
void Canvas::DrawTransparentText(int x, int y, const TCHAR *text) { assert(text != nullptr); assert(ValidateUTF8(text)); #ifdef HAVE_GLES assert(offset == OpenGL::translate); #endif if (font == nullptr) return; GLTexture *texture = TextCache::Get(*font, text); if (texture == nullptr) return; PrepareColoredAlphaTexture(text_color); #ifndef USE_GLSL GLEnable scope(GL_TEXTURE_2D); #endif const GLBlend blend(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); texture->Bind(); texture->Draw(x, y); }
void Canvas::DrawText(int x, int y, const TCHAR *text) { assert(text != nullptr); assert(ValidateUTF8(text)); #ifdef HAVE_GLES assert(offset == OpenGL::translate); #endif if (font == nullptr) return; GLTexture *texture = TextCache::Get(*font, text); if (texture == nullptr) return; if (background_mode == OPAQUE) DrawFilledRectangle(x, y, x + texture->GetWidth(), y + texture->GetHeight(), background_color); PrepareColoredAlphaTexture(text_color); #ifndef USE_GLSL GLEnable scope(GL_TEXTURE_2D); #endif const GLBlend blend(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); texture->Bind(); texture->Draw(x, y); }
void Canvas::DrawText(int x, int y, const TCHAR *text) { assert(text != nullptr); #ifndef UNICODE assert(ValidateUTF8(text)); #endif auto s = RenderText(font, text); if (s.data == nullptr) return; SDLRasterCanvas canvas(buffer); if (background_mode == OPAQUE) { OpaqueAlphaPixelOperations<SDLPixelTraits, GreyscalePixelTraits> opaque(canvas.Import(background_color), canvas.Import(text_color)); canvas.CopyRectangle<decltype(opaque), GreyscalePixelTraits> (x, y, s.width, s.height, GreyscalePixelTraits::const_pointer_type(s.data), s.pitch, opaque); } else { ColoredAlphaPixelOperations<SDLPixelTraits, GreyscalePixelTraits> transparent(canvas.Import(text_color)); canvas.CopyRectangle<decltype(transparent), GreyscalePixelTraits> (x, y, s.width, s.height, GreyscalePixelTraits::const_pointer_type(s.data), s.pitch, transparent); } }
void Canvas::DrawClippedText(int x, int y, unsigned width, unsigned height, const TCHAR *text) { assert(text != nullptr); assert(ValidateUTF8(text)); #ifdef HAVE_GLES assert(offset == OpenGL::translate); #endif if (font == nullptr) return; GLTexture *texture = TextCache::Get(*font, text); if (texture == nullptr) return; if (texture->GetHeight() < height) height = texture->GetHeight(); if (texture->GetWidth() < width) width = texture->GetWidth(); PrepareColoredAlphaTexture(text_color); #ifndef USE_GLSL GLEnable scope(GL_TEXTURE_2D); #endif const GLBlend blend(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); texture->Bind(); texture->Draw(x, y, width, height, 0, 0, width, height); }
static TCHAR * import_label(const char *src) { if (src == nullptr) return nullptr; src = TrimLeft(src); if (strcmp(src, "RAILWAY STATION") == 0 || strcmp(src, "RAILROAD STATION") == 0 || strcmp(src, "UNK") == 0) return nullptr; #ifdef _UNICODE size_t length = strlen(src); TCHAR *dest = new TCHAR[length + 1]; if (::MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, length + 1) <= 0) { delete[] dest; return nullptr; } return dest; #else if (!ValidateUTF8(src)) return nullptr; return strdup(src); #endif }
void Canvas::DrawTransparentText(int x, int y, const TCHAR *text) { assert(text != nullptr); #ifndef UNICODE assert(ValidateUTF8(text)); #endif #ifdef ENABLE_OPENGL /* * RenderText return buffer owned by TextCache, this can be delete by GUI Thread * lock is need for avoid to used alredy deleted buffer. */ TextCache::Lock(); #endif auto s = RenderText(font, text); if (s.data != nullptr) { SDLRasterCanvas canvas(buffer); ColoredAlphaPixelOperations<ActivePixelTraits, GreyscalePixelTraits> transparent(canvas.Import(text_color)); canvas.CopyRectangle<decltype(transparent), GreyscalePixelTraits> (x, y, s.width, s.height, GreyscalePixelTraits::const_pointer_type(s.data), s.pitch, transparent); } #ifdef ENABLE_OPENGL TextCache::Unlock(); #endif }
void Canvas::DrawClippedText(int x, int y, unsigned max_width, const TCHAR *text) { static TCHAR text_buffer[256]; assert(text != NULL); #ifndef UNICODE assert(ValidateUTF8(text)); #endif const TCHAR *clipped_text = text; unsigned width = Canvas::CalcTextWidth(text); if (width > max_width) { #warning "that wrong, does not handle multibyte char." unsigned new_size; fixed target_percent = fixed(max_width) / fixed(width); new_size = fixed(StringLength(text)) * target_percent; CopyString(text_buffer, text, std::min(new_size, 256u)); clipped_text = text_buffer; } #ifndef UNICODE assert(ValidateUTF8(clipped_text)); #endif auto s = RenderText(font, clipped_text); if (s.data == nullptr) return; SDLRasterCanvas canvas(buffer); if (background_mode == OPAQUE) { OpaqueAlphaPixelOperations<ActivePixelTraits, GreyscalePixelTraits> opaque(canvas.Import(background_color), canvas.Import(text_color)); canvas.CopyRectangle<decltype(opaque), GreyscalePixelTraits> (x, y, s.width, s.height, GreyscalePixelTraits::const_pointer_type(s.data), s.pitch, opaque); } else { ColoredAlphaPixelOperations<ActivePixelTraits, GreyscalePixelTraits> transparent(canvas.Import(text_color)); canvas.CopyRectangle<decltype(transparent), GreyscalePixelTraits> (x, y, s.width, s.height, GreyscalePixelTraits::const_pointer_type(s.data), s.pitch, transparent); } }
//------------------------------------------------------------------------------ PixelSize Font::TextSize(const TCHAR *text) const { assert(text != nullptr); #ifndef _UNICODE assert(ValidateUTF8(text)); #endif QFontMetrics m(this->font); return { m.width(text), m.height() }; }
gcc_pure bool IsValid() const { #ifdef _UNICODE return value != nullptr; #else assert(value != nullptr); return ValidateUTF8(value); #endif }
/** * Looks up a string of text from the current language file * * Currently very simple. Looks up the current string and current language * to find the appropriate string response. On failure will return * the string itself. * * NOTES CACHING: * - Could load the whole file or part * - qsort/bsearch good idea * - cache misses in data structure for future use * @param text The text to search for * @return The translation if found, otherwise the text itself */ const TCHAR* gettext(const TCHAR* text) { assert(language_allowed); assert(text != NULL); // If empty string or no language file is loaded -> skip the translation if (StringIsEmpty(text) || mo_file == NULL) return text; #ifdef _UNICODE // Try to lookup the english string in the map of cached TCHAR translations const tstring text2(text); translation_map::const_iterator it = translations.find(text2); if (it != translations.end()) // Return the looked up translation return it->second.c_str(); // Convert the english TCHAR string to char size_t wide_length = _tcslen(text); char original[wide_length * 4 + 1]; // If the conversion failed -> use the english original string if (::WideCharToMultiByte(CP_UTF8, 0, text, -1, original, sizeof(original), NULL, NULL) <= 0) return text; // Lookup the converted english char string in the MO file const char *translation = mo_file->lookup(original); // If the lookup failed -> use the english original string if (translation == NULL || *translation == 0 || strcmp(original, translation) == 0) return text; // Convert the translated char string to TCHAR TCHAR translation2[strlen(translation) + 1]; if (::MultiByteToWideChar(CP_UTF8, 0, translation, -1, translation2, ARRAY_SIZE(translation2)) <= 0) return text; // Add the translated TCHAR string to the cache map for the next time translations[text2] = translation2; // Return the translated TCHAR string return translations[text2].c_str(); #else // Search for the english original string in the MO file const char *translation = mo_file->lookup(text); // Return either the translated string if found or the original return translation != NULL && *translation != 0 && ValidateUTF8(translation) ? translation : text; #endif }
void Canvas::DrawOpaqueText(int x, int y, const PixelRect &rc, const TCHAR *_text) { assert(_text != nullptr); #ifndef UNICODE assert(ValidateUTF8(_text)); #endif DrawFilledRectangle(rc, background_color); DrawTransparentText(x, y, _text); }
void Canvas::DrawText(int x, int y, const TCHAR *_text, size_t length) { assert(_text != nullptr); TCHAR copy[length + 1]; *std::copy_n(_text, length, copy) = _T('\0'); #ifndef UNICODE assert(ValidateUTF8(copy)); #endif DrawText(x, y, copy); }
inline void SkyLinesTracking::Client::OnUserNameReceived(const UserNameResponsePacket &packet, size_t length) { if (length != sizeof(packet) + packet.name_length) return; /* the name follows the UserNameResponsePacket object */ const char *_name = (const char *)(&packet + 1); const std::string name(_name, packet.name_length); if (!ValidateUTF8(name.c_str())) return; UTF8ToWideConverter tname(name.c_str()); handler->OnUserName(FromBE32(packet.user_id), tname); }
void Canvas::DrawText(PixelScalar x, PixelScalar y, const TCHAR *_text, size_t length) { assert(_text != NULL); TCHAR copy[length + 1]; std::copy(_text, _text + length, copy); copy[length] = _T('\0'); #ifndef UNICODE assert(ValidateUTF8(copy)); #endif DrawText(x, y, copy); }
const PixelSize Canvas::CalcTextSize(const TCHAR *text, size_t length) const { assert(text != nullptr); TCHAR *duplicated = _tcsdup(text); duplicated[length] = 0; #ifndef UNICODE assert(ValidateUTF8(duplicated)); #endif const PixelSize size = CalcTextSize(duplicated); free(duplicated); return size; }
void Canvas::DrawTransparentText(int x, int y, const TCHAR *text) { assert(text != nullptr); #ifndef UNICODE assert(ValidateUTF8(text)); #endif auto s = RenderText(font, text); if (s.data == nullptr) return; SDLRasterCanvas canvas(buffer); ColoredAlphaPixelOperations<ActivePixelTraits, GreyscalePixelTraits> transparent(canvas.Import(text_color)); CopyTextRectangle(canvas, x, y, s.width, s.height, transparent, s); }
void Canvas::DrawText(int x, int y, const TCHAR *text) { assert(text != nullptr); #ifndef UNICODE assert(ValidateUTF8(text)); #endif auto s = RenderText(font, text); if (s.data == nullptr) return; SDLRasterCanvas canvas(buffer); CopyTextRectangle(canvas, x, y, s.width, s.height, s, text_color, background_color, background_mode == OPAQUE); }
/** * Decodes the FlarmNet.org file and puts the wanted * characters into the res pointer * @param file File handle * @param charCount Number of character to decode * @param res Pointer to be written in */ static void LoadString(const char *bytes, size_t length, TCHAR *res, size_t res_size) { const char *const end = bytes + length * 2; #ifndef _UNICODE const char *const limit = res + res_size - 2; #endif TCHAR *p = res; char tmp[3]; tmp[2] = 0; while (bytes < end) { tmp[0] = *bytes++; tmp[1] = *bytes++; /* FLARMNet files are ISO-Latin-1, which is kind of short-sighted */ const unsigned char ch = (unsigned char)strtoul(tmp, NULL, 16); #ifdef _UNICODE /* Latin-1 can be converted to WIN32 wchar_t by casting */ *p++ = ch; #else /* convert to UTF-8 on all other platforms */ if (p >= limit) break; p = Latin1ToUTF8(ch, p); #endif } *p = 0; #ifndef _UNICODE assert(ValidateUTF8(res)); #endif // Trim the string of any additional spaces StripRight(res); }
void Canvas::text(PixelScalar x, PixelScalar y, const TCHAR *text) { assert(text != NULL); assert(ValidateUTF8(text)); #ifdef HAVE_GLES assert(x_offset == OpenGL::translate_x); assert(y_offset == OpenGL::translate_y); #endif if (font == NULL) return; GLTexture *texture = TextCache::Get(font, text); if (texture == NULL) return; if (background_mode == OPAQUE) /* draw the opaque background */ DrawFilledRectangle(x, y, x + texture->GetWidth(), y + texture->GetHeight(), background_color); GLEnable scope(GL_TEXTURE_2D); texture->Bind(); GLLogicOp logic_op(GL_AND_INVERTED); if (background_mode != OPAQUE || background_color != COLOR_BLACK) { /* cut out the shape in black */ OpenGL::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); texture->Draw(x, y); } if (text_color != COLOR_BLACK) { /* draw the text color on top */ OpenGL::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); logic_op.set(GL_OR); text_color.Set(); texture->Draw(x, y); } }
const PixelSize Canvas::CalcTextSize(const TCHAR *text) const { assert(text != nullptr); #ifndef UNICODE assert(ValidateUTF8(text)); #endif PixelSize size = { 0, 0 }; if (font == nullptr) return size; /* see if the TextCache can handle this request */ size = TextCache::LookupSize(*font, text); if (size.cy > 0) return size; return TextCache::GetSize(*font, text); }
bool ProfileMap::Get(const char *key, TCHAR *value, size_t max_size) const { const char *src = Get(key); if (src == nullptr) { value[0] = _T('\0'); return false; } #ifdef _UNICODE int result = MultiByteToWideChar(CP_UTF8, 0, src, -1, value, max_size); return result > 0; #else if (!ValidateUTF8(src)) return false; CopyString(value, src, max_size); return true; #endif }
static TCHAR * import_label(const char *src) { if (src == NULL || strcmp(src, "UNK") == 0 || strcmp(src, "RAILWAY STATION") == 0 || strcmp(src, "RAILROAD STATION") == 0) return NULL; if (ispunct(src[0])) { fixed value(strtod(src + 1, NULL)); value = Units::ToUserAltitude(value); TCHAR buffer[32]; if (value > fixed(999)) _stprintf(buffer, _T("%.1f"), (double)(value / 1000)); else _stprintf(buffer, _T("%d"), (int)value); return _tcsdup(buffer); } #ifdef _UNICODE size_t length = strlen(src); TCHAR *dest = new TCHAR[length + 1]; if (::MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, length + 1) <= 0) { delete[] dest; return NULL; } return dest; #else if (!ValidateUTF8(src)) return NULL; return strdup(src); #endif }
int main(int argc, char **argv) { plan_tests(2 * ARRAY_SIZE(valid) + 2 * ARRAY_SIZE(invalid) + 2 * ARRAY_SIZE(length) + 4 * ARRAY_SIZE(crop) + ARRAY_SIZE(latin1_chars) + #ifndef _UNICODE ARRAY_SIZE(truncate_string_tests) + #endif 9 + 27); for (auto i : valid) { ok1(ValidateUTF8(i)); ok1(LengthUTF8(i) == MyLengthUTF8(i)); } for (auto i : invalid) { ok1(!ValidateUTF8(i)); ok1(!MyValidateUTF8(i)); } for (auto &l : length) { ok1(l.length == LengthUTF8(l.value)); ok1(l.length == MyLengthUTF8(l.value)); } char buffer[64]; for (auto &l : latin1_chars) { *Latin1ToUTF8(l.ch, buffer) = 0; ok1(strcmp(l.utf8, buffer) == 0); } for (auto &c : crop) { strcpy(buffer, c.input); auto *end = CropIncompleteUTF8(buffer); ok1(strcmp(c.output, buffer) == 0); ok1(end != nullptr); ok1(*end == '\0'); ok1(end == buffer + strlen(buffer)); } #ifndef _UNICODE TestTruncateString(); #endif { const char *p = "foo\xe7\x9b\xae"; auto n = NextUTF8(p); ok1(n.first == 'f'); ok1(n.second == p + 1); n = NextUTF8(p + 1); ok1(n.first == 'o'); ok1(n.second == p + 2); n = NextUTF8(p + 2); ok1(n.first == 'o'); ok1(n.second == p + 3); n = NextUTF8(p + 3); ok1(n.first == 30446); ok1(n.second == p + 6); n = NextUTF8(p + 6); ok1(n.first == 0); } /* test UnicodeToUTF8() */ buffer[0] = 1; ok1(UnicodeToUTF8(0, buffer) == buffer + 1); ok1(buffer[0] == 0); ok1(UnicodeToUTF8(' ', buffer) == buffer + 1); ok1(buffer[0] == ' '); ok1(UnicodeToUTF8(0x7f, buffer) == buffer + 1); ok1(buffer[0] == 0x7f); ok1(UnicodeToUTF8(0xa2, buffer) == buffer + 2); ok1(buffer[0] == char(0xc2)); ok1(buffer[1] == char(0xa2)); ok1(UnicodeToUTF8(0x6fb3, buffer) == buffer + 3); ok1(buffer[0] == char(0xe6)); ok1(buffer[1] == char(0xbe)); ok1(buffer[2] == char(0xb3)); ok1(UnicodeToUTF8(0xffff, buffer) == buffer + 3); ok1(buffer[0] == char(0xef)); ok1(buffer[1] == char(0xbf)); ok1(buffer[2] == char(0xbf)); ok1(UnicodeToUTF8(0x10000, buffer) == buffer + 4); ok1(buffer[0] == char(0xf0)); ok1(buffer[1] == char(0x90)); ok1(buffer[2] == char(0x80)); ok1(buffer[3] == char(0x80)); ok1(UnicodeToUTF8(0x10ffff, buffer) == buffer + 4); ok1(buffer[0] == char(0xf4)); ok1(buffer[1] == char(0x8f)); ok1(buffer[2] == char(0xbf)); ok1(buffer[3] == char(0xbf)); return exit_status(); }
void Canvas::DrawFormattedText(PixelRect *rc, const TCHAR *text, unsigned format) { assert(text != nullptr); #ifndef UNICODE assert(ValidateUTF8(text)); #endif if (font == nullptr) return; unsigned skip = font->GetLineSpacing(); unsigned max_lines = (format & DT_CALCRECT) ? -1 : (rc->bottom - rc->top + skip - 1) / skip; size_t len = _tcslen(text); TCHAR *duplicated = new TCHAR[len + 1], *p = duplicated; unsigned lines = 1; for (const TCHAR *i = text; *i != _T('\0'); ++i) { TCHAR ch = *i; if (ch == _T('\n')) { /* explicit line break */ if (++lines >= max_lines) break; ch = _T('\0'); } else if (ch == _T('\r')) /* skip */ continue; else if ((unsigned)ch < 0x20) /* replace non-printable characters */ ch = _T(' '); *p++ = ch; } *p = _T('\0'); len = p - duplicated; // simple wordbreak algorithm. looks for single spaces only, no tabs, // no grouping of multiple spaces if (format & DT_WORDBREAK) { for (size_t i = 0; i < len; i += _tcslen(duplicated + i) + 1) { PixelSize sz = CalcTextSize(duplicated + i); TCHAR *prev_p = nullptr; // remove words from behind till line fits or no more space is found while (sz.cx > rc->right - rc->left && (p = StringFindLast(duplicated + i, _T(' '))) != nullptr) { if (prev_p) *prev_p = _T(' '); *p = _T('\0'); prev_p = p; sz = CalcTextSize(duplicated + i); } if (prev_p) { lines++; if (lines >= max_lines) break; } } } if (format & DT_CALCRECT) { rc->bottom = rc->top + lines * skip; delete[] duplicated; return; } int y = (format & DT_VCENTER) && lines < max_lines ? (rc->top + rc->bottom - lines * skip) / 2 : rc->top; for (size_t i = 0; i < len; i += _tcslen(duplicated + i) + 1) { if (duplicated[i] != _T('\0')) { int x; if (format & (DT_RIGHT | DT_CENTER)) { PixelSize sz = CalcTextSize(duplicated + i); x = (format & DT_CENTER) ? (rc->left + rc->right - sz.cx)/2 : rc->right - sz.cx; // DT_RIGHT } else { // default is DT_LEFT x = rc->left; } TextAutoClipped(x, y, duplicated + i); if (format & DT_UNDERLINE) DrawHLine(x, x + CalcTextWidth(duplicated + i), y + font->GetAscentHeight() + 1, text_color); } y += skip; if (y >= rc->bottom) break; } delete[] duplicated; }
// // 2015-04-18 note by Paolo // // The original code from xcsoar was not spacing characters correctly in LK. // I have no idea if the problem exists in xcsoar, but we had to fix it here. // After quite some time, and frustration reading the confusing docs of FreeType, // I came to this solution which is much more accurate. // We use subpixels assuming we are always grid-aligned, which is true for our case. // Kerning does work, but we must always check that we are not going below the // available space, to avoid overlapping previous glyph. This is necessary since // the bitmap operations are copying, not merging, bitmaps. I have no idea how // Windows is doing this, apparently microsoft does not merge glyphs too. // Hinting is required to keep vertical alignement, which may hide a bug. // I am not sure if all of this (long and complicated) work is correct or it is instead // a workaround for a more complex problem existing elsewhere. // However it does work for us, against all odds. // Update april 21st: merging instead of copying makes the kerning process working fine. // void Font::Render(const TCHAR *text, const PixelSize size, void *_buffer) const { assert(text != nullptr); #ifndef _UNICODE assert(ValidateUTF8(text)); #endif uint8_t *buffer = (uint8_t *)_buffer; std::fill_n(buffer, BufferSize(size), 0); const FT_Face face = this->face; const FT_GlyphSlot glyph = face->glyph; #ifdef USE_KERNING bool use_kerning = FT_HAS_KERNING(face); unsigned prev_index = 0; #endif int x = 0; #ifdef FIX_HINTING FT_Pos prev_rsb_delta=0; #endif #ifndef ENABLE_OPENGL const Poco::ScopedLock<Poco::Mutex> protect(freetype_mutex); #endif while (true) { const auto n = NextChar(text); if (n.first == 0) break; const unsigned ch = n.first; text = n.second; FT_UInt i = FT_Get_Char_Index(face, ch); if (i == 0) continue; FT_Error error = FT_Load_Glyph(face, i, load_flags); if (error) continue; const FT_Glyph_Metrics metrics = glyph->metrics; #ifdef USE_KERNING if (use_kerning && x) { if (prev_index != 0) { FT_Vector delta; FT_Get_Kerning(face, prev_index, i, ft_kerning_default, &delta); #ifdef LIGHT_KERNING if (-delta.x <= metrics.horiBearingX) x += delta.x ; else x -= (metrics.horiBearingX + 64); #else x += delta.x; #endif } } prev_index = i; #endif #ifdef FIX_HINTING if (prev_rsb_delta - glyph->lsb_delta >= 32 ) x -= 64;// >> 6; else if ( prev_rsb_delta - glyph->lsb_delta < -32 ) x += 64;// >> 6; prev_rsb_delta = glyph->rsb_delta; #endif error = FT_Render_Glyph(glyph, render_mode); if (error) continue; /* * 32, 0 = Microsoft GDI weight=600 (64=32) */ if (demibold) FT_Bitmap_Embolden(ft_library,&glyph->bitmap, 32,0); RenderGlyph((uint8_t *)buffer, size.cx, size.cy, #ifdef USE_KERNING glyph, (x >> 6)+glyph->bitmap_left , ascent_height - FT_FLOOR(metrics.horiBearingY)); x += glyph->advance.x; // equivalent to metrics.horiAdvance #else glyph, (x + metrics.horiBearingX ) >> 6 , ascent_height - FT_FLOOR(metrics.horiBearingY)); x += (metrics.width > metrics.horiAdvance ? metrics.width : metrics.horiAdvance); #endif } }
TCHAR * ConvertLineReader::ReadLine() { char *narrow = source.ReadLine(); if (narrow == nullptr) return nullptr; // Check if there is byte order mark in front if (narrow[0] == (char)0xEF && narrow[1] == (char)0xBB && narrow[2] == (char)0xBF && (charset == Charset::AUTO || charset == Charset::UTF8)) { // -> if so, skip it narrow += 3; /* if it was "AUTO", then explicitly switch to UTF-8 now */ charset = Charset::UTF8; } if (charset == Charset::AUTO && !ValidateUTF8(narrow)) /* invalid UTF-8 sequence detected: switch to ISO-Latin-1 */ charset = Charset::ISO_LATIN_1; #ifdef _UNICODE size_t narrow_length = strlen(narrow); TCHAR *t = tbuffer.get(narrow_length + 1); if (t == nullptr) return nullptr; if (narrow_length == 0) { t[0] = _T('\0'); return t; } switch (charset) { case Charset::ISO_LATIN_1: iso_latin_1_to_tchar(t, narrow); break; default: int length = MultiByteToWideChar(CP_UTF8, 0, narrow, narrow_length, t, narrow_length); if (length == 0) return nullptr; t[length] = _T('\0'); break; } return t; #else switch (charset) { size_t buffer_size; const char *utf8; case Charset::ISO_LATIN_1: buffer_size = strlen(narrow) * 2 + 1; utf8 = Latin1ToUTF8(narrow, tbuffer.get(buffer_size), buffer_size); if (utf8 == nullptr) return narrow; return const_cast<char *>(utf8); case Charset::UTF8: if (!ValidateUTF8(narrow)) /* abort on invalid UTF-8 sequence */ return nullptr; /* fall through ... */ case Charset::AUTO: return narrow; } /* unreachable */ gcc_unreachable(); #endif }
/** * @brief Load language MSG file into memory * * @param fillup Switch value: * - false - load from scratch removing anything existing * - true - load over existing messages, adding only missing items filling up gaps * * @return @c false if language file problem, in this case english is reloaded from calling function */ bool LKLoadMessages(bool fillup) { TCHAR sFile[MAX_PATH]; TCHAR sPath[MAX_PATH]; TCHAR suffix[20]; #if DEBUG_GETTEXT int maxsize=0; #endif static bool doinit=true; if (doinit) { std::fill(std::begin(LKMessages), std::end(LKMessages), (TCHAR*)NULL); doinit=false; } else { if (!fillup) { // init data when reloading language files or changing it // but not in fillup mode of course LKUnloadMessage(); } } LocalPath(sPath,_T(LKD_LANGUAGE)); _tcscpy(suffix,_T("_MSG.TXT")); _stprintf(sFile,_T("%s%s%s%s"), sPath, _T(DIRSEP), LKLangSuffix, suffix); ZZIP_FILE *hFile = openzip(sFile, "rt"); if (hFile == NULL) { StartupStore(_T("... LoadText Missing Language File: <%s>%s"),sFile,NEWLINE); return false; } else { if (fillup) StartupStore(_T(". Language fillup load file: <%s>%s"),sFile,NEWLINE); else StartupStore(_T(". Language load file: <%s>%s"),sFile,NEWLINE); } // search for beginning of code index, in the range [email protected]_ [email protected]_ TCHAR sTmp[300]; TCHAR scapt[MAX_MESSAGE_SIZE+1]; TCHAR scaptraw[MAX_MESSAGE_SIZE+1]; bool havewarned=false; while (ReadULine(hFile, sTmp, array_size(sTmp))) { unsigned int slen=_tcslen(sTmp); // includes cr or lf or both if ( (slen<9) || (sTmp[0]!='_') || (sTmp[1]!='@') || (sTmp[2]!='M') ) { #if DEBUG_GETTEXT if(slen>0 && sTmp[0]!='#') { StartupStore(_T(".... MSG_ENG missing [email protected] line <%s>\n"),sTmp); } #endif continue; } // get the item index number, quick conversion from unicode unsigned short inumber = 0; for (unsigned int i = 0; i < slen - 4 && isdigit(sTmp[3 + i]); i++) { inumber = (inumber * 10) + ((char)sTmp[3 + i] - '0'); } if (inumber >=MAX_MESSAGES) { if (!havewarned) { StartupStore(_T("...... ERROR LOADING NON-COMPATIBLE MSG FILE!%s"),NEWLINE); havewarned=true; } StartupStore(_T("...... MSG token <%d> over limit! <%s>%s"),inumber,sTmp,NEWLINE); continue; } int start=0; for (unsigned i=3; i<slen; i++) { if (sTmp[i]=='\"') { start=i; break; } } int end=0; if (start==0) { #if DEBUG_GETTEXT StartupStore(_T(".... MSG_ENG no start\n")); #endif continue; } for (unsigned i=start+1; i<slen; i++) { if (sTmp[i]=='\"') { sTmp[i]='\0'; end=i; break; } } if (end==0) { #if DEBUG_GETTEXT StartupStore(_T(".... MSG_ENG no end <%s> start=%d\n"),sTmp,start); #endif continue; } int newlen; newlen=_tcslen(&sTmp[start+1]); if (newlen>MAX_MESSAGE_SIZE) { #if DEBUG_GETTEXT StartupStore(_T(".... MSG_ENG caption too big, len=%d\n"),newlen); #endif continue; } if (newlen==0) { #if DEBUG_GETTEXT StartupStore(_T(".... MSG_ENG TOKEN # %d : caption is empty, null text.\n"),inumber); #endif continue; } #if DEBUG_GETTEXT if (newlen>maxsize) maxsize=newlen; #endif // transcode special charcaters while loading from file TCHAR tcode; bool donetcode; _tcscpy(scaptraw,&sTmp[start+1]); unsigned j=0; for (unsigned i=0; i<_tcslen(scaptraw); i++) { donetcode=false; if (scaptraw[i] == '\\') { if ( (i+1) <_tcslen(scaptraw)) { switch(scaptraw[i+1]) { case 'n': tcode='\n'; i++; break; case 'r': tcode='\r'; i++; break; default: tcode='\\'; break; } scapt[j++]=tcode; donetcode=true; } } if (!donetcode) { scapt[j++]=scaptraw[i]; } } scapt[j]='\0'; if (LKMessages[inumber]) { // only for debugging translations #if TESTBENCH if (!fillup) StartupStore(_T("... INVALID LANGUAGE MESSAGE INDEX <%d> duplicated!\n"),inumber); #endif continue; } #if TESTBENCH #if (WINDOWSPC>0) // CAUTION, on a PNA this would freeze the device if language file is not updated! // StartupStore is locking and unlocking threads at each run!! if (fillup) StartupStore(_T("... Fillup: message index %d is missing from translation\n"),inumber); #endif #endif #ifndef UNICODE LKASSERT(ValidateUTF8(scapt)); #endif LKMessages[inumber] = (TCHAR *)malloc((_tcslen(scapt)+1)*sizeof(TCHAR)); LKASSERT(LKMessages[inumber]!=NULL); _tcscpy(LKMessages[inumber],scapt); if (inumber>=MAX_MESSAGES) { #if TESTBENCH StartupStore(_T("... TOO MANY MESSAGES, MAX %d%s"), MAX_MESSAGES, NEWLINE); #endif break; } } zzip_fclose(hFile); return true; }
PixelSize Font::TextSize(const TCHAR *text) const { assert(text != nullptr); #ifndef _UNICODE assert(ValidateUTF8(text)); #endif const FT_Face face = this->face; const bool use_kerning = FT_HAS_KERNING(face); int x = 0, minx = 0, maxx = 0; unsigned prev_index = 0; #ifndef ENABLE_OPENGL const ScopeLock protect(freetype_mutex); #endif while (true) { const auto n = NextChar(text); if (n.first == 0) break; const unsigned ch = n.first; text = n.second; FT_UInt i = FT_Get_Char_Index(face, ch); if (i == 0) continue; FT_Error error = FT_Load_Glyph(face, i, load_flags); if (error) continue; const FT_GlyphSlot glyph = face->glyph; const FT_Glyph_Metrics &metrics = glyph->metrics; if (use_kerning) { if (prev_index != 0 && i != 0) { FT_Vector delta; FT_Get_Kerning(face, prev_index, i, ft_kerning_default, &delta); x += delta.x >> 6; } prev_index = i; } const int glyph_minx = FT_FLOOR(metrics.horiBearingX); const int glyph_maxx = minx + FT_CEIL(metrics.width); const int glyph_advance = FT_CEIL(metrics.horiAdvance); int z = x + glyph_minx; if (z < minx) minx = z; z = x + std::max(glyph_maxx, glyph_advance); if (z > maxx) maxx = z; x += glyph_advance; }
int main(int argc, char **argv) { plan_tests(ARRAY_SIZE(valid) + ARRAY_SIZE(invalid) + ARRAY_SIZE(length) + ARRAY_SIZE(crop) + ARRAY_SIZE(latin1_chars) + 9 + 27); for (auto i : valid) ok1(ValidateUTF8(i)); for (auto i : invalid) ok1(!ValidateUTF8(i)); for (auto &l : length) ok1(l.length == LengthUTF8(l.value)); char buffer[64]; for (auto &l : latin1_chars) { *Latin1ToUTF8(l.ch, buffer) = 0; ok1(strcmp(l.utf8, buffer) == 0); } for (auto &c : crop) { strcpy(buffer, c.input); CropIncompleteUTF8(buffer); ok1(strcmp(c.output, buffer) == 0); } { const char *p = "foo\xe7\x9b\xae"; auto n = NextUTF8(p); ok1(n.first == 'f'); ok1(n.second == p + 1); n = NextUTF8(p + 1); ok1(n.first == 'o'); ok1(n.second == p + 2); n = NextUTF8(p + 2); ok1(n.first == 'o'); ok1(n.second == p + 3); n = NextUTF8(p + 3); ok1(n.first == 30446); ok1(n.second == p + 6); n = NextUTF8(p + 6); ok1(n.first == 0); } /* test UnicodeToUTF8() */ buffer[0] = 1; ok1(UnicodeToUTF8(0, buffer) == buffer + 1); ok1(buffer[0] == 0); ok1(UnicodeToUTF8(' ', buffer) == buffer + 1); ok1(buffer[0] == ' '); ok1(UnicodeToUTF8(0x7f, buffer) == buffer + 1); ok1(buffer[0] == 0x7f); ok1(UnicodeToUTF8(0xa2, buffer) == buffer + 2); ok1(buffer[0] == char(0xc2)); ok1(buffer[1] == char(0xa2)); ok1(UnicodeToUTF8(0x6fb3, buffer) == buffer + 3); ok1(buffer[0] == char(0xe6)); ok1(buffer[1] == char(0xbe)); ok1(buffer[2] == char(0xb3)); ok1(UnicodeToUTF8(0xffff, buffer) == buffer + 3); ok1(buffer[0] == char(0xef)); ok1(buffer[1] == char(0xbf)); ok1(buffer[2] == char(0xbf)); ok1(UnicodeToUTF8(0x10000, buffer) == buffer + 4); ok1(buffer[0] == char(0xf0)); ok1(buffer[1] == char(0x90)); ok1(buffer[2] == char(0x80)); ok1(buffer[3] == char(0x80)); ok1(UnicodeToUTF8(0x10ffff, buffer) == buffer + 4); ok1(buffer[0] == char(0xf4)); ok1(buffer[1] == char(0x8f)); ok1(buffer[2] == char(0xbf)); ok1(buffer[3] == char(0xbf)); return exit_status(); }