void String::decodeText(char *buffer, char *shortVersion, int sizeBuffer, int sizeShortVersion) { decodeText(buffer, sizeBuffer); if (!*buffer) { *shortVersion = '\0'; return; } // Handle control codes: char *to=buffer; int len=strlen(to); int IsShortName=0; while (len > 0) { int l = Utf8CharLen(to); unsigned char *p = (unsigned char *)to; if (l == 2 && *p == 0xC2) // UTF-8 sequence p++; if (*p == 0x86 || *p == 0x87) { IsShortName += (*p == 0x86) ? 1 : -1; memmove(to, to + l, len - l + 1); // we also copy the terminating 0! len -= l; l = 0; } if (l && IsShortName) { if (l < sizeShortVersion) { for (int i = 0; i < l; i++) *shortVersion++ = to[i]; sizeShortVersion -= l; } } to += l; len -= l; } *shortVersion = '\0'; }
/** * Insert a string into the text buffer. If maxwidth of the Textbuf is zero, * we don't care about the visual-length but only about the physical * length of the string. * @param str String to insert. * @param marked Replace the currently marked text with the new text. * @param caret Move the caret to this point in the insertion string. * @param insert_location Position at which to insert the string. * @param replacement_end Replace all characters from #insert_location up to this location with the new string. * @return True on successful change of Textbuf, or false otherwise. */ bool Textbuf::InsertString(const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end) { uint16 insertpos = (marked && this->marklength != 0) ? this->markpos : this->caretpos; if (insert_location != NULL) { insertpos = insert_location - this->buf; if (insertpos > this->bytes) return false; if (replacement_end != NULL) { this->DeleteText(insertpos, replacement_end - this->buf, str == NULL); } } else { if (marked) this->DiscardMarkedText(str == NULL); } if (str == NULL) return false; uint16 bytes = 0, chars = 0; WChar c; for (const char *ptr = str; (c = Utf8Consume(&ptr)) != '\0';) { if (!IsValidChar(c, this->afilter)) break; byte len = Utf8CharLen(c); if (this->bytes + bytes + len > this->max_bytes) break; if (this->chars + chars + 1 > this->max_chars) break; bytes += len; chars++; /* Move caret if needed. */ if (ptr == caret) this->caretpos = insertpos + bytes; } if (bytes == 0) return false; if (marked) { this->markpos = insertpos; this->markend = insertpos + bytes; } memmove(this->buf + insertpos + bytes, this->buf + insertpos, this->bytes - insertpos); memcpy(this->buf + insertpos, str, bytes); this->bytes += bytes; this->chars += chars; if (!marked && caret == NULL) this->caretpos += bytes; assert(this->bytes <= this->max_bytes); assert(this->chars <= this->max_chars); this->buf[this->bytes - 1] = '\0'; // terminating zero this->UpdateStringIter(); this->UpdateWidth(); this->UpdateCaretPosition(); this->UpdateMarkedText(); return true; }
/** * Insert a character to a textbuffer. If maxwidth of the Textbuf is zero, * we don't care about the visual-length but only about the physical * length of the string * @param key Character to be inserted * @return Return true on successful change of Textbuf, or false otherwise */ bool Textbuf::InsertChar(WChar key) { uint16 len = (uint16)Utf8CharLen(key); if (this->bytes + len <= this->max_bytes && this->chars + 1 <= this->max_chars) { memmove(this->buf + this->caretpos + len, this->buf + this->caretpos, this->bytes - this->caretpos); Utf8Encode(this->buf + this->caretpos, key); this->chars++; this->bytes += len; this->caretpos += len; this->UpdateStringIter(); this->UpdateWidth(); this->UpdateCaretPosition(); this->UpdateMarkedText(); return true; } return false; }
/** * Copy and convert old custom names to UTF-8. * They were all stored in a 512 by 32 (200 by 24 for TTO) long string array * and are now stored with stations, waypoints and other places with names. * @param id the StringID of the custom name to clone. * @return the clones custom name. */ char *CopyFromOldName(StringID id) { /* Is this name an (old) custom name? */ if (GB(id, 11, 5) != 15) return NULL; if (CheckSavegameVersion(37)) { /* Old names were 24/32 characters long, so 128 characters should be * plenty to allow for expansion when converted to UTF-8. */ char tmp[128]; uint offs = _savegame_type == SGT_TTO ? 24 * GB(id, 0, 8) : 32 * GB(id, 0, 9); const char *strfrom = &_old_name_array[offs]; char *strto = tmp; for (; *strfrom != '\0'; strfrom++) { WChar c = (byte)*strfrom; /* Map from non-ISO8859-15 characters to UTF-8. */ switch (c) { case 0xA4: c = 0x20AC; break; // Euro case 0xA6: c = 0x0160; break; // S with caron case 0xA8: c = 0x0161; break; // s with caron case 0xB4: c = 0x017D; break; // Z with caron case 0xB8: c = 0x017E; break; // z with caron case 0xBC: c = 0x0152; break; // OE ligature case 0xBD: c = 0x0153; break; // oe ligature case 0xBE: c = 0x0178; break; // Y with diaresis default: break; } /* Check character will fit into our buffer. */ if (strto + Utf8CharLen(c) > lastof(tmp)) break; strto += Utf8Encode(strto, c); } /* Terminate the new string and copy it back to the name array */ *strto = '\0'; return strdup(tmp); } else { /* Name will already be in UTF-8. */ return strdup(&_old_name_array[32 * GB(id, 0, 9)]); } }
/** * Copy and convert old custom names to UTF-8. * They were all stored in a 512 by 32 (200 by 24 for TTO) long string array * and are now stored with stations, waypoints and other places with names. * @param id the StringID of the custom name to clone. * @return the clones custom name. */ char *CopyFromOldName(StringID id) { /* Is this name an (old) custom name? */ if (GetStringTab(id) != TEXT_TAB_OLD_CUSTOM) return NULL; if (IsSavegameVersionBefore(SLV_37)) { /* Allow for expansion when converted to UTF-8. */ char tmp[LEN_OLD_STRINGS * MAX_CHAR_LENGTH]; uint offs = _savegame_type == SGT_TTO ? LEN_OLD_STRINGS_TTO * GB(id, 0, 8) : LEN_OLD_STRINGS * GB(id, 0, 9); const char *strfrom = &_old_name_array[offs]; char *strto = tmp; for (; *strfrom != '\0'; strfrom++) { WChar c = (byte)*strfrom; /* Map from non-ISO8859-15 characters to UTF-8. */ switch (c) { case 0xA4: c = 0x20AC; break; // Euro case 0xA6: c = 0x0160; break; // S with caron case 0xA8: c = 0x0161; break; // s with caron case 0xB4: c = 0x017D; break; // Z with caron case 0xB8: c = 0x017E; break; // z with caron case 0xBC: c = 0x0152; break; // OE ligature case 0xBD: c = 0x0153; break; // oe ligature case 0xBE: c = 0x0178; break; // Y with diaresis default: break; } /* Check character will fit into our buffer. */ if (strto + Utf8CharLen(c) > lastof(tmp)) break; strto += Utf8Encode(strto, c); } /* Terminate the new string and copy it back to the name array */ *strto = '\0'; return stredup(tmp); } else { /* Name will already be in UTF-8. */ return stredup(&_old_name_array[LEN_OLD_STRINGS * GB(id, 0, 9)]); } }
static void StripControlCharacters(char *s) { if (s) { int len = strlen(s); while (len > 0) { int l = Utf8CharLen(s); uchar *p = (uchar *)s; if (l == 2 && *p == 0xC2) // UTF-8 sequence p++; if (*p == 0x86 || *p == 0x87) { memmove(s, p + 1, len - l + 1); // we also copy the terminating 0! len -= l; l = 0; } s += l; len -= l; } } }
// originally from libdtv, Copyright Rolf Hakenes <*****@*****.**> void String::decodeText(char *buffer, int size) { const unsigned char *from=data.getData(0); char *to=buffer; int len=getLength(); if (len <= 0) { *to = '\0'; return; } bool singleByte; const char *cs = getCharacterTable(from, len, &singleByte); if (singleByte && SystemCharacterTableIsSingleByte || !convertCharacterTable((const char *)from, len, to, size, cs)) { if (len >= size) len = size - 1; strncpy(to, (const char *)from, len); to[len] = 0; } else len = strlen(to); // might have changed // Handle control codes: while (len > 0) { int l = Utf8CharLen(to); if (l <= 2) { unsigned char *p = (unsigned char *)to; if (l == 2 && *p == 0xC2) // UTF-8 sequence p++; bool Move = true; switch (*p) { case 0x8A: *to = '\n'; break; case 0xA0: *to = ' '; break; default: Move = false; } if (l == 2 && Move) { memmove(p, p + 1, len - 1); // we also copy the terminating 0! len -= 1; l = 1; } } to += l; len -= l; } }
static bool DrawScrollingStatusText(const NewsItem *ni, int scroll_pos, int left, int right, int top, int bottom) { CopyInDParam(0, ni->params, lengthof(ni->params)); StringID str = ni->string_id; char buf[512]; GetString(buf, str, lastof(buf)); const char *s = buf; char buffer[256]; char *d = buffer; const char *last = lastof(buffer); for (;;) { WChar c = Utf8Consume(&s); if (c == 0) { break; } else if (c == '\n') { if (d + 4 >= last) break; d[0] = d[1] = d[2] = d[3] = ' '; d += 4; } else if (IsPrintable(c)) { if (d + Utf8CharLen(c) >= last) break; d += Utf8Encode(d, c); } } *d = '\0'; DrawPixelInfo tmp_dpi; if (!FillDrawPixelInfo(&tmp_dpi, left, top, right - left, bottom)) return true; int width = GetStringBoundingBox(buffer).width; int pos = (_current_text_dir == TD_RTL) ? (scroll_pos - width) : (right - scroll_pos - left); DrawPixelInfo *old_dpi = _cur_dpi; _cur_dpi = &tmp_dpi; DrawString(pos, INT16_MAX, 0, buffer, TC_LIGHT_BLUE, SA_LEFT | SA_FORCE); _cur_dpi = old_dpi; return (_current_text_dir == TD_RTL) ? (pos < right - left) : (pos + width > 0); }
/** * Update Textbuf type with its actual physical character and screenlength * Get the count of characters in the string as well as the width in pixels. * Useful when copying in a larger amount of text at once */ void Textbuf::UpdateSize() { const char *buf = this->buf; this->chars = this->bytes = 1; // terminating zero WChar c; while ((c = Utf8Consume(&buf)) != '\0') { this->bytes += Utf8CharLen(c); this->chars++; } assert(this->bytes <= this->max_bytes); assert(this->chars <= this->max_chars); this->caretpos = this->bytes - 1; this->UpdateStringIter(); this->UpdateWidth(); this->UpdateMarkedText(); this->UpdateCaretPosition(); }
/** * Convert to OpenTTD's encoding from that of the local environment. * When the project is built in UNICODE, the system codepage is irrelevant and * the input string is wide. In ANSI mode, the string is in the * local codepage which we'll convert to wide-char, and then to UTF-8. * OpenTTD internal encoding is UTF8. * The returned value's contents can only be guaranteed until the next call to * this function. So if the value is needed for anything else, use convert_from_fs * @param name pointer to a valid string that will be converted (local, or wide) * @return pointer to the converted string; if failed string is of zero-length * @see the current code-page comes from video\win32_v.cpp, event-notification * WM_INPUTLANGCHANGE */ const char *FS2OTTD(const TCHAR *name) { static char utf8_buf[512]; #if defined(UNICODE) return convert_from_fs(name, utf8_buf, lengthof(utf8_buf)); #else char *s = utf8_buf; for (; *name != '\0'; name++) { wchar_t w; int len = MultiByteToWideChar(_codepage, 0, name, 1, &w, 1); if (len != 1) { DEBUG(misc, 0, "[utf8] M2W error converting '%c'. Errno %lu", *name, GetLastError()); continue; } if (s + Utf8CharLen(w) >= lastof(utf8_buf)) break; s += Utf8Encode(s, w); } *s = '\0'; return utf8_buf; #endif /* UNICODE */ }
int GetInternalCharLength(WChar c) const { /* ICU uses UTF-16 internally which means we need to account for surrogate pairs. */ return Utf8CharLen(c) < 4 ? 1 : 2; }