mirror of https://github.com/OpenTTD/OpenTTD
Change: Support side-by-side fallback FontCaches instead of hierarchical.
The text layouter system can now support using different fonts for different glyphs, including mixing scalable and sprite glyphs.pull/13303/head
parent
ee2f0f46ee
commit
8c9a3fc3ee
|
@ -2360,17 +2360,18 @@ static bool ConFont(std::span<std::string_view> argv)
|
||||||
SetFont(argfs, font, size);
|
SetFont(argfs, font, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
IConsolePrint(CC_INFO, "Configured fonts:");
|
||||||
FontCache *fc = FontCache::Get(fs);
|
for (uint i = 0; FontSize fs : FONTSIZES_ALL) {
|
||||||
FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
|
const FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
|
||||||
/* Make sure all non sprite fonts are loaded. */
|
IConsolePrint(CC_DEFAULT, "{}) {} font: \"{}\", size {}", i, FontSizeToName(fs), setting->font, setting->size);
|
||||||
if (!setting->font.empty() && !fc->HasParent()) {
|
++i;
|
||||||
InitFontCache(fs);
|
|
||||||
fc = FontCache::Get(fs);
|
|
||||||
}
|
}
|
||||||
IConsolePrint(CC_DEFAULT, "{} font:", FontSizeToName(fs));
|
|
||||||
IConsolePrint(CC_DEFAULT, "Currently active: \"{}\", size {}", fc->GetFontName(), fc->GetFontSize());
|
IConsolePrint(CC_INFO, "Currently active fonts:");
|
||||||
IConsolePrint(CC_DEFAULT, "Requested: \"{}\", size {}", setting->font, setting->size);
|
for (uint i = 0; const auto &fc : FontCache::Get()) {
|
||||||
|
if (fc == nullptr) continue;
|
||||||
|
IConsolePrint(CC_DEFAULT, "{}) {} font: \"{}\" size {}", i, FontSizeToName(fc->GetSize()), fc->GetFontName(), fc->GetFontSize());
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
/** @file fontcache.cpp Cache for characters from fonts. */
|
/** @file fontcache.cpp Cache for characters from fonts. */
|
||||||
|
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
#include "core/string_consumer.hpp"
|
||||||
#include "fontcache.h"
|
#include "fontcache.h"
|
||||||
#include "blitter/factory.hpp"
|
#include "blitter/factory.hpp"
|
||||||
#include "gfx_layout.h"
|
#include "gfx_layout.h"
|
||||||
|
@ -17,13 +19,10 @@
|
||||||
#include "viewport_func.h"
|
#include "viewport_func.h"
|
||||||
#include "window_func.h"
|
#include "window_func.h"
|
||||||
#include "fileio_func.h"
|
#include "fileio_func.h"
|
||||||
|
#include "zoom_func.h"
|
||||||
|
|
||||||
#include "safeguards.h"
|
#include "safeguards.h"
|
||||||
|
|
||||||
/** Default heights for the different sizes of fonts. */
|
|
||||||
static const int _default_font_height[FS_END] = {10, 6, 18, 10};
|
|
||||||
static const int _default_font_ascender[FS_END] = { 8, 5, 15, 8};
|
|
||||||
|
|
||||||
FontCacheSettings _fcsettings;
|
FontCacheSettings _fcsettings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,10 +30,10 @@ FontCacheSettings _fcsettings;
|
||||||
* @param fs Font size to load.
|
* @param fs Font size to load.
|
||||||
* @param fonttype Font type requested.
|
* @param fonttype Font type requested.
|
||||||
*/
|
*/
|
||||||
/* static */ void FontProviderManager::LoadFont(FontSize fs, FontType fonttype)
|
/* static */ void FontProviderManager::LoadFont(FontSize fs, FontType fonttype, bool search, const std::string &font_name, std::span<const std::byte> os_handle)
|
||||||
{
|
{
|
||||||
for (auto &provider : FontProviderManager::GetProviders()) {
|
for (auto &provider : FontProviderManager::GetProviders()) {
|
||||||
provider->LoadFont(fs, fonttype);
|
provider->LoadFont(fs, fonttype, search, font_name, os_handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,10 +46,10 @@ FontCacheSettings _fcsettings;
|
||||||
* @param callback The function to call to check for missing glyphs.
|
* @param callback The function to call to check for missing glyphs.
|
||||||
* @return true if a font has been set, false otherwise.
|
* @return true if a font has been set, false otherwise.
|
||||||
*/
|
*/
|
||||||
/* static */ bool FontProviderManager::SetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback)
|
/* static */ bool FontProviderManager::SetFallbackFont(const std::string &language_isocode, FontSizes bad_mask, class MissingGlyphSearcher *callback)
|
||||||
{
|
{
|
||||||
for (auto &provider : FontProviderManager::GetProviders()) {
|
for (auto &provider : FontProviderManager::GetProviders()) {
|
||||||
if (provider->SetFallbackFont(settings, language_isocode, callback)) {
|
if (provider->SetFallbackFont(language_isocode, bad_mask, callback)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,42 +60,49 @@ FontCacheSettings _fcsettings;
|
||||||
* Create a new font cache.
|
* Create a new font cache.
|
||||||
* @param fs The size of the font.
|
* @param fs The size of the font.
|
||||||
*/
|
*/
|
||||||
FontCache::FontCache(FontSize fs) : parent(FontCache::Get(fs)), fs(fs), height(_default_font_height[fs]),
|
FontCache::FontCache(FontSize fs) : fs(fs)
|
||||||
ascender(_default_font_ascender[fs]), descender(_default_font_ascender[fs] - _default_font_height[fs])
|
|
||||||
{
|
{
|
||||||
assert(this->parent == nullptr || this->fs == this->parent->fs);
|
/* Find an empty font cache slot. */
|
||||||
FontCache::caches[this->fs] = this;
|
auto it = std::find(std::begin(FontCache::caches), std::end(FontCache::caches), nullptr);
|
||||||
|
if (it == std::end(FontCache::caches)) it = FontCache::caches.insert(it, nullptr);
|
||||||
|
|
||||||
|
/* Register this font cache in the slot. */
|
||||||
|
it->reset(this);
|
||||||
|
|
||||||
|
/* Set up our font index and make us the default font cache for this font size. */
|
||||||
|
this->font_index = static_cast<FontIndex>(std::distance(std::begin(FontCache::caches), it));
|
||||||
|
FontCache::default_font_index[fs] = this->font_index;
|
||||||
|
|
||||||
Layouter::ResetFontCache(this->fs);
|
Layouter::ResetFontCache(this->fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clean everything up. */
|
/** Clean everything up. */
|
||||||
FontCache::~FontCache()
|
FontCache::~FontCache()
|
||||||
{
|
{
|
||||||
assert(this->fs == this->parent->fs);
|
|
||||||
FontCache::caches[this->fs] = this->parent;
|
|
||||||
Layouter::ResetFontCache(this->fs);
|
Layouter::ResetFontCache(this->fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FontCache::GetDefaultFontHeight(FontSize fs)
|
int FontCache::GetDefaultFontHeight(FontSize fs)
|
||||||
{
|
{
|
||||||
return _default_font_height[fs];
|
return DEFAULT_FONT_HEIGHT[fs];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* static */ void FontCache::UpdateCharacterHeight(FontSize fs)
|
||||||
* Get the font name of a given font size.
|
|
||||||
* @param fs The font size to look up.
|
|
||||||
* @return The font name.
|
|
||||||
*/
|
|
||||||
std::string FontCache::GetName(FontSize fs)
|
|
||||||
{
|
{
|
||||||
FontCache *fc = FontCache::Get(fs);
|
FontCache::max_height[fs] = 0;
|
||||||
if (fc != nullptr) {
|
for (const auto &fc : FontCache::caches) {
|
||||||
return fc->GetFontName();
|
if (fc == nullptr || fc->fs != fs) continue;
|
||||||
} else {
|
FontCache::max_height[fs] = std::max(FontCache::max_height[fs], fc->height);
|
||||||
return "[NULL]";
|
|
||||||
}
|
}
|
||||||
|
if (FontCache::max_height[fs] == 0) FontCache::max_height[fs] = GetDefaultFontHeight(fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int FontCache::GetGlyphYOffset()
|
||||||
|
{
|
||||||
|
int fs_height = FontCache::GetCharacterHeight(this->GetSize());
|
||||||
|
int height = this->GetAscender() - this->GetDescender();
|
||||||
|
return (fs_height - height) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get height of a character for a given font size.
|
* Get height of a character for a given font size.
|
||||||
|
@ -105,16 +111,19 @@ std::string FontCache::GetName(FontSize fs)
|
||||||
*/
|
*/
|
||||||
int GetCharacterHeight(FontSize size)
|
int GetCharacterHeight(FontSize size)
|
||||||
{
|
{
|
||||||
return FontCache::Get(size)->GetHeight();
|
uint height = FontCache::GetCharacterHeight(size);
|
||||||
|
if (height == 0) height = ScaleGUITrad(FontCache::GetDefaultFontHeight(FS_MONO));
|
||||||
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */ FontCache::FontCaches FontCache::caches;
|
||||||
/* static */ FontCache *FontCache::caches[FS_END];
|
/* static */ std::array<int, FS_END> FontCache::max_height{};
|
||||||
|
/* static */ std::array<FontIndex, FS_END> FontCache::default_font_index{};
|
||||||
|
|
||||||
/* static */ void FontCache::InitializeFontCaches()
|
/* static */ void FontCache::InitializeFontCaches()
|
||||||
{
|
{
|
||||||
for (FontSize fs = FS_BEGIN; fs != FS_END; fs++) {
|
for (FontSize fs : FONTSIZES_ALL) {
|
||||||
if (FontCache::caches[fs] == nullptr) FontProviderManager::LoadFont(fs, FontType::Sprite); /* FontCache inserts itself into to the cache. */
|
FontCache::max_height[fs] = ScaleSpriteTrad(GetDefaultFontHeight(fs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,15 +154,8 @@ void SetFont(FontSize fontsize, const std::string &font, uint size)
|
||||||
if (!changed) return;
|
if (!changed) return;
|
||||||
|
|
||||||
if (fontsize != FS_MONO) {
|
if (fontsize != FS_MONO) {
|
||||||
/* Try to reload only the modified font. */
|
/* Check if fallback fonts are needed. */
|
||||||
FontCacheSettings backup = _fcsettings;
|
|
||||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
|
||||||
if (fs == fontsize) continue;
|
|
||||||
FontCache *fc = FontCache::Get(fs);
|
|
||||||
GetFontCacheSubSetting(fs)->font = fc->HasParent() ? fc->GetFontName() : "";
|
|
||||||
}
|
|
||||||
CheckForMissingGlyphs();
|
CheckForMissingGlyphs();
|
||||||
_fcsettings = std::move(backup);
|
|
||||||
} else {
|
} else {
|
||||||
InitFontCache(fontsize);
|
InitFontCache(fontsize);
|
||||||
}
|
}
|
||||||
|
@ -171,7 +173,7 @@ void SetFont(FontSize fontsize, const std::string &font, uint size)
|
||||||
*/
|
*/
|
||||||
static bool IsDefaultFont(const FontCacheSubSetting &setting)
|
static bool IsDefaultFont(const FontCacheSubSetting &setting)
|
||||||
{
|
{
|
||||||
return setting.font.empty() && setting.os_handle == nullptr;
|
return setting.font.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -208,7 +210,7 @@ static std::string GetDefaultTruetypeFont(FontSize fs)
|
||||||
* @param fs Font size.
|
* @param fs Font size.
|
||||||
* @return Full path of default font file.
|
* @return Full path of default font file.
|
||||||
*/
|
*/
|
||||||
static std::string GetDefaultTruetypeFontFile([[maybe_unused]] FontSize fs)
|
std::string GetDefaultTruetypeFontFile([[maybe_unused]] FontSize fs)
|
||||||
{
|
{
|
||||||
#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
|
#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
|
||||||
/* Find font file. */
|
/* Find font file. */
|
||||||
|
@ -237,13 +239,52 @@ std::string GetFontCacheFontName(FontSize fs)
|
||||||
*/
|
*/
|
||||||
void InitFontCache(FontSizes fontsizes)
|
void InitFontCache(FontSizes fontsizes)
|
||||||
{
|
{
|
||||||
FontCache::InitializeFontCaches();
|
static constexpr std::string_view DEFAULT_FONT = "default";
|
||||||
|
|
||||||
for (FontSize fs : fontsizes) {
|
for (FontSize fs : fontsizes) {
|
||||||
FontCache *fc = FontCache::Get(fs);
|
Layouter::ResetFontCache(fs);
|
||||||
if (fc->HasParent()) delete fc;
|
FontCache::default_font_index[fs] = INVALID_FONT_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
FontProviderManager::LoadFont(fs, FontType::TrueType);
|
/* Remove all existing FontCaches. */
|
||||||
|
for (auto it = std::begin(FontCache::caches); it != std::end(FontCache::caches); ++it) {
|
||||||
|
if (*it == nullptr) continue;
|
||||||
|
if (!fontsizes.Test((*it)->fs)) continue;
|
||||||
|
it->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (FontSize fs : fontsizes) {
|
||||||
|
const FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
|
||||||
|
|
||||||
|
/* Add all detected fallback fonts. */
|
||||||
|
for (auto &fallback : setting->fallback_fonts) {
|
||||||
|
FontProviderManager::LoadFont(fs, FontType::TrueType, /*fallback.dynamic ? "missing-fallback" : "language-fallback", */ false, fallback.name, fallback.os_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse configured fonts, separated by ';' into a list. */
|
||||||
|
std::vector<std::string_view> fontnames;
|
||||||
|
StringConsumer consumer(setting->font);
|
||||||
|
do {
|
||||||
|
auto fontname = StrTrimView(consumer.ReadUntilChar(';', StringConsumer::SKIP_ONE_SEPARATOR), " \t");
|
||||||
|
if (!fontname.empty()) fontnames.push_back(fontname);
|
||||||
|
} while (consumer.AnyBytesLeft());
|
||||||
|
|
||||||
|
/* Add the default font as lowest priority if not manually specified. */
|
||||||
|
if (std::ranges::find(fontnames, DEFAULT_FONT) == std::end(fontnames)) fontnames.push_back(DEFAULT_FONT);
|
||||||
|
|
||||||
|
/* Load configured fonts in reverse order so that the first entry has priority. */
|
||||||
|
for (auto it = fontnames.rbegin(); it != fontnames.rend(); ++it) {
|
||||||
|
if (*it == DEFAULT_FONT) {
|
||||||
|
/* Load the sprite font, even if it's not preferred. */
|
||||||
|
FontProviderManager::LoadFont(fs, FontType::Sprite, /*"default"*/ false, {}, {});
|
||||||
|
if (!_fcsettings.prefer_sprite) {
|
||||||
|
/* Load the default truetype font if sprite not isn't preferred. */
|
||||||
|
FontProviderManager::LoadFont(fs, FontType::TrueType, /*"default",*/ false, GetDefaultTruetypeFontFile(fs), {});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FontProviderManager::LoadFont(fs, FontType::TrueType, /*"configured",*/ true, std::string{*it}, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,8 +293,5 @@ void InitFontCache(FontSizes fontsizes)
|
||||||
*/
|
*/
|
||||||
void UninitFontCache()
|
void UninitFontCache()
|
||||||
{
|
{
|
||||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
FontCache::caches.clear();
|
||||||
FontCache *fc = FontCache::Get(fs);
|
|
||||||
if (fc->HasParent()) delete fc;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
130
src/fontcache.h
130
src/fontcache.h
|
@ -16,17 +16,31 @@
|
||||||
|
|
||||||
/** Glyphs are characters from a font. */
|
/** Glyphs are characters from a font. */
|
||||||
typedef uint32_t GlyphID;
|
typedef uint32_t GlyphID;
|
||||||
static const GlyphID SPRITE_GLYPH = 1U << 30;
|
using FontIndex = uint8_t;
|
||||||
|
|
||||||
|
static const FontIndex INVALID_FONT_INDEX = std::numeric_limits<FontIndex>::max();
|
||||||
|
|
||||||
|
/** Default heights for the different sizes of fonts. */
|
||||||
|
static constexpr int DEFAULT_FONT_HEIGHT[FS_END] = {10, 6, 18, 10};
|
||||||
|
static constexpr int DEFAULT_FONT_ASCENDER[FS_END] = { 8, 5, 15, 8};
|
||||||
|
|
||||||
/** Font cache for basic fonts. */
|
/** Font cache for basic fonts. */
|
||||||
class FontCache {
|
class FontCache {
|
||||||
protected:
|
protected:
|
||||||
static FontCache *caches[FS_END]; ///< All the font caches.
|
using FontCaches = std::vector<std::unique_ptr<FontCache>>;
|
||||||
FontCache *parent; ///< The parent of this font cache.
|
static FontCaches caches;
|
||||||
|
|
||||||
|
static std::array<int, FS_END> max_height;
|
||||||
|
static std::array<FontIndex, FS_END> default_font_index;
|
||||||
|
|
||||||
const FontSize fs; ///< The size of the font.
|
const FontSize fs; ///< The size of the font.
|
||||||
int height; ///< The height of the font.
|
FontIndex font_index; ///< The index of the font.
|
||||||
int ascender; ///< The ascender value of the font.
|
int height = 0; ///< The height of the font.
|
||||||
int descender; ///< The descender value of the font.
|
int ascender = 0; ///< The ascender value of the font.
|
||||||
|
int descender = 0; ///< The descender value of the font.
|
||||||
|
|
||||||
|
friend void UninitFontCache();
|
||||||
|
friend void InitFontCache(FontSizes fontsizes);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FontCache(FontSize fs);
|
FontCache(FontSize fs);
|
||||||
|
@ -42,6 +56,8 @@ public:
|
||||||
*/
|
*/
|
||||||
inline FontSize GetSize() const { return this->fs; }
|
inline FontSize GetSize() const { return this->fs; }
|
||||||
|
|
||||||
|
inline FontIndex GetIndex() const { return this->font_index; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the height of the font.
|
* Get the height of the font.
|
||||||
* @return The height of the font.
|
* @return The height of the font.
|
||||||
|
@ -92,10 +108,9 @@ public:
|
||||||
/**
|
/**
|
||||||
* Map a character into a glyph.
|
* Map a character into a glyph.
|
||||||
* @param key The character.
|
* @param key The character.
|
||||||
* @param fallback Allow fallback to the parent font.
|
|
||||||
* @return The glyph ID used to draw the character.
|
* @return The glyph ID used to draw the character.
|
||||||
*/
|
*/
|
||||||
virtual GlyphID MapCharToGlyph(char32_t key, bool fallback = true) = 0;
|
virtual GlyphID MapCharToGlyph(char32_t key) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the native OS font handle, if there is one.
|
* Get the native OS font handle, if there is one.
|
||||||
|
@ -112,25 +127,57 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual std::string GetFontName() = 0;
|
virtual std::string GetFontName() = 0;
|
||||||
|
|
||||||
|
virtual int GetGlyphYOffset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get span of all FontCaches.
|
||||||
|
* @return Span of all FontCaches.
|
||||||
|
*/
|
||||||
|
static inline std::span<const std::unique_ptr<FontCache>> Get()
|
||||||
|
{
|
||||||
|
return FontCache::caches;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the font cache of a given font size.
|
* Get the font cache of a given font size.
|
||||||
* @param fs The font size to look up.
|
* @param fs The font size to look up.
|
||||||
* @return The font cache.
|
* @return The font cache.
|
||||||
*/
|
*/
|
||||||
static inline FontCache *Get(FontSize fs)
|
static inline FontCache *Get(FontIndex font_index)
|
||||||
{
|
{
|
||||||
assert(fs < FS_END);
|
assert(font_index < FontCache::caches.size());
|
||||||
return FontCache::caches[fs];
|
return FontCache::caches[font_index].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string GetName(FontSize fs);
|
static inline int GetCharacterHeight(FontSize fs)
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the font cache has a parent.
|
|
||||||
*/
|
|
||||||
inline bool HasParent()
|
|
||||||
{
|
{
|
||||||
return this->parent != nullptr;
|
return FontCache::max_height[fs];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UpdateCharacterHeight(FontSize fs);
|
||||||
|
|
||||||
|
static inline FontIndex GetDefaultFontIndex(FontSize fs)
|
||||||
|
{
|
||||||
|
return FontCache::default_font_index[fs];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline class FontCache *GetDefaultFontCache(FontSize fs)
|
||||||
|
{
|
||||||
|
FontIndex index = FontCache::GetDefaultFontIndex(fs);
|
||||||
|
if (index != INVALID_FONT_INDEX) return FontCache::Get(index);
|
||||||
|
NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline FontIndex GetFontIndexForCharacter(FontSize fs, char32_t c)
|
||||||
|
{
|
||||||
|
for (auto it = std::rbegin(FontCache::caches); it != std::rend(FontCache::caches); ++it) {
|
||||||
|
FontCache *fc = it->get();
|
||||||
|
if (fc == nullptr) continue;
|
||||||
|
if (fc->GetSize() != fs) continue;
|
||||||
|
if (fc->MapCharToGlyph(c) == 0) continue;
|
||||||
|
return std::distance(std::begin(FontCache::caches), std::next(it).base());
|
||||||
|
}
|
||||||
|
return INVALID_FONT_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -141,36 +188,55 @@ public:
|
||||||
|
|
||||||
inline void ClearFontCache(FontSizes fontsizes)
|
inline void ClearFontCache(FontSizes fontsizes)
|
||||||
{
|
{
|
||||||
for (FontSize fs : fontsizes) {
|
for (const auto &fc : FontCache::Get()) {
|
||||||
FontCache::Get(fs)->ClearFontCache();
|
if (fc == nullptr) continue;
|
||||||
|
if (!fontsizes.Test(fc->GetSize())) continue;
|
||||||
|
fc->ClearFontCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the Sprite for a glyph */
|
/** Get the Sprite for a glyph */
|
||||||
inline const Sprite *GetGlyph(FontSize size, char32_t key)
|
inline const Sprite *GetGlyph(FontSize size, char32_t key)
|
||||||
{
|
{
|
||||||
FontCache *fc = FontCache::Get(size);
|
FontIndex font_index = FontCache::GetFontIndexForCharacter(size, key);
|
||||||
|
FontCache *fc = font_index != INVALID_FONT_INDEX ? FontCache::Get(font_index) : FontCache::GetDefaultFontCache(size);
|
||||||
|
if (fc == nullptr) return nullptr;
|
||||||
return fc->GetGlyph(fc->MapCharToGlyph(key));
|
return fc->GetGlyph(fc->MapCharToGlyph(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the width of a glyph */
|
/** Get the width of a glyph */
|
||||||
inline uint GetGlyphWidth(FontSize size, char32_t key)
|
inline uint GetGlyphWidth(FontSize size, char32_t key)
|
||||||
{
|
{
|
||||||
FontCache *fc = FontCache::Get(size);
|
FontIndex font_index = FontCache::GetFontIndexForCharacter(size, key);
|
||||||
|
FontCache *fc = font_index != INVALID_FONT_INDEX ? FontCache::Get(font_index) : FontCache::GetDefaultFontCache(size);
|
||||||
|
if (fc == nullptr) return 0;
|
||||||
return fc->GetGlyphWidth(fc->MapCharToGlyph(key));
|
return fc->GetGlyphWidth(fc->MapCharToGlyph(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool GetDrawGlyphShadow(FontSize size)
|
|
||||||
{
|
|
||||||
return FontCache::Get(size)->GetDrawGlyphShadow();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Settings for a single font. */
|
/** Settings for a single font. */
|
||||||
struct FontCacheSubSetting {
|
struct FontCacheSubSetting {
|
||||||
std::string font; ///< The name of the font, or path to the font.
|
std::string font; ///< The name of the font, or path to the font.
|
||||||
uint size; ///< The (requested) size of the font.
|
uint size; ///< The (requested) size of the font.
|
||||||
|
|
||||||
const void *os_handle = nullptr; ///< Optional native OS font info. Only valid during font search.
|
struct FontCacheFallback {
|
||||||
|
std::string name;
|
||||||
|
std::vector<std::byte> os_handle;
|
||||||
|
bool dynamic;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<FontCacheFallback> fallback_fonts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a fallback font this font size configuration.
|
||||||
|
* @param name Name of font to add.
|
||||||
|
* @param handle OS-specific handle or data of font.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void AddFallback(const std::string &name, T &handle)
|
||||||
|
{
|
||||||
|
auto os_data = std::as_bytes(std::span(&handle, 1));
|
||||||
|
this->fallback_fonts.emplace_back(name, std::vector<std::byte>{os_data.begin(), os_data.end()});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Settings for the four different fonts. */
|
/** Settings for the four different fonts. */
|
||||||
|
@ -228,14 +294,14 @@ public:
|
||||||
ProviderManager<FontCacheFactory>::Unregister(*this);
|
ProviderManager<FontCacheFactory>::Unregister(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void LoadFont(FontSize fs, FontType fonttype) = 0;
|
virtual void LoadFont(FontSize fs, FontType fonttype, bool search, const std::string &font_name, std::span<const std::byte> os_handle) = 0;
|
||||||
virtual bool SetFallbackFont(struct FontCacheSettings *settings, const std::string &language_isocode, class MissingGlyphSearcher *callback) = 0;
|
virtual bool SetFallbackFont(const std::string &language_isocode, FontSizes bad_mask, class MissingGlyphSearcher *callback) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FontProviderManager : ProviderManager<FontCacheFactory> {
|
class FontProviderManager : ProviderManager<FontCacheFactory> {
|
||||||
public:
|
public:
|
||||||
static void LoadFont(FontSize fs, FontType fonttype);
|
static void LoadFont(FontSize fs, FontType fonttype, bool search, const std::string &font_name, std::span<const std::byte> os_handle);
|
||||||
static bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback);
|
static bool SetFallbackFont(const std::string &language_isocode, FontSizes bad_mask, class MissingGlyphSearcher *callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Implemented in spritefontcache.cpp */
|
/* Implemented in spritefontcache.cpp */
|
||||||
|
|
|
@ -40,7 +40,7 @@ public:
|
||||||
FreeTypeFontCache(FontSize fs, FT_Face face, int pixels);
|
FreeTypeFontCache(FontSize fs, FT_Face face, int pixels);
|
||||||
~FreeTypeFontCache();
|
~FreeTypeFontCache();
|
||||||
void ClearFontCache() override;
|
void ClearFontCache() override;
|
||||||
GlyphID MapCharToGlyph(char32_t key, bool allow_fallback = true) override;
|
GlyphID MapCharToGlyph(char32_t key) override;
|
||||||
std::string GetFontName() override { return fmt::format("{}, {}", face->family_name, face->style_name); }
|
std::string GetFontName() override { return fmt::format("{}, {}", face->family_name, face->style_name); }
|
||||||
bool IsBuiltInFont() override { return false; }
|
bool IsBuiltInFont() override { return false; }
|
||||||
const void *GetOSHandle() override { return &face; }
|
const void *GetOSHandle() override { return &face; }
|
||||||
|
@ -104,6 +104,7 @@ void FreeTypeFontCache::SetFontSize(int pixels)
|
||||||
this->ascender = this->face->size->metrics.ascender >> 6;
|
this->ascender = this->face->size->metrics.ascender >> 6;
|
||||||
this->descender = this->face->size->metrics.descender >> 6;
|
this->descender = this->face->size->metrics.descender >> 6;
|
||||||
this->height = this->ascender - this->descender;
|
this->height = this->ascender - this->descender;
|
||||||
|
FontCache::UpdateCharacterHeight(this->fs);
|
||||||
} else {
|
} else {
|
||||||
/* Both FT_Set_Pixel_Sizes and FT_Select_Size failed. */
|
/* Both FT_Set_Pixel_Sizes and FT_Select_Size failed. */
|
||||||
Debug(fontcache, 0, "Font size selection failed. Using FontCache defaults.");
|
Debug(fontcache, 0, "Font size selection failed. Using FontCache defaults.");
|
||||||
|
@ -193,17 +194,11 @@ const Sprite *FreeTypeFontCache::InternalGetGlyph(GlyphID key, bool aa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GlyphID FreeTypeFontCache::MapCharToGlyph(char32_t key, bool allow_fallback)
|
GlyphID FreeTypeFontCache::MapCharToGlyph(char32_t key)
|
||||||
{
|
{
|
||||||
assert(IsPrintable(key));
|
assert(IsPrintable(key));
|
||||||
|
|
||||||
FT_UInt glyph = FT_Get_Char_Index(this->face, key);
|
return FT_Get_Char_Index(this->face, key);
|
||||||
|
|
||||||
if (glyph == 0 && allow_fallback && key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
|
|
||||||
return this->parent->MapCharToGlyph(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return glyph;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FT_Library _ft_library = nullptr;
|
FT_Library _ft_library = nullptr;
|
||||||
|
@ -225,15 +220,10 @@ public:
|
||||||
* format is 'font family name' or 'font family name, font style'.
|
* format is 'font family name' or 'font family name, font style'.
|
||||||
* @param fs The font size to load.
|
* @param fs The font size to load.
|
||||||
*/
|
*/
|
||||||
void LoadFont(FontSize fs, FontType fonttype) override
|
void LoadFont(FontSize fs, FontType fonttype, bool search, const std::string &font, std::span<const std::byte> os_handle) override
|
||||||
{
|
{
|
||||||
if (fonttype != FontType::TrueType) return;
|
if (fonttype != FontType::TrueType) return;
|
||||||
|
|
||||||
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
|
|
||||||
|
|
||||||
std::string font = GetFontCacheFontName(fs);
|
|
||||||
if (font.empty()) return;
|
|
||||||
|
|
||||||
if (_ft_library == nullptr) {
|
if (_ft_library == nullptr) {
|
||||||
if (FT_Init_FreeType(&_ft_library) != FT_Err_Ok) {
|
if (FT_Init_FreeType(&_ft_library) != FT_Err_Ok) {
|
||||||
ShowInfo("Unable to initialize FreeType, using sprite fonts instead");
|
ShowInfo("Unable to initialize FreeType, using sprite fonts instead");
|
||||||
|
@ -247,7 +237,9 @@ public:
|
||||||
|
|
||||||
/* If font is an absolute path to a ttf, try loading that first. */
|
/* If font is an absolute path to a ttf, try loading that first. */
|
||||||
int32_t index = 0;
|
int32_t index = 0;
|
||||||
if (settings->os_handle != nullptr) index = *static_cast<const int32_t *>(settings->os_handle);
|
if (os_handle.size() == sizeof(index)) {
|
||||||
|
index = *reinterpret_cast<const int32_t *>(os_handle.data());
|
||||||
|
}
|
||||||
FT_Error error = FT_New_Face(_ft_library, font.c_str(), index, &face);
|
FT_Error error = FT_New_Face(_ft_library, font.c_str(), index, &face);
|
||||||
|
|
||||||
if (error != FT_Err_Ok) {
|
if (error != FT_Err_Ok) {
|
||||||
|
@ -259,24 +251,24 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_FONTCONFIG
|
#ifdef WITH_FONTCONFIG
|
||||||
/* Try loading based on font face name (OS-wide fonts). */
|
/* If allowed to search, try loading based on font face name (OS-wide fonts). */
|
||||||
if (error != FT_Err_Ok) error = GetFontByFaceName(font, &face);
|
if (error != FT_Err_Ok && search) error = GetFontByFaceName(font, &face);
|
||||||
#endif /* WITH_FONTCONFIG */
|
#endif /* WITH_FONTCONFIG */
|
||||||
|
|
||||||
if (error == FT_Err_Ok) {
|
if (error == FT_Err_Ok) {
|
||||||
error = LoadFont(fs, face, font, GetFontCacheFontSize(fs));
|
error = LoadFont(fs, face, font, GetFontCacheFontSize(fs));
|
||||||
if (error != FT_Err_Ok) {
|
if (error != FT_Err_Ok) {
|
||||||
ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}, using sprite font instead", font, FontSizeToName(fs), error);
|
ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}", font, FontSizeToName(fs), error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FT_Done_Face(face);
|
FT_Done_Face(face);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetFallbackFont(struct FontCacheSettings *settings, const std::string &language_isocode, class MissingGlyphSearcher *callback) override
|
bool SetFallbackFont(const std::string &language_isocode, FontSizes bad_mask, MissingGlyphSearcher *callback) override
|
||||||
{
|
{
|
||||||
#ifdef WITH_FONTCONFIG
|
#ifdef WITH_FONTCONFIG
|
||||||
if (FontConfigSetFallbackFont(settings, language_isocode, callback)) return true;
|
if (FontConfigSetFallbackFont(language_isocode, bad_mask, callback)) return true;
|
||||||
#endif /* WITH_FONTCONFIG */
|
#endif /* WITH_FONTCONFIG */
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -38,8 +38,15 @@ static int ScaleFontTrad(int value)
|
||||||
*/
|
*/
|
||||||
SpriteFontCache::SpriteFontCache(FontSize fs) : FontCache(fs)
|
SpriteFontCache::SpriteFontCache(FontSize fs) : FontCache(fs)
|
||||||
{
|
{
|
||||||
this->height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
|
this->UpdateMetrics();
|
||||||
this->ascender = (this->height - ScaleFontTrad(FontCache::GetDefaultFontHeight(this->fs))) / 2;
|
}
|
||||||
|
|
||||||
|
void SpriteFontCache::UpdateMetrics()
|
||||||
|
{
|
||||||
|
this->height = ScaleGUITrad(DEFAULT_FONT_HEIGHT[this->fs]);
|
||||||
|
this->ascender = ScaleFontTrad(DEFAULT_FONT_ASCENDER[fs]);
|
||||||
|
this->descender = ScaleFontTrad(DEFAULT_FONT_ASCENDER[fs] - DEFAULT_FONT_HEIGHT[fs]);
|
||||||
|
FontCache::UpdateCharacterHeight(this->fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,30 +67,29 @@ SpriteID SpriteFontCache::GetSpriteIDForChar(char32_t key)
|
||||||
void SpriteFontCache::ClearFontCache()
|
void SpriteFontCache::ClearFontCache()
|
||||||
{
|
{
|
||||||
Layouter::ResetFontCache(this->fs);
|
Layouter::ResetFontCache(this->fs);
|
||||||
this->height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
|
this->UpdateMetrics();
|
||||||
this->ascender = (this->height - ScaleFontTrad(FontCache::GetDefaultFontHeight(this->fs))) / 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Sprite *SpriteFontCache::GetGlyph(GlyphID key)
|
const Sprite *SpriteFontCache::GetGlyph(GlyphID key)
|
||||||
{
|
{
|
||||||
SpriteID sprite = this->GetSpriteIDForChar(static_cast<char32_t>(key & ~SPRITE_GLYPH));
|
SpriteID sprite = this->GetSpriteIDForChar(static_cast<char32_t>(key));
|
||||||
if (sprite == 0) sprite = this->GetSpriteIDForChar('?');
|
if (sprite == 0) sprite = this->GetSpriteIDForChar('?');
|
||||||
return GetSprite(sprite, SpriteType::Font);
|
return GetSprite(sprite, SpriteType::Font);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint SpriteFontCache::GetGlyphWidth(GlyphID key)
|
uint SpriteFontCache::GetGlyphWidth(GlyphID key)
|
||||||
{
|
{
|
||||||
SpriteID sprite = this->GetSpriteIDForChar(static_cast<char32_t>(key & ~SPRITE_GLYPH));
|
SpriteID sprite = this->GetSpriteIDForChar(static_cast<char32_t>(key));
|
||||||
if (sprite == 0) sprite = this->GetSpriteIDForChar('?');
|
if (sprite == 0) sprite = this->GetSpriteIDForChar('?');
|
||||||
return SpriteExists(sprite) ? GetSprite(sprite, SpriteType::Font)->width + ScaleFontTrad(this->fs != FS_NORMAL ? 1 : 0) : 0;
|
return SpriteExists(sprite) ? GetSprite(sprite, SpriteType::Font)->width + ScaleFontTrad(this->fs != FS_NORMAL ? 1 : 0) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GlyphID SpriteFontCache::MapCharToGlyph(char32_t key, [[maybe_unused]] bool allow_fallback)
|
GlyphID SpriteFontCache::MapCharToGlyph(char32_t key)
|
||||||
{
|
{
|
||||||
assert(IsPrintable(key));
|
assert(IsPrintable(key));
|
||||||
SpriteID sprite = this->GetSpriteIDForChar(key);
|
SpriteID sprite = this->GetSpriteIDForChar(key);
|
||||||
if (sprite == 0) return 0;
|
if (sprite == 0) return 0;
|
||||||
return SPRITE_GLYPH | key;
|
return static_cast<GlyphID>(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpriteFontCache::GetDrawGlyphShadow()
|
bool SpriteFontCache::GetDrawGlyphShadow()
|
||||||
|
@ -95,14 +101,14 @@ class SpriteFontCacheFactory : public FontCacheFactory {
|
||||||
public:
|
public:
|
||||||
SpriteFontCacheFactory() : FontCacheFactory("sprite", "Sprite font provider") {}
|
SpriteFontCacheFactory() : FontCacheFactory("sprite", "Sprite font provider") {}
|
||||||
|
|
||||||
void LoadFont(FontSize fs, FontType fonttype) override
|
void LoadFont(FontSize fs, FontType fonttype, bool, const std::string &, std::span<const std::byte>) override
|
||||||
{
|
{
|
||||||
if (fonttype != FontType::Sprite) return;
|
if (fonttype != FontType::Sprite) return;
|
||||||
|
|
||||||
new SpriteFontCache(fs);
|
new SpriteFontCache(fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetFallbackFont(struct FontCacheSettings *, const std::string &, class MissingGlyphSearcher *) override
|
bool SetFallbackFont(const std::string &, FontSizes, class MissingGlyphSearcher *) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,11 +21,12 @@ public:
|
||||||
const Sprite *GetGlyph(GlyphID key) override;
|
const Sprite *GetGlyph(GlyphID key) override;
|
||||||
uint GetGlyphWidth(GlyphID key) override;
|
uint GetGlyphWidth(GlyphID key) override;
|
||||||
bool GetDrawGlyphShadow() override;
|
bool GetDrawGlyphShadow() override;
|
||||||
GlyphID MapCharToGlyph(char32_t key, bool allow_fallback = true) override;
|
GlyphID MapCharToGlyph(char32_t key) override;
|
||||||
std::string GetFontName() override { return "sprite"; }
|
std::string GetFontName() override { return "sprite"; }
|
||||||
bool IsBuiltInFont() override { return true; }
|
bool IsBuiltInFont() override { return true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void UpdateMetrics();
|
||||||
SpriteID GetSpriteIDForChar(char32_t key);
|
SpriteID GetSpriteIDForChar(char32_t key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -64,8 +64,6 @@ bool TrueTypeFontCache::GetDrawGlyphShadow()
|
||||||
|
|
||||||
uint TrueTypeFontCache::GetGlyphWidth(GlyphID key)
|
uint TrueTypeFontCache::GetGlyphWidth(GlyphID key)
|
||||||
{
|
{
|
||||||
if ((key & SPRITE_GLYPH) != 0) return this->parent->GetGlyphWidth(key);
|
|
||||||
|
|
||||||
GlyphEntry *glyph = this->GetGlyphPtr(key);
|
GlyphEntry *glyph = this->GetGlyphPtr(key);
|
||||||
if (glyph == nullptr || glyph->data == nullptr) {
|
if (glyph == nullptr || glyph->data == nullptr) {
|
||||||
this->GetGlyph(key);
|
this->GetGlyph(key);
|
||||||
|
@ -77,8 +75,6 @@ uint TrueTypeFontCache::GetGlyphWidth(GlyphID key)
|
||||||
|
|
||||||
const Sprite *TrueTypeFontCache::GetGlyph(GlyphID key)
|
const Sprite *TrueTypeFontCache::GetGlyph(GlyphID key)
|
||||||
{
|
{
|
||||||
if ((key & SPRITE_GLYPH) != 0) return this->parent->GetGlyph(key);
|
|
||||||
|
|
||||||
/* Check for the glyph in our cache */
|
/* Check for the glyph in our cache */
|
||||||
GlyphEntry *glyph = this->GetGlyphPtr(key);
|
GlyphEntry *glyph = this->GetGlyphPtr(key);
|
||||||
if (glyph != nullptr && glyph->data != nullptr) return glyph->GetSprite();
|
if (glyph != nullptr && glyph->data != nullptr) return glyph->GetSprite();
|
||||||
|
|
23
src/gfx.cpp
23
src/gfx.cpp
|
@ -481,6 +481,12 @@ static void SetColourRemap(TextColour colour)
|
||||||
_colour_remap_ptr = _string_colourremap;
|
_colour_remap_ptr = _string_colourremap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void RenderGlyph(FontCache *fc, GlyphID glyph, int left, int, int top, int)
|
||||||
|
{
|
||||||
|
const Sprite *sprite = fc->GetGlyph(glyph);
|
||||||
|
GfxMainBlitter(sprite, left, top, BlitterMode::ColourRemap);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drawing routine for drawing a laid out line of text.
|
* Drawing routine for drawing a laid out line of text.
|
||||||
* @param line String to draw.
|
* @param line String to draw.
|
||||||
|
@ -595,6 +601,9 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
|
||||||
colour_has_shadow = (colour & TC_NO_SHADE) == 0 && colour != TC_BLACK;
|
colour_has_shadow = (colour & TC_NO_SHADE) == 0 && colour != TC_BLACK;
|
||||||
SetColourRemap(do_shadow ? TC_BLACK : colour); // the last run also sets the colour for the truncation dots
|
SetColourRemap(do_shadow ? TC_BLACK : colour); // the last run also sets the colour for the truncation dots
|
||||||
if (do_shadow && (!fc->GetDrawGlyphShadow() || !colour_has_shadow)) continue;
|
if (do_shadow && (!fc->GetDrawGlyphShadow() || !colour_has_shadow)) continue;
|
||||||
|
int height = GetCharacterHeight(fc->GetSize());
|
||||||
|
|
||||||
|
auto render = RenderGlyph;
|
||||||
|
|
||||||
DrawPixelInfo *dpi = _cur_dpi;
|
DrawPixelInfo *dpi = _cur_dpi;
|
||||||
int dpi_left = dpi->left;
|
int dpi_left = dpi->left;
|
||||||
|
@ -612,14 +621,16 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
|
||||||
|
|
||||||
/* Truncated away. */
|
/* Truncated away. */
|
||||||
if (truncation && (begin_x < min_x || end_x > max_x)) continue;
|
if (truncation && (begin_x < min_x || end_x > max_x)) continue;
|
||||||
|
/* Outside the clipping area. */
|
||||||
|
if (begin_x > dpi_right || end_x < dpi_left) continue;
|
||||||
|
|
||||||
const Sprite *sprite = fc->GetGlyph(glyph);
|
if (do_shadow) {
|
||||||
/* Check clipping (the "+ 1" is for the shadow). */
|
begin_x += shadow_offset;
|
||||||
if (begin_x + sprite->x_offs > dpi_right || begin_x + sprite->x_offs + sprite->width /* - 1 + 1 */ < dpi_left) continue;
|
end_x += shadow_offset;
|
||||||
|
top += shadow_offset;
|
||||||
|
}
|
||||||
|
|
||||||
if (do_shadow && (glyph & SPRITE_GLYPH) != 0) continue;
|
render(fc, glyph, begin_x, end_x, top, top + height - 1);
|
||||||
|
|
||||||
GfxMainBlitter(sprite, begin_x + (do_shadow ? shadow_offset : 0), top + (do_shadow ? shadow_offset : 0), BlitterMode::ColourRemap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,18 +38,17 @@
|
||||||
std::unique_ptr<Layouter::LineCache> Layouter::linecache;
|
std::unique_ptr<Layouter::LineCache> Layouter::linecache;
|
||||||
|
|
||||||
/** Cache of Font instances. */
|
/** Cache of Font instances. */
|
||||||
Layouter::FontColourMap Layouter::fonts[FS_END];
|
std::unordered_map<FontIndex, Layouter::FontColourMap> Layouter::fonts;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new font.
|
* Construct a new font.
|
||||||
* @param size The font size to use for this font.
|
* @param font_index The font index to use for this font.
|
||||||
* @param colour The colour to draw this font in.
|
* @param colour The colour to draw this font in.
|
||||||
*/
|
*/
|
||||||
Font::Font(FontSize size, TextColour colour) :
|
Font::Font(FontIndex font_index, TextColour colour) :
|
||||||
fc(FontCache::Get(size)), colour(colour)
|
fc(FontCache::Get(font_index)), colour(colour)
|
||||||
{
|
{
|
||||||
assert(size < FS_END);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,7 +70,7 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, std::string_view s
|
||||||
const typename T::CharType *buffer_last = buff_begin + str.size() + 1;
|
const typename T::CharType *buffer_last = buff_begin + str.size() + 1;
|
||||||
typename T::CharType *buff = buff_begin;
|
typename T::CharType *buff = buff_begin;
|
||||||
FontMap &font_mapping = line.runs;
|
FontMap &font_mapping = line.runs;
|
||||||
Font *f = Layouter::GetFont(state.fontsize, state.cur_colour);
|
Font *f = Layouter::GetFont(state.font_index, state.cur_colour);
|
||||||
|
|
||||||
font_mapping.clear();
|
font_mapping.clear();
|
||||||
|
|
||||||
|
@ -80,7 +79,10 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, std::string_view s
|
||||||
* whenever the font changes, and convert the wide characters into a format
|
* whenever the font changes, and convert the wide characters into a format
|
||||||
* usable by ParagraphLayout.
|
* usable by ParagraphLayout.
|
||||||
*/
|
*/
|
||||||
for (char32_t c : Utf8View(str)) {
|
Utf8View view(str);
|
||||||
|
for (auto it = view.begin(); it != view.end(); /* nothing */) {
|
||||||
|
auto cur = it;
|
||||||
|
uint32_t c = *it++;
|
||||||
if (c == '\0' || c == '\n') {
|
if (c == '\0' || c == '\n') {
|
||||||
/* Caller should already have filtered out these characters. */
|
/* Caller should already have filtered out these characters. */
|
||||||
NOT_REACHED();
|
NOT_REACHED();
|
||||||
|
@ -95,19 +97,40 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, std::string_view s
|
||||||
} else {
|
} else {
|
||||||
/* Filter out non printable characters */
|
/* Filter out non printable characters */
|
||||||
if (!IsPrintable(c)) continue;
|
if (!IsPrintable(c)) continue;
|
||||||
|
|
||||||
|
if (IsTextDirectionChar(c)) {
|
||||||
/* Filter out text direction characters that shouldn't be drawn, and
|
/* Filter out text direction characters that shouldn't be drawn, and
|
||||||
* will not be handled in the fallback case because they are mostly
|
* will not be handled in the fallback case because they are mostly
|
||||||
* needed for RTL languages which need more proper shaping support. */
|
* needed for RTL languages which need more proper shaping support. */
|
||||||
if (!T::SUPPORTS_RTL && IsTextDirectionChar(c)) continue;
|
if constexpr (!T::SUPPORTS_RTL) continue;
|
||||||
|
|
||||||
buff += T::AppendToBuffer(buff, buffer_last, c);
|
buff += T::AppendToBuffer(buff, buffer_last, c);
|
||||||
if (buff >= buffer_last) break;
|
if (buff >= buffer_last) break;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (font_mapping.empty() || font_mapping.back().first != buff - buff_begin) {
|
FontIndex font_index = FontCache::GetFontIndexForCharacter(state.fontsize, c);
|
||||||
|
|
||||||
|
if (font_index == INVALID_FONT_INDEX) {
|
||||||
|
font_index = FontCache::GetDefaultFontIndex(state.fontsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.font_index == font_index) {
|
||||||
|
buff += T::AppendToBuffer(buff, buffer_last, c);
|
||||||
|
if (buff >= buffer_last) break;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This character goes in the next run so don't advance. */
|
||||||
|
state.font_index = font_index;
|
||||||
|
|
||||||
|
it = cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buff - buff_begin > 0 && (font_mapping.empty() || font_mapping.back().first != buff - buff_begin)) {
|
||||||
font_mapping.emplace_back(buff - buff_begin, f);
|
font_mapping.emplace_back(buff - buff_begin, f);
|
||||||
}
|
}
|
||||||
f = Layouter::GetFont(state.fontsize, state.cur_colour);
|
f = Layouter::GetFont(state.font_index, state.cur_colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Better safe than sorry. */
|
/* Better safe than sorry. */
|
||||||
|
@ -116,6 +139,14 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, std::string_view s
|
||||||
if (font_mapping.empty() || font_mapping.back().first != buff - buff_begin) {
|
if (font_mapping.empty() || font_mapping.back().first != buff - buff_begin) {
|
||||||
font_mapping.emplace_back(buff - buff_begin, f);
|
font_mapping.emplace_back(buff - buff_begin, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if constexpr (!std::is_same_v<T, FallbackParagraphLayoutFactory>) {
|
||||||
|
/* Don't layout if all runs use a built-in font and we're not using the fallback layouter. */
|
||||||
|
if (std::all_of(std::begin(font_mapping), std::end(font_mapping), [](const auto &i) { return i.second->fc->IsBuiltInFont(); })) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
line.layout = T::GetParagraphLayout(buff_begin, buff, font_mapping);
|
line.layout = T::GetParagraphLayout(buff_begin, buff, font_mapping);
|
||||||
line.state_after = state;
|
line.state_after = state;
|
||||||
}
|
}
|
||||||
|
@ -128,7 +159,7 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, std::string_view s
|
||||||
*/
|
*/
|
||||||
Layouter::Layouter(std::string_view str, int maxw, FontSize fontsize) : string(str)
|
Layouter::Layouter(std::string_view str, int maxw, FontSize fontsize) : string(str)
|
||||||
{
|
{
|
||||||
FontState state(TC_INVALID, fontsize);
|
FontState state(TC_INVALID, fontsize, FontCache::GetDefaultFontIndex(fontsize));
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
auto line_length = str.find_first_of('\n');
|
auto line_length = str.find_first_of('\n');
|
||||||
|
@ -341,13 +372,17 @@ ptrdiff_t Layouter::GetCharAtPosition(int x, size_t line_index) const
|
||||||
/**
|
/**
|
||||||
* Get a static font instance.
|
* Get a static font instance.
|
||||||
*/
|
*/
|
||||||
Font *Layouter::GetFont(FontSize size, TextColour colour)
|
Font *Layouter::GetFont(FontIndex font_index, TextColour colour)
|
||||||
{
|
{
|
||||||
FontColourMap::iterator it = fonts[size].find(colour);
|
if (font_index == INVALID_FONT_INDEX) return nullptr;
|
||||||
if (it != fonts[size].end()) return it->second.get();
|
assert(font_index < FontCache::Get().size());
|
||||||
|
|
||||||
fonts[size][colour] = std::make_unique<Font>(size, colour);
|
FontColourMap &fcm = Layouter::fonts[font_index];
|
||||||
return fonts[size][colour].get();
|
auto it = fcm.find(colour);
|
||||||
|
if (it != fcm.end()) return it->second.get();
|
||||||
|
|
||||||
|
fcm[colour] = std::make_unique<Font>(font_index, colour);
|
||||||
|
return fcm[colour].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -362,11 +397,10 @@ void Layouter::Initialize()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset cached font information.
|
* Reset cached font information.
|
||||||
* @param size Font size to reset.
|
|
||||||
*/
|
*/
|
||||||
void Layouter::ResetFontCache(FontSize size)
|
void Layouter::ResetFontCache([[maybe_unused]] FontSize size)
|
||||||
{
|
{
|
||||||
fonts[size].clear();
|
Layouter::fonts.clear();
|
||||||
|
|
||||||
/* We must reset the linecache since it references the just freed fonts */
|
/* We must reset the linecache since it references the just freed fonts */
|
||||||
ResetLineCache();
|
ResetLineCache();
|
||||||
|
|
|
@ -23,11 +23,12 @@
|
||||||
*/
|
*/
|
||||||
struct FontState {
|
struct FontState {
|
||||||
FontSize fontsize; ///< Current font size.
|
FontSize fontsize; ///< Current font size.
|
||||||
|
FontIndex font_index; ///< Current font index.
|
||||||
TextColour cur_colour; ///< Current text colour.
|
TextColour cur_colour; ///< Current text colour.
|
||||||
std::vector<TextColour> colour_stack; ///< Stack of colours to assist with colour switching.
|
std::vector<TextColour> colour_stack; ///< Stack of colours to assist with colour switching.
|
||||||
|
|
||||||
FontState() : fontsize(FS_END), cur_colour(TC_INVALID) {}
|
FontState() : fontsize(FS_END), font_index(INVALID_FONT_INDEX), cur_colour(TC_INVALID) {}
|
||||||
FontState(TextColour colour, FontSize fontsize) : fontsize(fontsize), cur_colour(colour) {}
|
FontState(TextColour colour, FontSize fontsize, FontIndex font_index) : fontsize(fontsize), font_index(font_index), cur_colour(colour) {}
|
||||||
|
|
||||||
auto operator<=>(const FontState &) const = default;
|
auto operator<=>(const FontState &) const = default;
|
||||||
|
|
||||||
|
@ -67,6 +68,7 @@ struct FontState {
|
||||||
inline void SetFontSize(FontSize f)
|
inline void SetFontSize(FontSize f)
|
||||||
{
|
{
|
||||||
this->fontsize = f;
|
this->fontsize = f;
|
||||||
|
this->font_index = FontCache::GetDefaultFontIndex(this->fontsize);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,9 +87,10 @@ template <> struct std::hash<FontState> {
|
||||||
std::size_t operator()(const FontState &state) const noexcept
|
std::size_t operator()(const FontState &state) const noexcept
|
||||||
{
|
{
|
||||||
size_t h1 = std::hash<FontSize>{}(state.fontsize);
|
size_t h1 = std::hash<FontSize>{}(state.fontsize);
|
||||||
size_t h2 = std::hash<TextColour>{}(state.cur_colour);
|
size_t h2 = std::hash<FontIndex>{}(state.font_index);
|
||||||
size_t h3 = std::hash<std::vector<TextColour>>{}(state.colour_stack);
|
size_t h3 = std::hash<TextColour>{}(state.cur_colour);
|
||||||
return h1 ^ (h2 << 1) ^ (h3 << 2);
|
size_t h4 = std::hash<std::vector<TextColour>>{}(state.colour_stack);
|
||||||
|
return h1 ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -99,7 +102,7 @@ public:
|
||||||
FontCache *fc; ///< The font we are using.
|
FontCache *fc; ///< The font we are using.
|
||||||
TextColour colour; ///< The colour this font has to be.
|
TextColour colour; ///< The colour this font has to be.
|
||||||
|
|
||||||
Font(FontSize size, TextColour colour);
|
Font(FontIndex font_index, TextColour colour);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Mapping from index to font. The pointer is owned by FontColourMap. */
|
/** Mapping from index to font. The pointer is owned by FontColourMap. */
|
||||||
|
@ -206,9 +209,9 @@ private:
|
||||||
static LineCacheItem &GetCachedParagraphLayout(std::string_view str, const FontState &state);
|
static LineCacheItem &GetCachedParagraphLayout(std::string_view str, const FontState &state);
|
||||||
|
|
||||||
using FontColourMap = std::map<TextColour, std::unique_ptr<Font>>;
|
using FontColourMap = std::map<TextColour, std::unique_ptr<Font>>;
|
||||||
static FontColourMap fonts[FS_END];
|
static std::unordered_map<FontIndex, FontColourMap> fonts;
|
||||||
public:
|
public:
|
||||||
static Font *GetFont(FontSize size, TextColour colour);
|
static Font *GetFont(FontIndex font_index, TextColour colour);
|
||||||
|
|
||||||
Layouter(std::string_view str, int maxw = INT32_MAX, FontSize fontsize = FS_NORMAL);
|
Layouter(std::string_view str, int maxw = INT32_MAX, FontSize fontsize = FS_NORMAL);
|
||||||
Dimension GetBounds();
|
Dimension GetBounds();
|
||||||
|
@ -216,7 +219,7 @@ public:
|
||||||
ptrdiff_t GetCharAtPosition(int x, size_t line_index) const;
|
ptrdiff_t GetCharAtPosition(int x, size_t line_index) const;
|
||||||
|
|
||||||
static void Initialize();
|
static void Initialize();
|
||||||
static void ResetFontCache(FontSize size);
|
static void ResetFontCache(FontSize fs);
|
||||||
static void ResetLineCache();
|
static void ResetLineCache();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ public:
|
||||||
int GetGlyphCount() const override { return static_cast<int>(this->glyphs.size()); }
|
int GetGlyphCount() const override { return static_cast<int>(this->glyphs.size()); }
|
||||||
std::span<const GlyphID> GetGlyphs() const override { return this->glyphs; }
|
std::span<const GlyphID> GetGlyphs() const override { return this->glyphs; }
|
||||||
std::span<const Position> GetPositions() const override { return this->positions; }
|
std::span<const Position> GetPositions() const override { return this->positions; }
|
||||||
int GetLeading() const override { return this->GetFont()->fc->GetHeight(); }
|
int GetLeading() const override { return GetCharacterHeight(this->GetFont()->fc->GetSize()); }
|
||||||
std::span<const int> GetGlyphToCharMap() const override { return this->glyph_to_char; }
|
std::span<const int> GetGlyphToCharMap() const override { return this->glyph_to_char; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -112,23 +112,17 @@ public:
|
||||||
FallbackParagraphLayout::FallbackVisualRun::FallbackVisualRun(Font *font, const char32_t *chars, int char_count, int char_offset, int x) :
|
FallbackParagraphLayout::FallbackVisualRun::FallbackVisualRun(Font *font, const char32_t *chars, int char_count, int char_offset, int x) :
|
||||||
font(font)
|
font(font)
|
||||||
{
|
{
|
||||||
const bool isbuiltin = font->fc->IsBuiltInFont();
|
|
||||||
|
|
||||||
this->glyphs.reserve(char_count);
|
this->glyphs.reserve(char_count);
|
||||||
this->glyph_to_char.reserve(char_count);
|
this->glyph_to_char.reserve(char_count);
|
||||||
this->positions.reserve(char_count);
|
this->positions.reserve(char_count);
|
||||||
|
|
||||||
|
FontCache &fc = *this->font->fc;
|
||||||
|
int y_offset = fc.GetGlyphYOffset();;
|
||||||
int advance = x;
|
int advance = x;
|
||||||
for (int i = 0; i < char_count; i++) {
|
for (int i = 0; i < char_count; i++) {
|
||||||
const GlyphID &glyph_id = this->glyphs.emplace_back(font->fc->MapCharToGlyph(chars[i]));
|
const GlyphID &glyph_id = this->glyphs.emplace_back(fc.MapCharToGlyph(chars[i]));
|
||||||
int x_advance = font->fc->GetGlyphWidth(glyph_id);
|
int x_advance = fc.GetGlyphWidth(glyph_id);
|
||||||
if (isbuiltin) {
|
this->positions.emplace_back(advance, advance + x_advance - 1, y_offset); // No ascender adjustment.
|
||||||
this->positions.emplace_back(advance, advance + x_advance - 1, font->fc->GetAscender()); // Apply sprite font's ascender.
|
|
||||||
} else if (chars[i] >= SCC_SPRITE_START && chars[i] <= SCC_SPRITE_END) {
|
|
||||||
this->positions.emplace_back(advance, advance + x_advance - 1, (font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(font->fc->GetSize()))) / 2); // Align sprite font to centre
|
|
||||||
} else {
|
|
||||||
this->positions.emplace_back(advance, advance + x_advance - 1, 0); // No ascender adjustment.
|
|
||||||
}
|
|
||||||
advance += x_advance;
|
advance += x_advance;
|
||||||
this->glyph_to_char.push_back(char_offset + i);
|
this->glyph_to_char.push_back(char_offset + i);
|
||||||
}
|
}
|
||||||
|
@ -234,6 +228,7 @@ std::unique_ptr<const ParagraphLayouter::Line> FallbackParagraphLayout::NextLine
|
||||||
}
|
}
|
||||||
|
|
||||||
const FontCache *fc = iter->second->fc;
|
const FontCache *fc = iter->second->fc;
|
||||||
|
assert(fc != nullptr);
|
||||||
const char32_t *next_run = this->buffer_begin + iter->first;
|
const char32_t *next_run = this->buffer_begin + iter->first;
|
||||||
|
|
||||||
const char32_t *begin = this->buffer;
|
const char32_t *begin = this->buffer;
|
||||||
|
@ -251,6 +246,7 @@ std::unique_ptr<const ParagraphLayouter::Line> FallbackParagraphLayout::NextLine
|
||||||
|
|
||||||
if (this->buffer == next_run) {
|
if (this->buffer == next_run) {
|
||||||
int w = l->GetWidth();
|
int w = l->GetWidth();
|
||||||
|
assert(iter->second->fc != nullptr);
|
||||||
l->emplace_back(iter->second, begin, this->buffer - begin, begin - this->buffer_begin, w);
|
l->emplace_back(iter->second, begin, this->buffer - begin, begin - this->buffer_begin, w);
|
||||||
++iter;
|
++iter;
|
||||||
assert(iter != this->runs.end());
|
assert(iter != this->runs.end());
|
||||||
|
|
|
@ -26,5 +26,24 @@ public:
|
||||||
static size_t AppendToBuffer(char32_t *buff, const char32_t *buffer_last, char32_t c);
|
static size_t AppendToBuffer(char32_t *buff, const char32_t *buffer_last, char32_t c);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swap paired brackets for fallback RTL layouting.
|
||||||
|
* @param c Character to swap.
|
||||||
|
* @return Swapped character, or original character if it is not a paired bracket.
|
||||||
|
*/
|
||||||
|
inline char32_t SwapRtlPairedCharacters(char32_t c)
|
||||||
|
{
|
||||||
|
/* There are many more paired brackets, but for fallback purposes we only handle ASCII brackets. */
|
||||||
|
/* https://www.unicode.org/Public/UCD/latest/ucd/BidiBrackets.txt */
|
||||||
|
switch (c) {
|
||||||
|
case U'(': return U')';
|
||||||
|
case U')': return U'(';
|
||||||
|
case U'[': return U']';
|
||||||
|
case U']': return U'[';
|
||||||
|
case U'{': return U'}';
|
||||||
|
case U'}': return U'{';
|
||||||
|
default: return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* GFX_LAYOUT_FALLBACK_H */
|
#endif /* GFX_LAYOUT_FALLBACK_H */
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
|
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "gfx_layout_icu.h"
|
#include "gfx_layout_icu.h"
|
||||||
|
#include "gfx_layout_fallback.h"
|
||||||
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
#include "string_func.h"
|
||||||
#include "strings_func.h"
|
#include "strings_func.h"
|
||||||
#include "language.h"
|
#include "language.h"
|
||||||
#include "table/control_codes.h"
|
#include "table/control_codes.h"
|
||||||
|
@ -51,6 +53,7 @@ public:
|
||||||
ICURun(int start, int length, UBiDiLevel level, UScriptCode script = USCRIPT_UNKNOWN, Font *font = nullptr) : start(start), length(length), level(level), script(script), font(font) {}
|
ICURun(int start, int length, UBiDiLevel level, UScriptCode script = USCRIPT_UNKNOWN, Font *font = nullptr) : start(start), length(length), level(level), script(script), font(font) {}
|
||||||
|
|
||||||
void Shape(UChar *buff, size_t length);
|
void Shape(UChar *buff, size_t length);
|
||||||
|
void FallbackShape(UChar *buff);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,7 +79,7 @@ public:
|
||||||
std::span<const int> GetGlyphToCharMap() const override { return this->glyph_to_char; }
|
std::span<const int> GetGlyphToCharMap() const override { return this->glyph_to_char; }
|
||||||
|
|
||||||
const Font *GetFont() const override { return this->font; }
|
const Font *GetFont() const override { return this->font; }
|
||||||
int GetLeading() const override { return this->font->fc->GetHeight(); }
|
int GetLeading() const override { return GetCharacterHeight(this->font->fc->GetSize()); }
|
||||||
int GetGlyphCount() const override { return this->glyphs.size(); }
|
int GetGlyphCount() const override { return this->glyphs.size(); }
|
||||||
int GetAdvance() const { return this->total_advance; }
|
int GetAdvance() const { return this->total_advance; }
|
||||||
};
|
};
|
||||||
|
@ -135,12 +138,52 @@ ICUParagraphLayout::ICUVisualRun::ICUVisualRun(const ICURun &run, int x) :
|
||||||
assert(!run.positions.empty());
|
assert(!run.positions.empty());
|
||||||
this->positions.reserve(run.positions.size());
|
this->positions.reserve(run.positions.size());
|
||||||
|
|
||||||
|
int y_offset = this->font->fc->GetGlyphYOffset();
|
||||||
/* Copy positions, moving x coordinate by x offset. */
|
/* Copy positions, moving x coordinate by x offset. */
|
||||||
for (const auto &pos : run.positions) {
|
for (const auto &pos : run.positions) {
|
||||||
this->positions.emplace_back(pos.left + x, pos.right + x, pos.top);
|
this->positions.emplace_back(pos.left + x, pos.right + x, pos.top + y_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manually shape a run for built-in non-truetype fonts.
|
||||||
|
* Similar to but not quite the same as \a UniscribeRun::FallbackShape.
|
||||||
|
* @param buff The complete buffer of the run.
|
||||||
|
*/
|
||||||
|
void ICURun::FallbackShape(UChar *buff)
|
||||||
|
{
|
||||||
|
this->glyphs.reserve(this->length);
|
||||||
|
this->glyph_to_char.reserve(this->length);
|
||||||
|
|
||||||
|
/* Read each UTF-16 character, mapping to an appropriate glyph. */
|
||||||
|
for (int i = this->start; i < this->start + this->length; ++i) {
|
||||||
|
char32_t c = Utf16DecodeChar(buff + i);
|
||||||
|
if (this->level & 1) c = SwapRtlPairedCharacters(c);
|
||||||
|
this->glyphs.emplace_back(this->font->fc->MapCharToGlyph(c));
|
||||||
|
this->glyph_to_char.push_back(i);
|
||||||
|
if (Utf16IsLeadSurrogate(*(buff + i))) ++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reverse the sequence if this run is RTL. */
|
||||||
|
if (this->level & 1) {
|
||||||
|
std::reverse(std::begin(this->glyphs), std::end(this->glyphs));
|
||||||
|
std::reverse(std::begin(this->glyph_to_char), std::end(this->glyph_to_char));
|
||||||
|
}
|
||||||
|
|
||||||
|
this->positions.reserve(this->glyphs.size());
|
||||||
|
|
||||||
|
/* Set positions of each glyph. */
|
||||||
|
int y_offset = (GetCharacterHeight(this->font->fc->GetSize()) - this->font->fc->GetHeight()) / 2;
|
||||||
|
int advance = 0;
|
||||||
|
for (const GlyphID glyph : this->glyphs) {
|
||||||
|
int x_advance = this->font->fc->GetGlyphWidth(glyph);
|
||||||
|
this->positions.emplace_back(advance, advance + x_advance - 1, y_offset);
|
||||||
|
this->advance.push_back(x_advance);
|
||||||
|
advance += x_advance;
|
||||||
|
}
|
||||||
|
this->total_advance = advance;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shape a single run.
|
* Shape a single run.
|
||||||
*
|
*
|
||||||
|
@ -149,6 +192,17 @@ ICUParagraphLayout::ICUVisualRun::ICUVisualRun(const ICURun &run, int x) :
|
||||||
*/
|
*/
|
||||||
void ICURun::Shape(UChar *buff, size_t buff_length)
|
void ICURun::Shape(UChar *buff, size_t buff_length)
|
||||||
{
|
{
|
||||||
|
/* Make sure any former run is lost. */
|
||||||
|
this->glyphs.clear();
|
||||||
|
this->glyph_to_char.clear();
|
||||||
|
this->positions.clear();
|
||||||
|
this->advance.clear();
|
||||||
|
|
||||||
|
if (this->font->fc->IsBuiltInFont()) {
|
||||||
|
this->FallbackShape(buff);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto hbfont = hb_ft_font_create_referenced(*(static_cast<const FT_Face *>(font->fc->GetOSHandle())));
|
auto hbfont = hb_ft_font_create_referenced(*(static_cast<const FT_Face *>(font->fc->GetOSHandle())));
|
||||||
/* Match the flags with how we render the glyphs. */
|
/* Match the flags with how we render the glyphs. */
|
||||||
hb_ft_font_set_load_flags(hbfont, GetFontAAState() ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
|
hb_ft_font_set_load_flags(hbfont, GetFontAAState() ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
|
||||||
|
@ -170,12 +224,6 @@ void ICURun::Shape(UChar *buff, size_t buff_length)
|
||||||
auto glyph_info = hb_buffer_get_glyph_infos(hbbuf, &glyph_count);
|
auto glyph_info = hb_buffer_get_glyph_infos(hbbuf, &glyph_count);
|
||||||
auto glyph_pos = hb_buffer_get_glyph_positions(hbbuf, &glyph_count);
|
auto glyph_pos = hb_buffer_get_glyph_positions(hbbuf, &glyph_count);
|
||||||
|
|
||||||
/* Make sure any former run is lost. */
|
|
||||||
this->glyphs.clear();
|
|
||||||
this->glyph_to_char.clear();
|
|
||||||
this->positions.clear();
|
|
||||||
this->advance.clear();
|
|
||||||
|
|
||||||
/* Reserve space, as we already know the size. */
|
/* Reserve space, as we already know the size. */
|
||||||
this->glyphs.reserve(glyph_count);
|
this->glyphs.reserve(glyph_count);
|
||||||
this->glyph_to_char.reserve(glyph_count);
|
this->glyph_to_char.reserve(glyph_count);
|
||||||
|
@ -183,20 +231,12 @@ void ICURun::Shape(UChar *buff, size_t buff_length)
|
||||||
this->advance.reserve(glyph_count);
|
this->advance.reserve(glyph_count);
|
||||||
|
|
||||||
/* Prepare the glyphs/position. ICUVisualRun will give the position an offset if needed. */
|
/* Prepare the glyphs/position. ICUVisualRun will give the position an offset if needed. */
|
||||||
|
int y_offset = (GetCharacterHeight(this->font->fc->GetSize()) - this->font->fc->GetHeight()) / 2;
|
||||||
hb_position_t advance = 0;
|
hb_position_t advance = 0;
|
||||||
for (unsigned int i = 0; i < glyph_count; i++) {
|
for (unsigned int i = 0; i < glyph_count; i++) {
|
||||||
int x_advance;
|
int x_advance = glyph_pos[i].x_advance / FONT_SCALE;
|
||||||
|
|
||||||
if (buff[glyph_info[i].cluster] >= SCC_SPRITE_START && buff[glyph_info[i].cluster] <= SCC_SPRITE_END && glyph_info[i].codepoint == 0) {
|
|
||||||
auto glyph = this->font->fc->MapCharToGlyph(buff[glyph_info[i].cluster]);
|
|
||||||
x_advance = this->font->fc->GetGlyphWidth(glyph);
|
|
||||||
this->glyphs.push_back(glyph);
|
|
||||||
this->positions.emplace_back(advance, advance + x_advance - 1, (this->font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(this->font->fc->GetSize()))) / 2); // Align sprite font to centre
|
|
||||||
} else {
|
|
||||||
x_advance = glyph_pos[i].x_advance / FONT_SCALE;
|
|
||||||
this->glyphs.push_back(glyph_info[i].codepoint);
|
this->glyphs.push_back(glyph_info[i].codepoint);
|
||||||
this->positions.emplace_back(glyph_pos[i].x_offset / FONT_SCALE + advance, glyph_pos[i].x_offset / FONT_SCALE + advance + x_advance - 1, glyph_pos[i].y_offset / FONT_SCALE);
|
this->positions.emplace_back(glyph_pos[i].x_offset / FONT_SCALE + advance, glyph_pos[i].x_offset / FONT_SCALE + advance + x_advance - 1, glyph_pos[i].y_offset / FONT_SCALE + y_offset);
|
||||||
}
|
|
||||||
|
|
||||||
this->glyph_to_char.push_back(glyph_info[i].cluster);
|
this->glyph_to_char.push_back(glyph_info[i].cluster);
|
||||||
this->advance.push_back(x_advance);
|
this->advance.push_back(x_advance);
|
||||||
|
@ -359,11 +399,6 @@ std::vector<ICURun> ItemizeStyle(std::vector<ICURun> &runs_current, FontMap &fon
|
||||||
/* Can't layout an empty string. */
|
/* Can't layout an empty string. */
|
||||||
if (length == 0) return nullptr;
|
if (length == 0) return nullptr;
|
||||||
|
|
||||||
/* Can't layout our in-built sprite fonts. */
|
|
||||||
for (auto const &[position, font] : font_mapping) {
|
|
||||||
if (font->fc->IsBuiltInFont()) return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto runs = ItemizeBidi(buff, length);
|
auto runs = ItemizeBidi(buff, length);
|
||||||
runs = ItemizeScript(buff, length, runs);
|
runs = ItemizeScript(buff, length, runs);
|
||||||
runs = ItemizeStyle(runs, font_mapping);
|
runs = ItemizeStyle(runs, font_mapping);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
CoreTextFontCache::CoreTextFontCache(FontSize fs, CFAutoRelease<CTFontDescriptorRef> &&font, int pixels) : TrueTypeFontCache(fs, pixels), font_desc(std::move(font))
|
CoreTextFontCache::CoreTextFontCache(FontSize fs, CFAutoRelease<CTFontDescriptorRef> &&font, int pixels) : TrueTypeFontCache(fs, pixels), font_desc(std::move(font))
|
||||||
{
|
{
|
||||||
this->SetFontSize(pixels);
|
this->SetFontSize(pixels);
|
||||||
|
FontCache::UpdateCharacterHeight(this->fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,7 +95,7 @@ void CoreTextFontCache::SetFontSize(int pixels)
|
||||||
Debug(fontcache, 2, "Loaded font '{}' with size {}", this->font_name, pixels);
|
Debug(fontcache, 2, "Loaded font '{}' with size {}", this->font_name, pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlyphID CoreTextFontCache::MapCharToGlyph(char32_t key, bool allow_fallback)
|
GlyphID CoreTextFontCache::MapCharToGlyph(char32_t key)
|
||||||
{
|
{
|
||||||
assert(IsPrintable(key));
|
assert(IsPrintable(key));
|
||||||
|
|
||||||
|
@ -112,10 +113,6 @@ GlyphID CoreTextFontCache::MapCharToGlyph(char32_t key, bool allow_fallback)
|
||||||
return glyph[0];
|
return glyph[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allow_fallback && key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
|
|
||||||
return this->parent->MapCharToGlyph(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +208,7 @@ public:
|
||||||
* fallback search, use it. Otherwise, try to resolve it by font name.
|
* fallback search, use it. Otherwise, try to resolve it by font name.
|
||||||
* @param fs The font size to load.
|
* @param fs The font size to load.
|
||||||
*/
|
*/
|
||||||
void LoadFont(FontSize fs, FontType fonttype)
|
void LoadFont(FontSize fs, FontType fonttype, bool search, const std::string &font, std::span<const std::byte> os_handle) override
|
||||||
{
|
{
|
||||||
if (fonttype != FontType::TrueType) return;
|
if (fonttype != FontType::TrueType) return;
|
||||||
|
|
||||||
|
@ -259,7 +256,7 @@ public:
|
||||||
new CoreTextFontCache(fs, std::move(font_ref), GetFontCacheFontSize(fs));
|
new CoreTextFontCache(fs, std::move(font_ref), GetFontCacheFontSize(fs));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback) override
|
bool SetFallbackFont(const std::string &language_isocode, FontSizes bad_mask, MissingGlyphSearcher *callback)
|
||||||
{
|
{
|
||||||
/* Determine fallback font using CoreText. This uses the language isocode
|
/* Determine fallback font using CoreText. This uses the language isocode
|
||||||
* to find a suitable font. CoreText is available from 10.5 onwards. */
|
* to find a suitable font. CoreText is available from 10.5 onwards. */
|
||||||
|
@ -305,7 +302,7 @@ public:
|
||||||
/* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
|
/* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
|
||||||
if (symbolic_traits & kCTFontBoldTrait) continue;
|
if (symbolic_traits & kCTFontBoldTrait) continue;
|
||||||
/* Select monospaced fonts if asked for. */
|
/* Select monospaced fonts if asked for. */
|
||||||
if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
|
if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != fontsizes.Test(FS_MONO)) continue;
|
||||||
|
|
||||||
/* Get font name. */
|
/* Get font name. */
|
||||||
char buffer[128];
|
char buffer[128];
|
||||||
|
@ -323,7 +320,13 @@ public:
|
||||||
if (name.starts_with(".") || name.starts_with("LastResort")) continue;
|
if (name.starts_with(".") || name.starts_with("LastResort")) continue;
|
||||||
|
|
||||||
/* Save result. */
|
/* Save result. */
|
||||||
callback->SetFontNames(settings, name);
|
for (FontSize fs : bad_mask) {
|
||||||
|
GetFontCacheSubSetting(fs)->AddFallback(name, std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitFontCache(bad_mask);
|
||||||
|
// return true;
|
||||||
|
|
||||||
if (!callback->FindMissingGlyphs()) {
|
if (!callback->FindMissingGlyphs()) {
|
||||||
Debug(fontcache, 2, "CT-Font for {}: {}", language_isocode, name);
|
Debug(fontcache, 2, "CT-Font for {}: {}", language_isocode, name);
|
||||||
result = true;
|
result = true;
|
||||||
|
@ -335,7 +338,9 @@ public:
|
||||||
if (!result) {
|
if (!result) {
|
||||||
/* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
|
/* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
|
||||||
* supports. If we didn't find any other font, just try it, maybe we get lucky. */
|
* supports. If we didn't find any other font, just try it, maybe we get lucky. */
|
||||||
callback->SetFontNames(settings, "Arial Unicode MS");
|
for (FontSize fs : bad_mask) {
|
||||||
|
GetFontCacheSubSetting(fs)->AddFallback("Arial Unicode MS", std::nullopt);
|
||||||
|
}
|
||||||
result = !callback->FindMissingGlyphs();
|
result = !callback->FindMissingGlyphs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
~CoreTextFontCache() {}
|
~CoreTextFontCache() {}
|
||||||
|
|
||||||
void ClearFontCache() override;
|
void ClearFontCache() override;
|
||||||
GlyphID MapCharToGlyph(char32_t key, bool allow_fallback = true) override;
|
GlyphID MapCharToGlyph(char32_t key) override;
|
||||||
std::string GetFontName() override { return font_name; }
|
std::string GetFontName() override { return font_name; }
|
||||||
bool IsBuiltInFont() override { return false; }
|
bool IsBuiltInFont() override { return false; }
|
||||||
const void *GetOSHandle() override { return font.get(); }
|
const void *GetOSHandle() override { return font.get(); }
|
||||||
|
|
|
@ -52,7 +52,7 @@ extern "C" {
|
||||||
/** Cached current locale. */
|
/** Cached current locale. */
|
||||||
static CFAutoRelease<CFLocaleRef> _osx_locale;
|
static CFAutoRelease<CFLocaleRef> _osx_locale;
|
||||||
/** CoreText cache for font information, cleared when OTTD changes fonts. */
|
/** CoreText cache for font information, cleared when OTTD changes fonts. */
|
||||||
static CFAutoRelease<CTFontRef> _font_cache[FS_END];
|
static std::unordered_map<FontIndex, CFAutoRelease<CTFontRef>> _font_cache;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,7 +88,7 @@ public:
|
||||||
std::span<const int> GetGlyphToCharMap() const override { return this->glyph_to_char; }
|
std::span<const int> GetGlyphToCharMap() const override { return this->glyph_to_char; }
|
||||||
|
|
||||||
const Font *GetFont() const override { return this->font; }
|
const Font *GetFont() const override { return this->font; }
|
||||||
int GetLeading() const override { return this->font->fc->GetHeight(); }
|
int GetLeading() const override { return GetCharacterHeight(this->font->fc->GetSize()); }
|
||||||
int GetGlyphCount() const override { return (int)this->glyphs.size(); }
|
int GetGlyphCount() const override { return (int)this->glyphs.size(); }
|
||||||
int GetAdvance() const { return this->total_advance; }
|
int GetAdvance() const { return this->total_advance; }
|
||||||
};
|
};
|
||||||
|
@ -137,17 +137,17 @@ public:
|
||||||
|
|
||||||
|
|
||||||
/** Get the width of an encoded sprite font character. */
|
/** Get the width of an encoded sprite font character. */
|
||||||
static CGFloat SpriteFontGetWidth(void *ref_con)
|
static CGFloat CustomFontGetWidth(void *ref_con)
|
||||||
{
|
{
|
||||||
FontSize fs = (FontSize)((size_t)ref_con >> 24);
|
FontIndex fi = static_cast<FontIndex>(reinterpret_cast<uintptr_t>(ref_con) >> 24);
|
||||||
char32_t c = (char32_t)((size_t)ref_con & 0xFFFFFF);
|
char32_t c = static_cast<char32_t>(reinterpret_cast<uintptr_t>(ref_con) & 0xFFFFFF);
|
||||||
|
|
||||||
return GetGlyphWidth(fs, c);
|
return FontCache::Get(fi)->GetGlyphWidth(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const CTRunDelegateCallbacks _sprite_font_callback = {
|
static const CTRunDelegateCallbacks _sprite_font_callback = {
|
||||||
kCTRunDelegateCurrentVersion, nullptr, nullptr, nullptr,
|
kCTRunDelegateCurrentVersion, nullptr, nullptr, nullptr,
|
||||||
&SpriteFontGetWidth
|
&CustomFontGetWidth
|
||||||
};
|
};
|
||||||
|
|
||||||
/* static */ std::unique_ptr<ParagraphLayouter> CoreTextParagraphLayoutFactory::GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &font_mapping)
|
/* static */ std::unique_ptr<ParagraphLayouter> CoreTextParagraphLayoutFactory::GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &font_mapping)
|
||||||
|
@ -158,11 +158,6 @@ static const CTRunDelegateCallbacks _sprite_font_callback = {
|
||||||
ptrdiff_t length = buff_end - buff;
|
ptrdiff_t length = buff_end - buff;
|
||||||
if (length == 0) return nullptr;
|
if (length == 0) return nullptr;
|
||||||
|
|
||||||
/* Can't layout our in-built sprite fonts. */
|
|
||||||
for (const auto &[position, font] : font_mapping) {
|
|
||||||
if (font->fc->IsBuiltInFont()) return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make attributed string with embedded font information. */
|
/* Make attributed string with embedded font information. */
|
||||||
CFAutoRelease<CFMutableAttributedStringRef> str(CFAttributedStringCreateMutable(kCFAllocatorDefault, 0));
|
CFAutoRelease<CFMutableAttributedStringRef> str(CFAttributedStringCreateMutable(kCFAllocatorDefault, 0));
|
||||||
CFAttributedStringBeginEditing(str.get());
|
CFAttributedStringBeginEditing(str.get());
|
||||||
|
@ -181,12 +176,12 @@ static const CTRunDelegateCallbacks _sprite_font_callback = {
|
||||||
|
|
||||||
CTFontRef font_handle = static_cast<CTFontRef>(font->fc->GetOSHandle());
|
CTFontRef font_handle = static_cast<CTFontRef>(font->fc->GetOSHandle());
|
||||||
if (font_handle == nullptr) {
|
if (font_handle == nullptr) {
|
||||||
if (!_font_cache[font->fc->GetSize()]) {
|
if (!_font_cache[font->fc->GetIndex()]) {
|
||||||
/* Cache font information. */
|
/* Cache font information. */
|
||||||
CFAutoRelease<CFStringRef> font_name(CFStringCreateWithCString(kCFAllocatorDefault, font->fc->GetFontName().c_str(), kCFStringEncodingUTF8));
|
CFAutoRelease<CFStringRef> font_name(CFStringCreateWithCString(kCFAllocatorDefault, font->fc->GetFontName().c_str(), kCFStringEncodingUTF8));
|
||||||
_font_cache[font->fc->GetSize()].reset(CTFontCreateWithName(font_name.get(), font->fc->GetFontSize(), nullptr));
|
_font_cache[font->fc->GetIndex()].reset(CTFontCreateWithName(font_name.get(), font->fc->GetFontSize(), nullptr));
|
||||||
}
|
}
|
||||||
font_handle = _font_cache[font->fc->GetSize()].get();
|
font_handle = _font_cache[font->fc->GetIndex()].get();
|
||||||
}
|
}
|
||||||
CFAttributedStringSetAttribute(str.get(), CFRangeMake(last, position - last), kCTFontAttributeName, font_handle);
|
CFAttributedStringSetAttribute(str.get(), CFRangeMake(last, position - last), kCTFontAttributeName, font_handle);
|
||||||
|
|
||||||
|
@ -194,10 +189,10 @@ static const CTRunDelegateCallbacks _sprite_font_callback = {
|
||||||
CFAttributedStringSetAttribute(str.get(), CFRangeMake(last, position - last), kCTForegroundColorAttributeName, color);
|
CFAttributedStringSetAttribute(str.get(), CFRangeMake(last, position - last), kCTForegroundColorAttributeName, color);
|
||||||
CGColorRelease(color);
|
CGColorRelease(color);
|
||||||
|
|
||||||
/* Install a size callback for our special private-use sprite glyphs in case the font does not provide them. */
|
/* Install a size callback for our custom fonts. */
|
||||||
for (ssize_t c = last; c < position; c++) {
|
if (font->fc->IsBuiltInFont()) {
|
||||||
if (buff[c] >= SCC_SPRITE_START && buff[c] <= SCC_SPRITE_END && font->fc->MapCharToGlyph(buff[c], false) == 0) {
|
for (ssize_t c = last; c < i.first; c++) {
|
||||||
CFAutoRelease<CTRunDelegateRef> del(CTRunDelegateCreate(&_sprite_font_callback, (void *)(size_t)(buff[c] | (font->fc->GetSize() << 24))));
|
CFAutoRelease<CTRunDelegateRef> del(CTRunDelegateCreate(&_custom_font_callback, static_cast<void *>(reinterpret_cast<uintptr_t>(buff[c] | (font->fc->GetIndex() << 24)))));
|
||||||
/* According to the official documentation, if a run delegate is used, the char should always be 0xFFFC. */
|
/* According to the official documentation, if a run delegate is used, the char should always be 0xFFFC. */
|
||||||
CFAttributedStringReplaceString(str.get(), CFRangeMake(c, 1), replacment_str.get());
|
CFAttributedStringReplaceString(str.get(), CFRangeMake(c, 1), replacment_str.get());
|
||||||
CFAttributedStringSetAttribute(str.get(), CFRangeMake(c, 1), kCTRunDelegateAttributeName, del.get());
|
CFAttributedStringSetAttribute(str.get(), CFRangeMake(c, 1), kCTRunDelegateAttributeName, del.get());
|
||||||
|
@ -247,19 +242,15 @@ CoreTextParagraphLayout::CoreTextVisualRun::CoreTextVisualRun(CTRunRef run, Font
|
||||||
CTRunGetAdvances(run, CFRangeMake(0, 0), advs);
|
CTRunGetAdvances(run, CFRangeMake(0, 0), advs);
|
||||||
this->positions.reserve(this->glyphs.size());
|
this->positions.reserve(this->glyphs.size());
|
||||||
|
|
||||||
|
int y_offset = this->font->fc->GetGlyphYOffset();
|
||||||
|
|
||||||
/* Convert glyph array to our data type. At the same time, substitute
|
/* Convert glyph array to our data type. At the same time, substitute
|
||||||
* the proper glyphs for our private sprite glyphs. */
|
* the proper glyphs for our private sprite glyphs. */
|
||||||
CGGlyph gl[this->glyphs.size()];
|
CGGlyph gl[this->glyphs.size()];
|
||||||
CTRunGetGlyphs(run, CFRangeMake(0, 0), gl);
|
CTRunGetGlyphs(run, CFRangeMake(0, 0), gl);
|
||||||
for (size_t i = 0; i < this->glyphs.size(); i++) {
|
for (size_t i = 0; i < this->glyphs.size(); i++) {
|
||||||
if (buff[this->glyph_to_char[i]] >= SCC_SPRITE_START && buff[this->glyph_to_char[i]] <= SCC_SPRITE_END && (gl[i] == 0 || gl[i] == 3)) {
|
|
||||||
/* A glyph of 0 indidicates not found, while apparently 3 is what char 0xFFFC maps to. */
|
|
||||||
this->glyphs[i] = font->fc->MapCharToGlyph(buff[this->glyph_to_char[i]]);
|
|
||||||
this->positions.emplace_back(pts[i].x, pts[i].x + advs[i].width - 1, (font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(font->fc->GetSize()))) / 2); // Align sprite font to centre
|
|
||||||
} else {
|
|
||||||
this->glyphs[i] = gl[i];
|
this->glyphs[i] = gl[i];
|
||||||
this->positions.emplace_back(pts[i].x, pts[i].x + advs[i].width - 1, pts[i].y);
|
this->positions.emplace_back(pts[i].x, pts[i].x + advs[i].width - 1, pts[i].y + y_offset);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this->total_advance = (int)std::ceil(CTRunGetTypographicBounds(run, CFRangeMake(0, 0), nullptr, nullptr, nullptr));
|
this->total_advance = (int)std::ceil(CTRunGetTypographicBounds(run, CFRangeMake(0, 0), nullptr, nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,10 +120,14 @@ FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (err != FT_Err_Ok) {
|
||||||
|
ShowInfo("Unable to find '{}' font", font_name);
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FontConfigSetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback)
|
bool FontConfigSetFallbackFont(const std::string &language_isocode, FontSizes bad_mask, MissingGlyphSearcher *callback)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
|
@ -132,22 +136,25 @@ bool FontConfigSetFallbackFont(FontCacheSettings *settings, const std::string &l
|
||||||
auto fc_instance = AutoRelease<FcConfig, FcConfigDestroy>(FcConfigReference(nullptr));
|
auto fc_instance = AutoRelease<FcConfig, FcConfigDestroy>(FcConfigReference(nullptr));
|
||||||
assert(fc_instance != nullptr);
|
assert(fc_instance != nullptr);
|
||||||
|
|
||||||
|
/* Get set of required characters. XXX Do we know what font size we want here? */
|
||||||
|
auto chars = callback->GetRequiredGlyphs(bad_mask);
|
||||||
|
|
||||||
/* Fontconfig doesn't handle full language isocodes, only the part
|
/* Fontconfig doesn't handle full language isocodes, only the part
|
||||||
* before the _ of e.g. en_GB is used, so "remove" everything after
|
* before the _ of e.g. en_GB is used, so "remove" everything after
|
||||||
* the _. */
|
* the _. */
|
||||||
std::string lang = fmt::format(":lang={}", language_isocode.substr(0, language_isocode.find('_')));
|
std::string lang = language_isocode.empty() ? "" : fmt::format(":lang={}", language_isocode.substr(0, language_isocode.find('_')));
|
||||||
|
|
||||||
/* First create a pattern to match the wanted language. */
|
/* First create a pattern to match the wanted language. */
|
||||||
auto pat = AutoRelease<FcPattern, FcPatternDestroy>(FcNameParse(ToFcString(lang)));
|
auto pat = AutoRelease<FcPattern, FcPatternDestroy>(FcNameParse(ToFcString(lang)));
|
||||||
/* We only want to know these attributes. */
|
/* We only want to know these attributes. */
|
||||||
auto os = AutoRelease<FcObjectSet, FcObjectSetDestroy>(FcObjectSetBuild(FC_FILE, FC_INDEX, FC_SPACING, FC_SLANT, FC_WEIGHT, nullptr));
|
auto os = AutoRelease<FcObjectSet, FcObjectSetDestroy>(FcObjectSetBuild(FC_FILE, FC_INDEX, FC_SPACING, FC_SLANT, FC_WEIGHT, FC_CHARSET, nullptr));
|
||||||
/* Get the list of filenames matching the wanted language. */
|
/* Get the list of filenames matching the wanted language. */
|
||||||
auto fs = AutoRelease<FcFontSet, FcFontSetDestroy>(FcFontList(nullptr, pat.get(), os.get()));
|
auto fs = AutoRelease<FcFontSet, FcFontSetDestroy>(FcFontList(nullptr, pat.get(), os.get()));
|
||||||
|
|
||||||
if (fs == nullptr) return ret;
|
if (fs == nullptr) return ret;
|
||||||
|
|
||||||
int best_weight = -1;
|
int best_weight = -1;
|
||||||
const char *best_font = nullptr;
|
std::string best_font;
|
||||||
int best_index = 0;
|
int best_index = 0;
|
||||||
|
|
||||||
for (FcPattern *font : std::span(fs->fonts, fs->nfont)) {
|
for (FcPattern *font : std::span(fs->fonts, fs->nfont)) {
|
||||||
|
@ -158,7 +165,7 @@ bool FontConfigSetFallbackFont(FontCacheSettings *settings, const std::string &l
|
||||||
/* Get a font with the right spacing .*/
|
/* Get a font with the right spacing .*/
|
||||||
int value = 0;
|
int value = 0;
|
||||||
FcPatternGetInteger(font, FC_SPACING, 0, &value);
|
FcPatternGetInteger(font, FC_SPACING, 0, &value);
|
||||||
if (callback->Monospace() != (value == FC_MONO) && value != FC_DUAL) continue;
|
if (bad_mask.Test(FS_MONO) != (value == FC_MONO) && value != FC_DUAL) continue;
|
||||||
|
|
||||||
/* Do not use those that explicitly say they're slanted. */
|
/* Do not use those that explicitly say they're slanted. */
|
||||||
FcPatternGetInteger(font, FC_SLANT, 0, &value);
|
FcPatternGetInteger(font, FC_SLANT, 0, &value);
|
||||||
|
@ -168,26 +175,35 @@ bool FontConfigSetFallbackFont(FontCacheSettings *settings, const std::string &l
|
||||||
FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
|
FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
|
||||||
if (value <= best_weight) continue;
|
if (value <= best_weight) continue;
|
||||||
|
|
||||||
|
size_t matching_chars = 0;
|
||||||
|
FcCharSet *charset;
|
||||||
|
FcPatternGetCharSet(font, FC_CHARSET, 0, &charset);
|
||||||
|
for (const char32_t &c : chars) {
|
||||||
|
if (FcCharSetHasChar(charset, c)) ++matching_chars;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matching_chars < chars.size()) {
|
||||||
|
// Debug(fontcache, 0, "Font \"{}\" misses {} glyphs", (char *)file, chars.size() - matching_chars);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Possible match based on attributes, get index. */
|
/* Possible match based on attributes, get index. */
|
||||||
int32_t index;
|
int32_t index;
|
||||||
res = FcPatternGetInteger(font, FC_INDEX, 0, &index);
|
res = FcPatternGetInteger(font, FC_INDEX, 0, &index);
|
||||||
if (res != FcResultMatch) continue;
|
if (res != FcResultMatch) continue;
|
||||||
|
|
||||||
callback->SetFontNames(settings, FromFcString(file), &index);
|
|
||||||
|
|
||||||
bool missing = callback->FindMissingGlyphs();
|
|
||||||
Debug(fontcache, 1, "Font \"{}\" misses{} glyphs", FromFcString(file), missing ? "" : " no");
|
|
||||||
|
|
||||||
if (!missing) {
|
|
||||||
best_weight = value;
|
best_weight = value;
|
||||||
best_font = FromFcString(file);
|
best_font = FromFcString(file);
|
||||||
best_index = index;
|
best_index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (best_font.empty()) return false;
|
||||||
|
|
||||||
|
for (FontSize fs : bad_mask) {
|
||||||
|
GetFontCacheSubSetting(fs)->AddFallback(best_font, best_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (best_font == nullptr) return false;
|
InitFontCache(bad_mask);
|
||||||
|
|
||||||
callback->SetFontNames(settings, best_font, &best_index);
|
|
||||||
InitFontCache(callback->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face);
|
FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face);
|
||||||
|
|
||||||
bool FontConfigSetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback);
|
bool FontConfigSetFallbackFont(const std::string &language_isocode, FontSizes bad_mask, MissingGlyphSearcher *callback);
|
||||||
|
|
||||||
#endif /* WITH_FONTCONFIG */
|
#endif /* WITH_FONTCONFIG */
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,8 @@
|
||||||
#include "../../safeguards.h"
|
#include "../../safeguards.h"
|
||||||
|
|
||||||
struct EFCParam {
|
struct EFCParam {
|
||||||
FontCacheSettings *settings;
|
|
||||||
LOCALESIGNATURE locale;
|
LOCALESIGNATURE locale;
|
||||||
|
FontSizes fontsizes;
|
||||||
MissingGlyphSearcher *callback;
|
MissingGlyphSearcher *callback;
|
||||||
std::vector<std::wstring> fonts;
|
std::vector<std::wstring> fonts;
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXT
|
||||||
/* Don't use SYMBOL fonts */
|
/* Don't use SYMBOL fonts */
|
||||||
if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET) return 1;
|
if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET) return 1;
|
||||||
/* Use monospaced fonts when asked for it. */
|
/* Use monospaced fonts when asked for it. */
|
||||||
if (info->callback->Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH)) return 1;
|
if (info->fontsizes.Test(FS_MONO) && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH)) return 1;
|
||||||
|
|
||||||
/* The font has to have at least one of the supported locales to be usable. */
|
/* The font has to have at least one of the supported locales to be usable. */
|
||||||
auto check_bitfields = [&]() {
|
auto check_bitfields = [&]() {
|
||||||
|
@ -78,8 +78,15 @@ static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXT
|
||||||
char font_name[MAX_PATH];
|
char font_name[MAX_PATH];
|
||||||
convert_from_fs(logfont->elfFullName, font_name);
|
convert_from_fs(logfont->elfFullName, font_name);
|
||||||
|
|
||||||
info->callback->SetFontNames(info->settings, font_name, &logfont->elfLogFont);
|
Debug(misc, 0, "Trying font {}", font_name);
|
||||||
if (info->callback->FindMissingGlyphs()) return 1;
|
for (FontSize fs : info->fontsizes) {
|
||||||
|
GetFontCacheSubSetting(fs)->AddFallback(font_name, logfont->elfLogFont);
|
||||||
|
}
|
||||||
|
|
||||||
|
InitFontCache(info->fontsizes);
|
||||||
|
if (info->callback->FindMissingGlyphs().None()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
Debug(fontcache, 1, "Fallback font: {}", font_name);
|
Debug(fontcache, 1, "Fallback font: {}", font_name);
|
||||||
return 0; // stop enumerating
|
return 0; // stop enumerating
|
||||||
}
|
}
|
||||||
|
@ -156,10 +163,11 @@ void Win32FontCache::SetFontSize(int pixels)
|
||||||
this->height = this->ascender + this->descender;
|
this->height = this->ascender + this->descender;
|
||||||
this->glyph_size.cx = otm->otmTextMetrics.tmMaxCharWidth;
|
this->glyph_size.cx = otm->otmTextMetrics.tmMaxCharWidth;
|
||||||
this->glyph_size.cy = otm->otmTextMetrics.tmHeight;
|
this->glyph_size.cy = otm->otmTextMetrics.tmHeight;
|
||||||
|
FontCache::UpdateCharacterHeight(this->fs);
|
||||||
|
|
||||||
this->fontname = FS2OTTD((LPWSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFaceName));
|
this->fontname = FS2OTTD((LPWSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFaceName));
|
||||||
|
|
||||||
Debug(fontcache, 2, "Loaded font '{}' with size {}", this->fontname, pixels);
|
Debug(fontcache, 2, "Win32FontCache: Loaded font '{}' with size {}", this->fontname, pixels);
|
||||||
delete[] (BYTE*)otm;
|
delete[] (BYTE*)otm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +254,7 @@ void Win32FontCache::ClearFontCache()
|
||||||
return this->SetGlyphPtr(key, std::move(new_glyph)).GetSprite();
|
return this->SetGlyphPtr(key, std::move(new_glyph)).GetSprite();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* virtual */ GlyphID Win32FontCache::MapCharToGlyph(char32_t key, bool allow_fallback)
|
/* virtual */ GlyphID Win32FontCache::MapCharToGlyph(char32_t key)
|
||||||
{
|
{
|
||||||
assert(IsPrintable(key));
|
assert(IsPrintable(key));
|
||||||
|
|
||||||
|
@ -263,7 +271,7 @@ void Win32FontCache::ClearFontCache()
|
||||||
GetGlyphIndicesW(this->dc, chars, key >= 0x010000U ? 2 : 1, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
|
GetGlyphIndicesW(this->dc, chars, key >= 0x010000U ? 2 : 1, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
|
||||||
|
|
||||||
if (glyphs[0] != 0xFFFF) return glyphs[0];
|
if (glyphs[0] != 0xFFFF) return glyphs[0];
|
||||||
return allow_fallback && key >= SCC_SPRITE_START && key <= SCC_SPRITE_END ? this->parent->MapCharToGlyph(key) : 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Win32FontCacheFactory : FontCacheFactory {
|
class Win32FontCacheFactory : FontCacheFactory {
|
||||||
|
@ -276,27 +284,23 @@ public:
|
||||||
* fallback search, use it. Otherwise, try to resolve it by font name.
|
* fallback search, use it. Otherwise, try to resolve it by font name.
|
||||||
* @param fs The font size to load.
|
* @param fs The font size to load.
|
||||||
*/
|
*/
|
||||||
void LoadFont(FontSize fs, FontType fonttype) override
|
void LoadFont(FontSize fs, FontType fonttype, bool search, const std::string &font, std::span<const std::byte> os_handle) override
|
||||||
{
|
{
|
||||||
if (fonttype != FontType::TrueType) return;
|
if (fonttype != FontType::TrueType) return;
|
||||||
|
|
||||||
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
|
|
||||||
|
|
||||||
std::string font = GetFontCacheFontName(fs);
|
|
||||||
if (font.empty()) return;
|
|
||||||
|
|
||||||
LOGFONT logfont{};
|
LOGFONT logfont{};
|
||||||
logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
|
logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
|
||||||
logfont.lfCharSet = DEFAULT_CHARSET;
|
logfont.lfCharSet = DEFAULT_CHARSET;
|
||||||
logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
|
logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
|
||||||
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||||||
|
|
||||||
if (settings->os_handle != nullptr) {
|
if (!os_handle.empty()) {
|
||||||
logfont = *(const LOGFONT *)settings->os_handle;
|
logfont = *reinterpret_cast<const LOGFONT *>(os_handle.data());
|
||||||
} else if (font.find('.') != std::string::npos) {
|
} else if (font.find('.') != std::string::npos) {
|
||||||
/* Might be a font file name, try load it. */
|
/* Might be a font file name, try load it. */
|
||||||
if (!TryLoadFontFromFile(font, logfont)) {
|
if (!TryLoadFontFromFile(font, logfont)) {
|
||||||
ShowInfo("Unable to load file '{}' for {} font, using default windows font selection instead", font, FontSizeToName(fs));
|
ShowInfo("Unable to load file '{}' for {} font, using default windows font selection instead", font, FontSizeToName(fs));
|
||||||
|
if (!search) return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,7 +312,7 @@ public:
|
||||||
LoadWin32Font(fs, logfont, GetFontCacheFontSize(fs), font);
|
LoadWin32Font(fs, logfont, GetFontCacheFontSize(fs), font);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback) override
|
bool SetFallbackFont(const std::string &language_isocode, FontSizes bad_mask, MissingGlyphSearcher *callback) override
|
||||||
{
|
{
|
||||||
Debug(fontcache, 1, "Trying fallback fonts");
|
Debug(fontcache, 1, "Trying fallback fonts");
|
||||||
EFCParam langInfo;
|
EFCParam langInfo;
|
||||||
|
@ -318,7 +322,7 @@ public:
|
||||||
Debug(fontcache, 1, "Can't get locale info for fallback font (isocode={})", language_isocode);
|
Debug(fontcache, 1, "Can't get locale info for fallback font (isocode={})", language_isocode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
langInfo.settings = settings;
|
langInfo.fontsizes = bad_mask;
|
||||||
langInfo.callback = callback;
|
langInfo.callback = callback;
|
||||||
|
|
||||||
LOGFONT font;
|
LOGFONT font;
|
||||||
|
|
|
@ -37,11 +37,11 @@ public:
|
||||||
Win32FontCache(FontSize fs, const LOGFONT &logfont, int pixels);
|
Win32FontCache(FontSize fs, const LOGFONT &logfont, int pixels);
|
||||||
~Win32FontCache();
|
~Win32FontCache();
|
||||||
void ClearFontCache() override;
|
void ClearFontCache() override;
|
||||||
GlyphID MapCharToGlyph(char32_t key, bool allow_fallback = true) override;
|
GlyphID MapCharToGlyph(char32_t key) override;
|
||||||
std::string GetFontName() override { return this->fontname; }
|
std::string GetFontName() override { return this->fontname; }
|
||||||
const void *GetOSHandle() override { return &this->logfont; }
|
const void *GetOSHandle() override { return &this->logfont; }
|
||||||
};
|
};
|
||||||
|
|
||||||
void LoadWin32Font(FontSize fs);
|
void LoadWin32Font(FontSize fs, bool search, const std::string &font_name, std::span<const std::byte> os_handle);
|
||||||
|
|
||||||
#endif /* FONT_WIN32_H */
|
#endif /* FONT_WIN32_H */
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "../../stdafx.h"
|
#include "../../stdafx.h"
|
||||||
#include "../../debug.h"
|
#include "../../debug.h"
|
||||||
#include "string_uniscribe.h"
|
#include "string_uniscribe.h"
|
||||||
|
#include "../../gfx_layout_fallback.h"
|
||||||
#include "../../language.h"
|
#include "../../language.h"
|
||||||
#include "../../strings_func.h"
|
#include "../../strings_func.h"
|
||||||
#include "../../string_func.h"
|
#include "../../string_func.h"
|
||||||
|
@ -29,7 +30,7 @@
|
||||||
|
|
||||||
|
|
||||||
/** Uniscribe cache for internal font information, cleared when OTTD changes fonts. */
|
/** Uniscribe cache for internal font information, cleared when OTTD changes fonts. */
|
||||||
static SCRIPT_CACHE _script_cache[FS_END];
|
static std::map<FontIndex, SCRIPT_CACHE> _script_cache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all information about a run of characters. A run are consecutive
|
* Contains all information about a run of characters. A run are consecutive
|
||||||
|
@ -52,6 +53,8 @@ struct UniscribeRun {
|
||||||
int total_advance;
|
int total_advance;
|
||||||
|
|
||||||
UniscribeRun(int pos, int len, Font *font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {}
|
UniscribeRun(int pos, int len, Font *font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {}
|
||||||
|
|
||||||
|
void FallbackShape(const UniscribeParagraphLayoutFactory::CharType *buff);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Break a string into language formatting ranges. */
|
/** Break a string into language formatting ranges. */
|
||||||
|
@ -94,7 +97,7 @@ public:
|
||||||
std::span<const int> GetGlyphToCharMap() const override;
|
std::span<const int> GetGlyphToCharMap() const override;
|
||||||
|
|
||||||
const Font *GetFont() const override { return this->font; }
|
const Font *GetFont() const override { return this->font; }
|
||||||
int GetLeading() const override { return this->font->fc->GetHeight(); }
|
int GetLeading() const override { return GetCharacterHeight(this->font->fc->GetSize()); }
|
||||||
int GetGlyphCount() const override { return this->num_glyphs; }
|
int GetGlyphCount() const override { return this->num_glyphs; }
|
||||||
int GetAdvance() const { return this->total_advance; }
|
int GetAdvance() const { return this->total_advance; }
|
||||||
};
|
};
|
||||||
|
@ -130,12 +133,12 @@ public:
|
||||||
std::unique_ptr<const Line> NextLine(int max_width) override;
|
std::unique_ptr<const Line> NextLine(int max_width) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
void UniscribeResetScriptCache(FontSize size)
|
void UniscribeResetScriptCache(FontSize)
|
||||||
{
|
{
|
||||||
if (_script_cache[size] != nullptr) {
|
for (auto &sc : _script_cache) {
|
||||||
ScriptFreeCache(&_script_cache[size]);
|
ScriptFreeCache(&sc.second);
|
||||||
_script_cache[size] = nullptr;
|
|
||||||
}
|
}
|
||||||
|
_script_cache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load the matching native Windows font. */
|
/** Load the matching native Windows font. */
|
||||||
|
@ -152,6 +155,41 @@ static HFONT HFontFromFont(Font *font)
|
||||||
return CreateFontIndirect(&logfont);
|
return CreateFontIndirect(&logfont);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manually shape a run for built-in non-truetype fonts.
|
||||||
|
* Similar to but not quite the same as \a ICURun::FallbackShape.
|
||||||
|
* @param buff The complete buffer of the run.
|
||||||
|
*/
|
||||||
|
void UniscribeRun::FallbackShape(const UniscribeParagraphLayoutFactory::CharType *buff)
|
||||||
|
{
|
||||||
|
this->glyphs.reserve(this->len);
|
||||||
|
|
||||||
|
/* Read each UTF-16 character, mapping to an appropriate glyph. */
|
||||||
|
for (int i = this->pos; i < this->pos + this->len; ++i) {
|
||||||
|
char32_t c = Utf16DecodeChar(reinterpret_cast<const char16_t *>(buff + i));
|
||||||
|
if (this->sa.fRTL) c = SwapRtlPairedCharacters(c);
|
||||||
|
this->glyphs.emplace_back(this->font->fc->MapCharToGlyph(c));
|
||||||
|
if (Utf16IsLeadSurrogate(*(buff + i))) ++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reverse the sequence if this run is RTL. */
|
||||||
|
if (this->sa.fRTL) {
|
||||||
|
std::reverse(std::begin(this->glyphs), std::end(this->glyphs));
|
||||||
|
}
|
||||||
|
|
||||||
|
this->offsets.reserve(this->glyphs.size());
|
||||||
|
|
||||||
|
/* Set positions of each glyph. */
|
||||||
|
int y_offset = this->font->fc->GetGlyphYOffset();
|
||||||
|
int advance = 0;
|
||||||
|
for (const GlyphID glyph : this->glyphs) {
|
||||||
|
this->offsets.emplace_back(advance, y_offset);
|
||||||
|
int x_advance = this->font->fc->GetGlyphWidth(glyph);
|
||||||
|
this->advances.push_back(x_advance);
|
||||||
|
advance += x_advance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Determine the glyph positions for a run. */
|
/** Determine the glyph positions for a run. */
|
||||||
static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range)
|
static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range)
|
||||||
{
|
{
|
||||||
|
@ -166,10 +204,15 @@ static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *b
|
||||||
HFONT old_font = nullptr;
|
HFONT old_font = nullptr;
|
||||||
HFONT cur_font = nullptr;
|
HFONT cur_font = nullptr;
|
||||||
|
|
||||||
|
if (range.font->fc->IsBuiltInFont()) {
|
||||||
|
range.FallbackShape(buff);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
/* Shape the text run by determining the glyphs needed for display. */
|
/* Shape the text run by determining the glyphs needed for display. */
|
||||||
int glyphs_used = 0;
|
int glyphs_used = 0;
|
||||||
HRESULT hr = ScriptShape(temp_dc, &_script_cache[range.font->fc->GetSize()], buff + range.pos, range.len, (int)range.glyphs.size(), &range.sa, &range.glyphs[0], &range.char_to_glyph[0], &range.vis_attribs[0], &glyphs_used);
|
HRESULT hr = ScriptShape(temp_dc, &_script_cache[range.font->fc->GetIndex()], buff + range.pos, range.len, (int)range.glyphs.size(), &range.sa, &range.glyphs[0], &range.char_to_glyph[0], &range.vis_attribs[0], &glyphs_used);
|
||||||
|
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
range.glyphs.resize(glyphs_used);
|
range.glyphs.resize(glyphs_used);
|
||||||
|
@ -179,7 +222,7 @@ static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *b
|
||||||
ABC abc;
|
ABC abc;
|
||||||
range.advances.resize(range.glyphs.size());
|
range.advances.resize(range.glyphs.size());
|
||||||
range.offsets.resize(range.glyphs.size());
|
range.offsets.resize(range.glyphs.size());
|
||||||
hr = ScriptPlace(temp_dc, &_script_cache[range.font->fc->GetSize()], &range.glyphs[0], (int)range.glyphs.size(), &range.vis_attribs[0], &range.sa, &range.advances[0], &range.offsets[0], &abc);
|
hr = ScriptPlace(temp_dc, &_script_cache[range.font->fc->GetIndex()], &range.glyphs[0], (int)range.glyphs.size(), &range.vis_attribs[0], &range.sa, &range.advances[0], &range.offsets[0], &abc);
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
/* We map our special sprite chars to values that don't fit into a WORD. Copy the glyphs
|
/* We map our special sprite chars to values that don't fit into a WORD. Copy the glyphs
|
||||||
* into a new vector and query the real glyph to use for these special chars. */
|
* into a new vector and query the real glyph to use for these special chars. */
|
||||||
|
@ -187,22 +230,12 @@ static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *b
|
||||||
for (size_t g_id = 0; g_id < range.glyphs.size(); g_id++) {
|
for (size_t g_id = 0; g_id < range.glyphs.size(); g_id++) {
|
||||||
range.ft_glyphs[g_id] = range.glyphs[g_id];
|
range.ft_glyphs[g_id] = range.glyphs[g_id];
|
||||||
}
|
}
|
||||||
for (int i = 0; i < range.len; i++) {
|
|
||||||
if (buff[range.pos + i] >= SCC_SPRITE_START && buff[range.pos + i] <= SCC_SPRITE_END) {
|
|
||||||
auto pos = range.char_to_glyph[i];
|
|
||||||
if (range.ft_glyphs[pos] == 0) { // Font doesn't have our special glyph, so remap.
|
|
||||||
range.ft_glyphs[pos] = range.font->fc->MapCharToGlyph(buff[range.pos + i]);
|
|
||||||
range.offsets[pos].dv = (range.font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(range.font->fc->GetSize()))) / 2; // Align sprite font to centre
|
|
||||||
range.advances[pos] = range.font->fc->GetGlyphWidth(range.ft_glyphs[pos]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
range.total_advance = 0;
|
range.total_advance = 0;
|
||||||
for (size_t i = 0; i < range.advances.size(); i++) {
|
for (size_t i = 0; i < range.advances.size(); i++) {
|
||||||
#ifdef WITH_FREETYPE
|
#ifdef WITH_FREETYPE
|
||||||
/* FreeType and GDI/Uniscribe seems to occasionally disagree over the width of a glyph. */
|
/* FreeType and GDI/Uniscribe seems to occasionally disagree over the width of a glyph. */
|
||||||
if (range.advances[i] > 0 && range.ft_glyphs[i] != 0xFFFF) range.advances[i] = range.font->fc->GetGlyphWidth(range.ft_glyphs[i]);
|
if (range.advances[i] > 0 && range.glyphs[i] != 0xFFFF) range.advances[i] = range.font->fc->GetGlyphWidth(range.glyphs[i]);
|
||||||
#endif
|
#endif
|
||||||
range.total_advance += range.advances[i];
|
range.total_advance += range.advances[i];
|
||||||
}
|
}
|
||||||
|
@ -280,11 +313,6 @@ static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutF
|
||||||
/* Can't layout an empty string. */
|
/* Can't layout an empty string. */
|
||||||
if (length == 0) return nullptr;
|
if (length == 0) return nullptr;
|
||||||
|
|
||||||
/* Can't layout our in-built sprite fonts. */
|
|
||||||
for (auto const &[position, font] : font_mapping) {
|
|
||||||
if (font->fc->IsBuiltInFont()) return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Itemize text. */
|
/* Itemize text. */
|
||||||
std::vector<SCRIPT_ITEM> items = UniscribeItemizeString(buff, length);
|
std::vector<SCRIPT_ITEM> items = UniscribeItemizeString(buff, length);
|
||||||
if (items.empty()) return nullptr;
|
if (items.empty()) return nullptr;
|
||||||
|
|
|
@ -697,7 +697,7 @@ public:
|
||||||
* break point, but we only want word starts. Move to the next location in
|
* break point, but we only want word starts. Move to the next location in
|
||||||
* case the new position points to whitespace. */
|
* case the new position points to whitespace. */
|
||||||
while (pos != icu::BreakIterator::DONE &&
|
while (pos != icu::BreakIterator::DONE &&
|
||||||
IsWhitespace(Utf16DecodeChar((const uint16_t *)&this->utf16_str[pos]))) {
|
IsWhitespace(Utf16DecodeChar(&this->utf16_str[pos]))) {
|
||||||
int32_t new_pos = this->word_itr->next();
|
int32_t new_pos = this->word_itr->next();
|
||||||
/* Don't set it to DONE if it was valid before. Otherwise we'll return END
|
/* Don't set it to DONE if it was valid before. Otherwise we'll return END
|
||||||
* even though the iterator wasn't at the end of the string before. */
|
* even though the iterator wasn't at the end of the string before. */
|
||||||
|
@ -729,7 +729,7 @@ public:
|
||||||
* break point, but we only want word starts. Move to the previous location in
|
* break point, but we only want word starts. Move to the previous location in
|
||||||
* case the new position points to whitespace. */
|
* case the new position points to whitespace. */
|
||||||
while (pos != icu::BreakIterator::DONE &&
|
while (pos != icu::BreakIterator::DONE &&
|
||||||
IsWhitespace(Utf16DecodeChar((const uint16_t *)&this->utf16_str[pos]))) {
|
IsWhitespace(Utf16DecodeChar(&this->utf16_str[pos]))) {
|
||||||
int32_t new_pos = this->word_itr->previous();
|
int32_t new_pos = this->word_itr->previous();
|
||||||
/* Don't set it to DONE if it was valid before. Otherwise we'll return END
|
/* Don't set it to DONE if it was valid before. Otherwise we'll return END
|
||||||
* even though the iterator wasn't at the start of the string before. */
|
* even though the iterator wasn't at the start of the string before. */
|
||||||
|
|
|
@ -94,7 +94,7 @@ inline char32_t Utf16DecodeSurrogate(uint lead, uint trail)
|
||||||
* @param c Pointer to one or two UTF-16 code points.
|
* @param c Pointer to one or two UTF-16 code points.
|
||||||
* @return Decoded Unicode character.
|
* @return Decoded Unicode character.
|
||||||
*/
|
*/
|
||||||
inline char32_t Utf16DecodeChar(const uint16_t *c)
|
inline char32_t Utf16DecodeChar(const char16_t *c)
|
||||||
{
|
{
|
||||||
if (Utf16IsLeadSurrogate(c[0])) {
|
if (Utf16IsLeadSurrogate(c[0])) {
|
||||||
return Utf16DecodeSurrogate(c[0], c[1]);
|
return Utf16DecodeSurrogate(c[0], c[1]);
|
||||||
|
|
102
src/strings.cpp
102
src/strings.cpp
|
@ -2252,42 +2252,60 @@ std::string_view GetCurrentLanguageIsoCode()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether there are glyphs missing in the current language.
|
* Check whether there are glyphs missing in the current language.
|
||||||
* @return If glyphs are missing, return \c true, else return \c false.
|
* @return Bit mask of font sizes have any missing glyphs.
|
||||||
*/
|
*/
|
||||||
bool MissingGlyphSearcher::FindMissingGlyphs()
|
FontSizes MissingGlyphSearcher::FindMissingGlyphs()
|
||||||
{
|
{
|
||||||
InitFontCache(this->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
for (FontSize size : this->font_sizes) {
|
||||||
|
GetFontCacheSubSetting(size)->fallback_fonts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
InitFontCache(this->font_sizes);
|
||||||
|
|
||||||
|
FontSizes bad_fontsizes{};
|
||||||
|
|
||||||
|
for (FontSize size : this->font_sizes) {
|
||||||
|
auto set = this->GetRequiredGlyphs(size);
|
||||||
|
if (set.empty()) continue;
|
||||||
|
|
||||||
|
Debug(fontcache, 0, "Missing {} glyphs in {} font size", set.size(), FontSizeToName(size));
|
||||||
|
bad_fontsizes.Set(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bad_fontsizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<char32_t> BaseStringMissingGlyphSearcher::GetRequiredGlyphs(FontSizes fontsizes)
|
||||||
|
{
|
||||||
|
std::set<char32_t> glyphs{};
|
||||||
|
|
||||||
this->Reset();
|
this->Reset();
|
||||||
for (auto text = this->NextString(); text.has_value(); text = this->NextString()) {
|
for (auto text = this->NextString(); text.has_value(); text = this->NextString()) {
|
||||||
FontSize size = this->DefaultSize();
|
FontSize size = this->DefaultSize();
|
||||||
FontCache *fc = FontCache::Get(size);
|
|
||||||
for (char32_t c : Utf8View(*text)) {
|
for (char32_t c : Utf8View(*text)) {
|
||||||
if (c >= SCC_FIRST_FONT && c <= SCC_LAST_FONT) {
|
if (c >= SCC_FIRST_FONT && c <= SCC_LAST_FONT) {
|
||||||
size = (FontSize)(c - SCC_FIRST_FONT);
|
size = (FontSize)(c - SCC_FIRST_FONT);
|
||||||
fc = FontCache::Get(size);
|
continue;
|
||||||
} else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && fc->MapCharToGlyph(c, false) == 0) {
|
|
||||||
/* The character is printable, but not in the normal font. This is the case we were testing for. */
|
|
||||||
std::string size_name;
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
case FS_NORMAL: size_name = "medium"; break;
|
|
||||||
case FS_SMALL: size_name = "small"; break;
|
|
||||||
case FS_LARGE: size_name = "large"; break;
|
|
||||||
case FS_MONO: size_name = "mono"; break;
|
|
||||||
default: NOT_REACHED();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug(fontcache, 0, "Font is missing glyphs to display char 0x{:X} in {} font size", (int)c, size_name);
|
if (IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END)) continue;
|
||||||
return true;
|
if (!IsPrintable(c) || IsTextDirectionChar(c)) continue;
|
||||||
|
if (fontsizes.Test(size)) continue;
|
||||||
|
if (FontCache::GetFontIndexForCharacter(size, c) != INVALID_FONT_INDEX) continue;
|
||||||
|
|
||||||
|
glyphs.insert(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
return glyphs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Helper for searching through the language pack. */
|
/** Helper for searching through the language pack. */
|
||||||
class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
|
class LanguagePackGlyphSearcher : public BaseStringMissingGlyphSearcher {
|
||||||
|
public:
|
||||||
|
LanguagePackGlyphSearcher() : BaseStringMissingGlyphSearcher({FS_NORMAL, FS_SMALL, FS_LARGE}) {}
|
||||||
|
|
||||||
|
private:
|
||||||
uint i; ///< Iterator for the primary language tables.
|
uint i; ///< Iterator for the primary language tables.
|
||||||
uint j; ///< Iterator for the secondary language tables.
|
uint j; ///< Iterator for the secondary language tables.
|
||||||
|
|
||||||
|
@ -2316,26 +2334,11 @@ class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Monospace() override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetFontNames([[maybe_unused]] FontCacheSettings *settings, [[maybe_unused]] std::string_view font_name, [[maybe_unused]] const void *os_data) override
|
|
||||||
{
|
|
||||||
#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
|
|
||||||
settings->small.font = font_name;
|
|
||||||
settings->medium.font = font_name;
|
|
||||||
settings->large.font = font_name;
|
|
||||||
|
|
||||||
settings->small.os_handle = os_data;
|
|
||||||
settings->medium.os_handle = os_data;
|
|
||||||
settings->large.os_handle = os_data;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static LanguagePackGlyphSearcher _language_pack_searcher;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the currently loaded language pack
|
* Check whether the currently loaded language pack
|
||||||
* uses characters that the currently loaded font
|
* uses characters that the currently loaded font
|
||||||
|
@ -2351,22 +2354,17 @@ class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
|
||||||
*/
|
*/
|
||||||
void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
|
void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
|
||||||
{
|
{
|
||||||
static LanguagePackGlyphSearcher pack_searcher;
|
if (searcher == nullptr) searcher = &_language_pack_searcher;
|
||||||
if (searcher == nullptr) searcher = &pack_searcher;
|
FontSizes bad_mask = searcher->FindMissingGlyphs();
|
||||||
bool bad_font = !base_font || searcher->FindMissingGlyphs();
|
bool bad_font = bad_mask.Any();
|
||||||
|
|
||||||
#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
|
#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
|
||||||
if (bad_font) {
|
if (bad_mask.Any()) {
|
||||||
/* We found an unprintable character... lets try whether we can find
|
/* We found an unprintable character... lets try whether we can find
|
||||||
* a fallback font that can print the characters in the current language. */
|
* a fallback font that can print the characters in the current language. */
|
||||||
bool any_font_configured = !_fcsettings.medium.font.empty();
|
bool any_font_configured = !_fcsettings.medium.font.empty();
|
||||||
FontCacheSettings backup = _fcsettings;
|
|
||||||
|
|
||||||
_fcsettings.mono.os_handle = nullptr;
|
bad_font = !FontProviderManager::SetFallbackFont(_langpack.langpack->isocode, bad_mask, searcher);
|
||||||
_fcsettings.medium.os_handle = nullptr;
|
|
||||||
|
|
||||||
bad_font = !FontProviderManager::SetFallbackFont(&_fcsettings, _langpack.langpack->isocode, searcher);
|
|
||||||
|
|
||||||
_fcsettings = std::move(backup);
|
|
||||||
|
|
||||||
if (!bad_font && any_font_configured) {
|
if (!bad_font && any_font_configured) {
|
||||||
/* If the user configured a bad font, and we found a better one,
|
/* If the user configured a bad font, and we found a better one,
|
||||||
|
@ -2383,7 +2381,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
|
||||||
/* Our fallback font does miss characters too, so keep the
|
/* Our fallback font does miss characters too, so keep the
|
||||||
* user chosen font as that is more likely to be any good than
|
* user chosen font as that is more likely to be any good than
|
||||||
* the wild guess we made */
|
* the wild guess we made */
|
||||||
InitFontCache(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
InitFontCache(bad_mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -2400,12 +2398,12 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
|
||||||
ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_WARNING);
|
ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_WARNING);
|
||||||
|
|
||||||
/* Reset the font width */
|
/* Reset the font width */
|
||||||
LoadStringWidthTable(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
LoadStringWidthTable(bad_mask);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update the font with cache */
|
/* Update the font with cache */
|
||||||
LoadStringWidthTable(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
LoadStringWidthTable(searcher->font_sizes);
|
||||||
|
|
||||||
#if !(defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ)) && !defined(WITH_UNISCRIBE) && !defined(WITH_COCOA)
|
#if !(defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ)) && !defined(WITH_UNISCRIBE) && !defined(WITH_COCOA)
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -157,9 +157,31 @@ EncodedString GetEncodedString(StringID string, const Args&... args)
|
||||||
*/
|
*/
|
||||||
class MissingGlyphSearcher {
|
class MissingGlyphSearcher {
|
||||||
public:
|
public:
|
||||||
|
FontSizes font_sizes; ///< Font sizes to search for.
|
||||||
|
|
||||||
|
MissingGlyphSearcher(FontSizes font_sizes) : font_sizes(font_sizes) {}
|
||||||
|
|
||||||
/** Make sure everything gets destructed right. */
|
/** Make sure everything gets destructed right. */
|
||||||
virtual ~MissingGlyphSearcher() = default;
|
virtual ~MissingGlyphSearcher() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if any glyphs are missing.
|
||||||
|
* @return Font sizes which have missing glyphs.
|
||||||
|
*/
|
||||||
|
FontSizes FindMissingGlyphs();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get set of glyphs required for the current language.
|
||||||
|
* @param fontsizes Font sizes to test.
|
||||||
|
* @return Set of required glyphs.
|
||||||
|
**/
|
||||||
|
virtual std::set<char32_t> GetRequiredGlyphs(FontSizes fontsizes) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BaseStringMissingGlyphSearcher : public MissingGlyphSearcher {
|
||||||
|
public:
|
||||||
|
BaseStringMissingGlyphSearcher(FontSizes font_sizes) : MissingGlyphSearcher(font_sizes) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next string to search through.
|
* Get the next string to search through.
|
||||||
* @return The next string or nullopt if there is none.
|
* @return The next string or nullopt if there is none.
|
||||||
|
@ -177,23 +199,9 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void Reset() = 0;
|
virtual void Reset() = 0;
|
||||||
|
|
||||||
/**
|
std::set<char32_t> GetRequiredGlyphs(FontSizes fontsizes) override;
|
||||||
* Whether to search for a monospace font or not.
|
|
||||||
* @return True if searching for monospace.
|
|
||||||
*/
|
|
||||||
virtual bool Monospace() = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the right font names.
|
|
||||||
* @param settings The settings to modify.
|
|
||||||
* @param font_name The new font name.
|
|
||||||
* @param os_data Opaque pointer to OS-specific data.
|
|
||||||
*/
|
|
||||||
virtual void SetFontNames(struct FontCacheSettings *settings, std::string_view font_name, const void *os_data = nullptr) = 0;
|
|
||||||
|
|
||||||
bool FindMissingGlyphs();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void CheckForMissingGlyphs(bool base_font = true, MissingGlyphSearcher *search = nullptr);
|
void CheckForMissingGlyphs(bool base_font = true, MissingGlyphSearcher *searcher = nullptr);
|
||||||
|
|
||||||
#endif /* STRINGS_FUNC_H */
|
#endif /* STRINGS_FUNC_H */
|
||||||
|
|
|
@ -298,10 +298,16 @@ void SurveyConfiguration(nlohmann::json &survey)
|
||||||
*/
|
*/
|
||||||
void SurveyFont(nlohmann::json &survey)
|
void SurveyFont(nlohmann::json &survey)
|
||||||
{
|
{
|
||||||
survey["small"] = FontCache::Get(FS_SMALL)->GetFontName();
|
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
||||||
survey["medium"] = FontCache::Get(FS_NORMAL)->GetFontName();
|
const FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
|
||||||
survey["large"] = FontCache::Get(FS_LARGE)->GetFontName();
|
auto &font = survey[std::string(FontSizeToName(fs))];
|
||||||
survey["mono"] = FontCache::Get(FS_MONO)->GetFontName();
|
font["configured"]["font"] = setting->font;
|
||||||
|
font["configured"]["size"] = setting->size;
|
||||||
|
}
|
||||||
|
for (const auto &fc : FontCache::Get()) {
|
||||||
|
auto &font = survey[std::string(FontSizeToName(fc->GetSize()))];
|
||||||
|
font["active"].push_back(fc->GetFontName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,20 +19,22 @@ public:
|
||||||
MockFontCache(FontSize fs) : FontCache(fs)
|
MockFontCache(FontSize fs) : FontCache(fs)
|
||||||
{
|
{
|
||||||
this->height = FontCache::GetDefaultFontHeight(this->fs);
|
this->height = FontCache::GetDefaultFontHeight(this->fs);
|
||||||
|
FontCache::UpdateCharacterHeight(this->fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearFontCache() override {}
|
void ClearFontCache() override {}
|
||||||
const Sprite *GetGlyph(GlyphID) override { return nullptr; }
|
const Sprite *GetGlyph(GlyphID) override { return nullptr; }
|
||||||
uint GetGlyphWidth(GlyphID) override { return this->height / 2; }
|
uint GetGlyphWidth(GlyphID) override { return this->height / 2; }
|
||||||
bool GetDrawGlyphShadow() override { return false; }
|
bool GetDrawGlyphShadow() override { return false; }
|
||||||
GlyphID MapCharToGlyph(char32_t key, [[maybe_unused]] bool allow_fallback = true) override { return key; }
|
GlyphID MapCharToGlyph(char32_t key) override { return key; }
|
||||||
std::string GetFontName() override { return "mock"; }
|
std::string GetFontName() override { return "mock"; }
|
||||||
bool IsBuiltInFont() override { return true; }
|
bool IsBuiltInFont() override { return true; }
|
||||||
|
|
||||||
static void InitializeFontCaches()
|
static void InitializeFontCaches()
|
||||||
{
|
{
|
||||||
|
FontCache::caches.clear();
|
||||||
for (FontSize fs = FS_BEGIN; fs != FS_END; fs++) {
|
for (FontSize fs = FS_BEGIN; fs != FS_END; fs++) {
|
||||||
if (FontCache::caches[fs] == nullptr) new MockFontCache(fs); /* FontCache inserts itself into to the cache. */
|
new MockFontCache(fs); /* FontCache inserts itself into to the cache. */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -83,7 +83,7 @@ static WindowDesc _textfile_desc(
|
||||||
_nested_textfile_widgets
|
_nested_textfile_widgets
|
||||||
);
|
);
|
||||||
|
|
||||||
TextfileWindow::TextfileWindow(Window *parent, TextfileType file_type) : Window(_textfile_desc), file_type(file_type)
|
TextfileWindow::TextfileWindow(Window *parent, TextfileType file_type) : Window(_textfile_desc), BaseStringMissingGlyphSearcher(FS_MONO), file_type(file_type)
|
||||||
{
|
{
|
||||||
/* Init of nested tree is deferred.
|
/* Init of nested tree is deferred.
|
||||||
* TextfileWindow::ConstructWindow must be called by the inheriting window. */
|
* TextfileWindow::ConstructWindow must be called by the inheriting window. */
|
||||||
|
@ -754,19 +754,6 @@ bool TextfileWindow::IsTextWrapped() const
|
||||||
return this->lines[this->search_iterator++].text;
|
return this->lines[this->search_iterator++].text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* virtual */ bool TextfileWindow::Monospace()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* virtual */ void TextfileWindow::SetFontNames([[maybe_unused]] FontCacheSettings *settings, [[maybe_unused]] std::string_view font_name, [[maybe_unused]] const void *os_data)
|
|
||||||
{
|
|
||||||
#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
|
|
||||||
settings->mono.font = font_name;
|
|
||||||
settings->mono.os_handle = os_data;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(WITH_ZLIB)
|
#if defined(WITH_ZLIB)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
std::optional<std::string> GetTextfile(TextfileType type, Subdirectory dir, std::string_view filename);
|
std::optional<std::string> GetTextfile(TextfileType type, Subdirectory dir, std::string_view filename);
|
||||||
|
|
||||||
/** Window for displaying a textfile */
|
/** Window for displaying a textfile */
|
||||||
struct TextfileWindow : public Window, MissingGlyphSearcher {
|
struct TextfileWindow : public Window, BaseStringMissingGlyphSearcher {
|
||||||
TextfileType file_type{}; ///< Type of textfile to view.
|
TextfileType file_type{}; ///< Type of textfile to view.
|
||||||
Scrollbar *vscroll = nullptr; ///< Vertical scrollbar.
|
Scrollbar *vscroll = nullptr; ///< Vertical scrollbar.
|
||||||
Scrollbar *hscroll = nullptr; ///< Horizontal scrollbar.
|
Scrollbar *hscroll = nullptr; ///< Horizontal scrollbar.
|
||||||
|
@ -38,8 +38,6 @@ struct TextfileWindow : public Window, MissingGlyphSearcher {
|
||||||
void Reset() override;
|
void Reset() override;
|
||||||
FontSize DefaultSize() override;
|
FontSize DefaultSize() override;
|
||||||
std::optional<std::string_view> NextString() override;
|
std::optional<std::string_view> NextString() override;
|
||||||
bool Monospace() override;
|
|
||||||
void SetFontNames(FontCacheSettings *settings, std::string_view font_name, const void *os_data) override;
|
|
||||||
void ScrollToLine(size_t line);
|
void ScrollToLine(size_t line);
|
||||||
bool IsTextWrapped() const;
|
bool IsTextWrapped() const;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue