//SPDX-License-Identifier: LGPL-3.0-only //SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® module; #define STB_TRUETYPE_IMPLEMENTATION #include "../lib/stb_truetype.h" module Crafter.Graphics:Font_impl; import :Font; import std; using namespace Crafter; Font::Font(const std::filesystem::path& fontFilePath) { // 1. Load the font file into memory std::ifstream fontFile(fontFilePath, std::ios::binary | std::ios::ate); if (!fontFile.is_open()) { std::cerr << "Failed to open font file\n"; } std::streamsize size = fontFile.tellg(); fontFile.seekg(0, std::ios::beg); fontBuffer.resize(size); if (!fontFile.read((char*)fontBuffer.data(), size)) { std::cerr << "Failed to read font file\n"; } // 2. Initialize the font if (!stbtt_InitFont(&font, fontBuffer.data(), stbtt_GetFontOffsetForIndex(fontBuffer.data(), 0))) { std::cout << "Test6" << std::endl; std::cerr << "Failed to initialize font.\n"; } int ascent; int descent; int lineGap; stbtt_GetFontVMetrics(&font, &ascent, &descent, &lineGap); this->ascent = ascent; this->descent = descent; this->lineGap = lineGap; asciiAdvance_.fill(-1); } std::int32_t Font::AdvanceUnits(std::uint32_t cp) { if (cp < asciiAdvance_.size()) { if (asciiAdvance_[cp] < 0) { int advance = 0, lsb = 0; stbtt_GetCodepointHMetrics(&font, static_cast(cp), &advance, &lsb); asciiAdvance_[cp] = advance; } return asciiAdvance_[cp]; } if (auto it = advanceUnits_.find(cp); it != advanceUnits_.end()) return it->second; int advance = 0, lsb = 0; stbtt_GetCodepointHMetrics(&font, static_cast(cp), &advance, &lsb); return advanceUnits_.emplace(cp, advance).first->second; } std::uint32_t Font::GetLineWidth(const std::string_view text, float size) { float scale = stbtt_ScaleForPixelHeight(&font, size); std::uint32_t lineWidth = 0; std::size_t i = 0; while (i < text.size()) { std::uint32_t cp = DecodeUtf8(text, i); if (cp == 0) break; lineWidth += (int)(AdvanceUnits(cp) * scale); } return lineWidth; } std::size_t Font::NearestCursorByte(std::string_view text, float size, float target) { if (target <= 0.0f) return 0; float scale = stbtt_ScaleForPixelHeight(&font, size); std::size_t best = 0; // byte offset 0 → caret before the first glyph float bestDist = target; // |target - 0|, and target > 0 here float cumWidth = 0.0f; std::size_t i = 0; while (i < text.size()) { std::uint32_t cp = DecodeUtf8(text, i); // i advances to the next boundary if (cp == 0) break; cumWidth += (int)(AdvanceUnits(cp) * scale); // match GetLineWidth's per-glyph rounding float d = std::abs(target - cumWidth); if (d < bestDist) { bestDist = d; best = i; // byte offset just past this codepoint } else { break; // monotonic advance ⇒ already past the closest boundary } } return best; } float Font::LineHeight(float size) { float scale = stbtt_ScaleForPixelHeight(&font, size); return (ascent - descent + lineGap) * scale; } float Font::AscentPx(float size) { return ascent * stbtt_ScaleForPixelHeight(&font, size); } float Font::ScaleForSize(float size) { return stbtt_ScaleForPixelHeight(&font, size); }