Files
LCE-Revelations/Minecraft.Client/Common/UI/UIFontData.h
Revela d7822ac81e Enable multi-language font rendering and Unicode text input
Goal:
Allow players to type and display text in any language supported by
Unicode, including Chinese, Japanese, Korean, Thai, Arabic, Korean, Hindi, and more. This
covers all text surfaces: chat editor, chat messages, signs (in-world
and editor), world name/seed, server address/port fields, and all
Iggy Flash UI text fields.

Multi-language support:
Two complementary rendering systems were added to handle Unicode text
across the entire client:

1. Iggy UI (Flash-based text fields): A new UIUnicodeBitmapFont class
   serves Java Minecraft's glyph page PNGs (glyph_00.png-glyph_FF.png)
   through Iggy's bitmap font provider API. Registered as the global
   fallback font with metrics matching the Mojangles bitmap font for
   correct baseline alignment. When the primary bitmap font lacks a
   glyph, it returns IGGY_GLYPH_INVALID and Iggy seamlessly falls back
   to the unicode bitmap font.

2. Legacy C++ Font renderer (chat editor, in-world signs): Revived the
   commented-out unicode glyph page system in Font.cpp. Characters not
   in the bitmap font texture are rendered from glyph page PNGs loaded
   on demand, with proper texture switching mid-string.

3. ChatScreen input: Removed the restrictive acceptableLetters filter
   so all printable Unicode characters are accepted in chat.

Languages now supported for text input and rendering:
- Japanese (Hiragana, Katakana, Kanji)
- Chinese (Simplified and Traditional)
- Korean (Hangul)
- Thai
- Arabic
- Hindi (Devanagari)
- Russian (Cyrillic) - already worked via bitmap font
- Greek - already worked via bitmap font
- Polish, Czech, Turkish (Extended Latin) - already worked via bitmap font
- Armenian, Georgian, and other scripts covered by glyph pages

Security fixes:
- Fixed memset under-initialization of Font::charWidths (zeroed 460
  bytes instead of 460*sizeof(int)=1840 bytes, leaving entries 115+
  uninitialized) - pre-existing bug
- Added bounds checks to all UIUnicodeBitmapFont callbacks to reject
  glyph IDs outside [0, 65535], preventing OOB array access
- Added bounds check in Font::width() section-sign fallback path to
  prevent OOB read on charWidths[] with high codepoints
- Blocked Unicode bidirectional override characters (U+202A-202E,
  U+2066-2069) in chat input to prevent message spoofing

Memory leak fix:
- Fixed SignTileEntity::load allocating wchar_t[256] with new[] on
  every sign load without freeing. Replaced with stack allocation.

Debug logging:
- Added [SIGN] prefixed logging for sign save/update operations
- Added [CHAT] prefixed logging for chat send/receive operations

Files changed:
- UIUnicodeBitmapFont.h/.cpp (new) - Iggy bitmap font for glyph pages
- UIBitmapFont.cpp - Return IGGY_GLYPH_INVALID for unknown chars
- UIFontData.h/.cpp - Added hasGlyph() method
- UIController.h/.cpp - Load and register unicode bitmap fallback font
- UITTFFont.h/.cpp - Added registerAsDefaultFonts parameter
- Font.h/.cpp - Revived unicode glyph page rendering system
- ChatScreen.cpp - Accept all Unicode input, block bidi overrides
- Gui.cpp - Chat display debug logging
- ClientConnection.cpp - Sign update debug logging
- SignTileEntity.cpp - Sign save logging, memory leak fix
2026-03-16 23:08:05 -05:00

137 lines
3.3 KiB
C++

#pragma once
#include <unordered_map>
using namespace std;
#define _DEBUG_BLOCK_CHARS 0
// For hardcoded font data.
struct SFontData
{
public:
static const unsigned short FONTCOLS = 23;
static const unsigned short FONTROWS = 20;
static const unsigned short FONTSIZE = FONTCOLS * FONTROWS;
public:
// Font name.
string m_strFontName;
// Filename of the glyph archive.
wstring m_wstrFilename;
// Number of glyphs in the archive.
unsigned int m_uiGlyphCount;
// Unicode values of each glyph.
unsigned short *m_arrCodepoints;
// X resolution of glyph archive.
unsigned int m_uiGlyphMapX;
// Y resolution of glyph archive.
unsigned int m_uiGlyphMapY;
// Number of columns in the glyph archive.
unsigned int m_uiGlyphMapCols;
// Number of rows in the glyph archive.
unsigned int m_uiGlyphMapRows;
// Width of each glyph.
unsigned int m_uiGlyphWidth;
// Height of each glyph.
unsigned int m_uiGlyphHeight;
// Ascent of each glyph above the baseline (units?).
float m_fAscent;
// Descent of each glyph below the baseline (units?).
float m_fDescent;
// How much to advance for each pixel wide the glyph is.
float m_fAdvPerPixel;
// How many pixels wide any whitespace characters are.
unsigned int m_uiWhitespaceWidth;
public:
static unsigned short Codepoints[FONTSIZE];
static SFontData Mojangles_7;
static SFontData Mojangles_11;
};
// Provides a common interface for dealing with font data.
class CFontData
{
public:
CFontData();
// pbRawImage consumed by constructor.
CFontData(SFontData &sFontData, int *pbRawImage);
// Release memory.
void release();
protected:
// Hardcoded font data.
SFontData *m_sFontData;
// Map Unicodepoints to glyph ids.
unordered_map<unsigned int, unsigned short> m_unicodeMap;
// Kerning value for each glyph.
unsigned short *m_kerningTable;
// Binary blob of the archive image.
unsigned char *m_pbRawImage;
// Total advance of each character.
float *m_pfAdvanceTable;
public:
// Accessor for the font name in the internal SFontData.
const string getFontName();
// Accessor for the hardcoded internal font data.
SFontData *getFontData();
// Get the glyph id corresponding to a unicode point.
unsigned short getGlyphId(unsigned int unicodepoint);
// Get the unicodepoint corresponding to a glyph id.
unsigned int getUnicode(unsigned short glyphId);
// Get a pointer to the top left pixel of a row/column in the raw image.
unsigned char *topLeftPixel(int row, int col);
// Get the row and column where a glyph appears in the archive.
void getPos(unsigned short gyphId, int &row, int &col);
// Get the advance of this character (units?).
float getAdvance(unsigned short glyphId);
// Get the width (in pixels) of a given character.
int getWidth(unsigned short glyphId);
// Returns true if this glyph is whitespace.
bool glyphIsWhitespace(unsigned short glyphId);
// Returns true if this unicodepoint is whitespace
bool unicodeIsWhitespace(unsigned int unicodepoint);
// Returns true if this unicodepoint exists in the font's glyph map.
bool hasGlyph(unsigned int unicodepoint);
private:
// Move a pointer in an image dx pixels right and dy pixels down, wrap around in either dimension leads to unknown behaviour.
void moveCursor(unsigned char *&cursor, unsigned int dx, unsigned int dy);
};