From 66547fc090ada07da18c088072e6cd8f6891f0ab Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Fri, 31 Jul 2026 13:25:00 +1000 Subject: [PATCH] [client] font: skip stb-incompatible UI font faces --- client/CMakeLists.txt | 1 + client/include/font.h | 30 +++++++++++ client/src/font.c | 119 ++++++++++++++++++++++++++++++++++++++++++ client/src/util.c | 30 +++++++++-- 4 files changed, 177 insertions(+), 3 deletions(-) create mode 100644 client/include/font.h create mode 100644 client/src/font.c diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 8130c510..f05f0d52 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -166,6 +166,7 @@ set(SOURCES src/message.c src/config.c src/keybind.c + src/font.c src/util.c src/clipboard.c src/kb.c diff --git a/client/include/font.h b/client/include/font.h new file mode 100644 index 00000000..bbf88ef9 --- /dev/null +++ b/client/include/font.h @@ -0,0 +1,30 @@ +/** + * Looking Glass + * Copyright © 2017-2026 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _H_LG_FONT_ +#define _H_LG_FONT_ + +#include +#include + +bool font_isStbTruetypeCompatible( + const void * data, size_t size, int fontIndex); + +#endif diff --git a/client/src/font.c b/client/src/font.c new file mode 100644 index 00000000..61cfa009 --- /dev/null +++ b/client/src/font.c @@ -0,0 +1,119 @@ +/** + * Looking Glass + * Copyright © 2017-2026 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "font.h" + +#include +#include + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +#define STBTT_STATIC +#define STB_TRUETYPE_IMPLEMENTATION +#include "repos/cimgui/imgui/imstb_truetype.h" +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +static uint16_t font_readU16(const unsigned char * data) +{ + return (uint16_t)(((uint16_t)data[0] << 8) | data[1]); +} + +static uint32_t font_readU32(const unsigned char * data) +{ + return + ((uint32_t)data[0] << 24) | + ((uint32_t)data[1] << 16) | + ((uint32_t)data[2] << 8) | + (uint32_t)data[3]; +} + +static bool font_getSfntOffset(const unsigned char * data, size_t size, + int fontIndex, size_t * offset) +{ + if (!data || size < 12 || size > INT_MAX || fontIndex < 0) + return false; + + size_t fontOffset = 0; + const uint32_t signature = font_readU32(data); + switch (signature) + { + case 0x00010000: // TrueType 1.0 + case 0x74727565: // true + case 0x74797031: // typ1 + case 0x4F54544F: // OTTO + if (fontIndex != 0) + return false; + break; + + case 0x74746366: // ttcf + { + const uint32_t version = font_readU32(data + 4); + const uint32_t fontCount = font_readU32(data + 8); + if ((version != 0x00010000 && version != 0x00020000) || + (uint32_t)fontIndex >= fontCount || + fontCount > (size - 12) / 4) + return false; + + fontOffset = font_readU32(data + 12 + (size_t)fontIndex * 4); + if (fontOffset > size - 12 || fontOffset > INT_MAX) + return false; + break; + } + + default: + return false; + } + + const uint16_t tableCount = font_readU16(data + fontOffset + 4); + if (tableCount > (size - fontOffset - 12) / 16) + return false; + + const unsigned char * table = data + fontOffset + 12; + for (uint16_t i = 0; i < tableCount; ++i, table += 16) + { + const size_t tableOffset = font_readU32(table + 8); + const size_t tableSize = font_readU32(table + 12); + if (tableOffset > size || tableSize > size - tableOffset) + return false; + } + + *offset = fontOffset; + return true; +} + +bool font_isStbTruetypeCompatible( + const void * data, size_t size, int fontIndex) +{ + size_t checkedOffset; + if (!font_getSfntOffset(data, size, fontIndex, &checkedOffset)) + return false; + + const int stbOffset = + stbtt_GetFontOffsetForIndex((const unsigned char *)data, fontIndex); + if (stbOffset < 0 || (size_t)stbOffset != checkedOffset) + return false; + + stbtt_fontinfo info; + return stbtt_InitFont(&info, data, stbOffset); +} diff --git a/client/src/util.c b/client/src/util.c index ac722fcd..4a9b4af7 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -20,6 +20,7 @@ #include "util.h" #include "main.h" +#include "font.h" #include "common/debug.h" #include "common/stringutils.h" @@ -105,6 +106,7 @@ static bool uiFontAddFace(const FcPattern * pattern, FcCharSet ** remainingPtr) FcChar8 * file; FcCharSet * charset; FcBool outline = FcTrue; + int index = 0; if (FcPatternGetString(pattern, FC_FILE, 0, &file) != FcResultMatch || FcPatternGetCharSet(pattern, FC_CHARSET, 0, &charset) != FcResultMatch) @@ -114,6 +116,9 @@ static bool uiFontAddFace(const FcPattern * pattern, FcCharSet ** remainingPtr) !outline) return true; + if (FcPatternGetInteger(pattern, FC_INDEX, 0, &index) == FcResultMatch) + index &= 0xFFFF; + FcCharSet * covered = FcCharSetIntersect(remaining, charset); if (!covered) return false; @@ -124,6 +129,27 @@ static bool uiFontAddFace(const FcPattern * pattern, FcCharSet ** remainingPtr) return true; } + size_t fontDataSize; + void * fontData = igImFileLoadToMemory( + (const char *)file, "rb", &fontDataSize, 0); + if (!fontData) + { + DEBUG_WARN("Failed to read UI font face: %s", file); + FcCharSetDestroy(covered); + return true; + } + + const bool compatible = font_isStbTruetypeCompatible( + fontData, fontDataSize, index); + igMemFree(fontData); + if (!compatible) + { + DEBUG_WARN("Skipping stb-incompatible UI font face: %s (index %d)", + file, index); + FcCharSetDestroy(covered); + return true; + } + struct UIFontFace * faces = realloc(UIFontFaces, sizeof(*UIFontFaces) * (UIFontFaceCount + 1)); if (!faces) @@ -144,9 +170,7 @@ static bool uiFontAddFace(const FcPattern * pattern, FcCharSet ** remainingPtr) return false; } - int index = 0; - if (FcPatternGetInteger(pattern, FC_INDEX, 0, &index) == FcResultMatch) - face->index = index & 0xFFFF; + face->index = index; ++UIFontFaceCount;