/* * font.hpp * MontaukOS text rendering — TrueType with bitmap fallback * Copyright (c) 2025 Daniel Hammer */ #pragma once #include "gui/gui.hpp" #include "gui/framebuffer.hpp" #include "gui/truetype.hpp" namespace gui { static constexpr int FONT_WIDTH = 8; static constexpr int FONT_HEIGHT = 16; // Defined by programs that ship the built-in VGA fallback. Keep the symbol // weak so Canvas remains usable by small apps that intentionally rely only on // TrueType fonts, while login/desktop can recover when TrueType setup fails. extern const uint8_t font_data[256 * 16] __attribute__((weak)); // The bitmap fallback font is Code Page 437. Map a Unicode codepoint to its // CP437 cell ('?' when it has none) so UTF-8 text still draws sensibly, // including the box-drawing characters terminal programs use. inline uint8_t cp437_index(uint32_t cp) { if (cp < 0x80) return (uint8_t)cp; static constexpr uint16_t high[128] = { 0x00C7,0x00FC,0x00E9,0x00E2,0x00E4,0x00E0,0x00E5,0x00E7, 0x00EA,0x00EB,0x00E8,0x00EF,0x00EE,0x00EC,0x00C4,0x00C5, 0x00C9,0x00E6,0x00C6,0x00F4,0x00F6,0x00F2,0x00FB,0x00F9, 0x00FF,0x00D6,0x00DC,0x00A2,0x00A3,0x00A5,0x20A7,0x0192, 0x00E1,0x00ED,0x00F3,0x00FA,0x00F1,0x00D1,0x00AA,0x00BA, 0x00BF,0x2310,0x00AC,0x00BD,0x00BC,0x00A1,0x00AB,0x00BB, 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556, 0x2555,0x2563,0x2551,0x2557,0x255D,0x255C,0x255B,0x2510, 0x2514,0x2534,0x252C,0x251C,0x2500,0x253C,0x255E,0x255F, 0x255A,0x2554,0x2569,0x2566,0x2560,0x2550,0x256C,0x2567, 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256B, 0x256A,0x2518,0x250C,0x2588,0x2584,0x258C,0x2590,0x2580, 0x03B1,0x00DF,0x0393,0x03C0,0x03A3,0x03C3,0x00B5,0x03C4, 0x03A6,0x0398,0x03A9,0x03B4,0x221E,0x03C6,0x03B5,0x2229, 0x2261,0x00B1,0x2265,0x2264,0x2320,0x2321,0x00F7,0x2248, 0x00B0,0x2219,0x00B7,0x221A,0x207F,0x00B2,0x25A0,0x00A0, }; for (int i = 0; i < 128; i++) if (high[i] == cp) return (uint8_t)(0x80 + i); return '?'; } // Dynamic font height: TTF line height or 16 (bitmap fallback) inline int system_font_height() { if (fonts::system_font && fonts::system_font->valid) return fonts::system_font->get_line_height(fonts::UI_SIZE); return FONT_HEIGHT; } // Dynamic mono font cell dimensions inline int mono_cell_width() { if (fonts::mono && fonts::mono->valid) { // Monospace: all glyphs have the same advance GlyphCache* gc = fonts::mono->get_cache(fonts::TERM_SIZE); CachedGlyph* g = fonts::mono->get_glyph(gc, 'M'); if (g) return g->advance; } return FONT_WIDTH; } inline int mono_cell_height() { if (fonts::mono && fonts::mono->valid) return fonts::mono->get_line_height(fonts::TERM_SIZE); return FONT_HEIGHT; } inline void draw_char(Framebuffer& fb, int x, int y, char c, Color fg) { const uint8_t* glyph = &font_data[(unsigned char)c * FONT_HEIGHT]; for (int row = 0; row < FONT_HEIGHT; row++) { uint8_t bits = glyph[row]; for (int col = 0; col < FONT_WIDTH; col++) { if (bits & (0x80 >> col)) { fb.put_pixel(x + col, y + row, fg); } } } } inline void draw_char_bg(Framebuffer& fb, int x, int y, char c, Color fg, Color bg) { const uint8_t* glyph = &font_data[(unsigned char)c * FONT_HEIGHT]; for (int row = 0; row < FONT_HEIGHT; row++) { uint8_t bits = glyph[row]; for (int col = 0; col < FONT_WIDTH; col++) { if (bits & (0x80 >> col)) { fb.put_pixel(x + col, y + row, fg); } else { fb.put_pixel(x + col, y + row, bg); } } } } inline void draw_text(Framebuffer& fb, int x, int y, const char* text, Color fg) { if (fonts::system_font && fonts::system_font->valid) { fonts::system_font->draw(fb, x, y, text, fg, fonts::UI_SIZE); return; } for (const char* p = text; *p; x += FONT_WIDTH) { draw_char(fb, x, y, (char)cp437_index(montauk::utf8::next(&p)), fg); } } inline void draw_text_bg(Framebuffer& fb, int x, int y, const char* text, Color fg, Color bg) { if (fonts::system_font && fonts::system_font->valid) { fonts::system_font->draw_bg(fb, x, y, text, fg, bg, fonts::UI_SIZE); return; } for (const char* p = text; *p; x += FONT_WIDTH) { draw_char_bg(fb, x, y, (char)cp437_index(montauk::utf8::next(&p)), fg, bg); } } // Width of the first `len` bytes of a UTF-8 string in the UI font. inline int text_width_n(const char* text, int len) { if (fonts::system_font && fonts::system_font->valid) return fonts::system_font->measure_text_n(text, len, fonts::UI_SIZE); return montauk::utf8::count(text, len) * FONT_WIDTH; } inline int text_width(const char* text) { if (fonts::system_font && fonts::system_font->valid) { return fonts::system_font->measure_text(text, fonts::UI_SIZE); } return montauk::utf8::count(text) * FONT_WIDTH; } } // namespace gui