Cache font list into binary file by Cereal - remember bad font
This commit is contained in:
parent
dd6dfb5567
commit
67d8b8291a
2 changed files with 152 additions and 31 deletions
|
@ -1234,20 +1234,6 @@ std::size_t hash_value(wxString const &s)
|
||||||
return hasher(s.ToStdString());
|
return hasher(s.ToStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLGizmoEmboss::select_facename(const wxString &facename) {
|
|
||||||
if (!wxFontEnumerator::IsValidFacename(facename)) return false;
|
|
||||||
// Select font
|
|
||||||
const wxFontEncoding &encoding = m_face_names.encoding;
|
|
||||||
wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(encoding));
|
|
||||||
if (!wx_font.IsOk()) return false;
|
|
||||||
// wx font could change source file by size of font
|
|
||||||
int point_size = static_cast<int>(m_style_manager.get_font_prop().size_in_mm);
|
|
||||||
wx_font.SetPointSize(point_size);
|
|
||||||
if (!m_style_manager.set_wx_font(wx_font)) return false;
|
|
||||||
process();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string concat(std::vector<wxString> data) {
|
static std::string concat(std::vector<wxString> data) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
for (const auto &d : data)
|
for (const auto &d : data)
|
||||||
|
@ -1255,11 +1241,113 @@ static std::string concat(std::vector<wxString> data) {
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boost::filesystem::path get_fontlist_cache_path()
|
||||||
|
{
|
||||||
|
return boost::filesystem::path(data_dir()) / "cache" / "fonts.cereal";
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache font list by cereal
|
||||||
|
#include <cereal/cereal.hpp>
|
||||||
|
#include <cereal/types/vector.hpp>
|
||||||
|
#include <cereal/types/string.hpp>
|
||||||
|
#include <cereal/archives/binary.hpp>
|
||||||
|
|
||||||
|
// increase number when change struct FacenamesSerializer
|
||||||
|
#define FACENAMES_VERSION 1
|
||||||
|
struct FacenamesSerializer
|
||||||
|
{
|
||||||
|
// hash number for unsorted vector of installed font into system
|
||||||
|
size_t hash = 0;
|
||||||
|
// assumption that is loadable
|
||||||
|
std::vector<wxString> good;
|
||||||
|
// Can't load for some reason
|
||||||
|
std::vector<wxString> bad;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Archive> void save(Archive &archive, wxString const &d)
|
||||||
|
{ std::string s(d.ToUTF8().data()); archive(s);}
|
||||||
|
template<class Archive> void load(Archive &archive, wxString &d)
|
||||||
|
{ std::string s; archive(s); d = s;}
|
||||||
|
|
||||||
|
template<class Archive> void serialize(Archive &ar, FacenamesSerializer &t, const std::uint32_t version)
|
||||||
|
{
|
||||||
|
// When performing a load, the version associated with the class
|
||||||
|
// is whatever it was when that data was originally serialized
|
||||||
|
// When we save, we'll use the version that is defined in the macro
|
||||||
|
if (version != FACENAMES_VERSION) return;
|
||||||
|
ar(t.hash, t.good, t.bad);
|
||||||
|
}
|
||||||
|
CEREAL_CLASS_VERSION(FacenamesSerializer, FACENAMES_VERSION); // register class version
|
||||||
|
|
||||||
|
bool GLGizmoEmboss::store(const Facenames &facenames) {
|
||||||
|
std::string cache_path = get_fontlist_cache_path().string();
|
||||||
|
boost::nowide::ofstream file(cache_path, std::ios::binary);
|
||||||
|
cereal::BinaryOutputArchive archive(file);
|
||||||
|
std::vector<wxString> good;
|
||||||
|
good.reserve(facenames.faces.size());
|
||||||
|
for (const FaceName &face : facenames.faces) good.push_back(face.wx_name);
|
||||||
|
FacenamesSerializer data = {facenames.hash, good, facenames.bad};
|
||||||
|
|
||||||
|
assert(std::is_sorted(data.bad.begin(), data.bad.end()));
|
||||||
|
assert(std::is_sorted(data.good.begin(), data.good.end()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
archive(data);
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Failed to write fontlist cache - " << cache_path << ex.what();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLGizmoEmboss::load(Facenames &facenames) {
|
||||||
|
boost::filesystem::path path = get_fontlist_cache_path();
|
||||||
|
std::string path_str = path.string();
|
||||||
|
if (!boost::filesystem::exists(path)) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "Fontlist cache - '" << path_str << "' does not exists.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boost::nowide::ifstream file(path_str, std::ios::binary);
|
||||||
|
cereal::BinaryInputArchive archive(file);
|
||||||
|
|
||||||
|
FacenamesSerializer data;
|
||||||
|
try {
|
||||||
|
archive(data);
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Failed to load fontlist cache - '" << path_str << "'. Exception: " << ex.what();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(std::is_sorted(data.bad.begin(), data.bad.end()));
|
||||||
|
assert(std::is_sorted(data.good.begin(), data.good.end()));
|
||||||
|
|
||||||
|
facenames.hash = data.hash;
|
||||||
|
facenames.faces.reserve(data.good.size());
|
||||||
|
for (const wxString &face : data.good)
|
||||||
|
facenames.faces.push_back({face});
|
||||||
|
facenames.bad = data.bad;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void GLGizmoEmboss::init_face_names() {
|
void GLGizmoEmboss::init_face_names() {
|
||||||
Timer t("enumerate_fonts");
|
Timer t("enumerate_fonts");
|
||||||
if (m_face_names.is_init) return;
|
if (m_face_names.is_init) return;
|
||||||
m_face_names.is_init = true;
|
m_face_names.is_init = true;
|
||||||
|
|
||||||
|
auto create_truncated_names = [&facenames = m_face_names, &width = m_gui_cfg->face_name_max_width]() {
|
||||||
|
for (FaceName &face : facenames.faces) {
|
||||||
|
std::string name_str(face.wx_name.ToUTF8().data());
|
||||||
|
face.name_truncated = ImGuiWrapper::trunc(name_str, width);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// try load cache
|
||||||
|
// Only not OS enumerated face has hash value 0
|
||||||
|
if (m_face_names.hash == 0) {
|
||||||
|
load(m_face_names);
|
||||||
|
create_truncated_names();
|
||||||
|
}
|
||||||
|
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
steady_clock::time_point enumerate_start = steady_clock::now();
|
steady_clock::time_point enumerate_start = steady_clock::now();
|
||||||
ScopeGuard sg([&enumerate_start, &face_names = m_face_names]() {
|
ScopeGuard sg([&enumerate_start, &face_names = m_face_names]() {
|
||||||
|
@ -1272,20 +1360,25 @@ void GLGizmoEmboss::init_face_names() {
|
||||||
});
|
});
|
||||||
wxArrayString facenames = wxFontEnumerator::GetFacenames(m_face_names.encoding);
|
wxArrayString facenames = wxFontEnumerator::GetFacenames(m_face_names.encoding);
|
||||||
size_t hash = boost::hash_range(facenames.begin(), facenames.end());
|
size_t hash = boost::hash_range(facenames.begin(), facenames.end());
|
||||||
|
// Zero value is used as uninitialized hash
|
||||||
|
if (hash == 0) hash = 1;
|
||||||
// check if it is same as last time
|
// check if it is same as last time
|
||||||
if (m_face_names.hash == hash) return; // no new installed font
|
if (m_face_names.hash == hash) return; // no new installed font
|
||||||
m_face_names.hash = hash;
|
m_face_names.hash = hash;
|
||||||
|
|
||||||
// validation lambda
|
// validation lambda
|
||||||
auto is_valid_font = [encoding = m_face_names.encoding](const wxString &name) {
|
auto is_valid_font = [encoding = m_face_names.encoding, bad = m_face_names.bad /*copy*/](const wxString &name) {
|
||||||
if (name.empty()) return false;
|
if (name.empty()) return false;
|
||||||
|
|
||||||
// vertical font start with @, we will filter it out
|
// vertical font start with @, we will filter it out
|
||||||
// Not sure if it is only in Windows so filtering is on all platforms
|
// Not sure if it is only in Windows so filtering is on all platforms
|
||||||
if (name[0] == '@') return false;
|
if (name[0] == '@') return false;
|
||||||
|
|
||||||
wxFont wx_font(wxFontInfo().FaceName(name).Encoding(encoding));
|
|
||||||
|
|
||||||
|
// previously detected bad font
|
||||||
|
auto it = std::lower_bound(bad.begin(), bad.end(), name);
|
||||||
|
if (it != bad.end() && *it == name) return false;
|
||||||
|
|
||||||
|
wxFont wx_font(wxFontInfo().FaceName(name).Encoding(encoding));
|
||||||
//*
|
//*
|
||||||
// Faster chech if wx_font is loadable but not 100%
|
// Faster chech if wx_font is loadable but not 100%
|
||||||
// names could contain not loadable font
|
// names could contain not loadable font
|
||||||
|
@ -1301,19 +1394,20 @@ void GLGizmoEmboss::init_face_names() {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const float &width = m_gui_cfg->face_name_max_width;
|
m_face_names.faces.clear();
|
||||||
|
m_face_names.bad.clear();
|
||||||
m_face_names.faces.reserve(facenames.size());
|
m_face_names.faces.reserve(facenames.size());
|
||||||
|
std::sort(facenames.begin(), facenames.end());
|
||||||
for (const wxString &name : facenames) {
|
for (const wxString &name : facenames) {
|
||||||
if (!is_valid_font(name)) {
|
if (is_valid_font(name)) {
|
||||||
|
m_face_names.faces.push_back({name});
|
||||||
|
}else{
|
||||||
m_face_names.bad.push_back(name);
|
m_face_names.bad.push_back(name);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FaceName face_name = {name};
|
|
||||||
std::string name_str(name.ToUTF8().data());
|
|
||||||
face_name.name_truncated = ImGuiWrapper::trunc(name_str, width);
|
|
||||||
m_face_names.faces.push_back(std::move(face_name));
|
|
||||||
}
|
}
|
||||||
|
assert(std::is_sorted(m_face_names.bad.begin(), m_face_names.bad.end()));
|
||||||
|
create_truncated_names();
|
||||||
|
store(m_face_names);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create texture for visualization font face
|
// create texture for visualization font face
|
||||||
|
@ -1410,6 +1504,21 @@ void GLGizmoEmboss::draw_font_preview(FaceName& face)
|
||||||
ImGui::Image(tex_id, size, uv0, uv1);
|
ImGui::Image(tex_id, size, uv0, uv1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GLGizmoEmboss::select_facename(const wxString &facename)
|
||||||
|
{
|
||||||
|
if (!wxFontEnumerator::IsValidFacename(facename)) return false;
|
||||||
|
// Select font
|
||||||
|
const wxFontEncoding &encoding = m_face_names.encoding;
|
||||||
|
wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(encoding));
|
||||||
|
if (!wx_font.IsOk()) return false;
|
||||||
|
// wx font could change source file by size of font
|
||||||
|
int point_size = static_cast<int>(m_style_manager.get_font_prop().size_in_mm);
|
||||||
|
wx_font.SetPointSize(point_size);
|
||||||
|
if (!m_style_manager.set_wx_font(wx_font)) return false;
|
||||||
|
process();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void GLGizmoEmboss::draw_font_list()
|
void GLGizmoEmboss::draw_font_list()
|
||||||
{
|
{
|
||||||
// Set partial
|
// Set partial
|
||||||
|
@ -1479,10 +1588,16 @@ void GLGizmoEmboss::draw_font_list()
|
||||||
m_face_names.texture_id = 0;
|
m_face_names.texture_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete unloadable face name when appear
|
// delete unloadable face name when try to use
|
||||||
if (del_index.has_value()) {
|
if (del_index.has_value()) {
|
||||||
// IMPROVE: store list of deleted facename into app.ini
|
auto face = m_face_names.faces.begin() + (*del_index);
|
||||||
m_face_names.faces.erase(m_face_names.faces.begin() + (*del_index));
|
std::vector<wxString>& bad = m_face_names.bad;
|
||||||
|
// sorted insert into bad fonts
|
||||||
|
auto it = std::upper_bound(bad.begin(), bad.end(), face->wx_name);
|
||||||
|
bad.insert(it, face->wx_name);
|
||||||
|
m_face_names.faces.erase(face);
|
||||||
|
// update cached file
|
||||||
|
store(m_face_names);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ALLOW_ADD_FONT_BY_FILE
|
#ifdef ALLOW_ADD_FONT_BY_FILE
|
||||||
|
|
|
@ -244,11 +244,14 @@ private:
|
||||||
// Keep sorted list of loadable face names
|
// Keep sorted list of loadable face names
|
||||||
struct Facenames
|
struct Facenames
|
||||||
{
|
{
|
||||||
// flag if face names was enumerated from OS
|
// flag to keep need of enumeration fonts from OS
|
||||||
|
// false .. wants new enumeration check by Hash
|
||||||
|
// true .. already enumerated(During opened combo box)
|
||||||
bool is_init = false;
|
bool is_init = false;
|
||||||
|
|
||||||
// data of can_load() faces
|
// data of can_load() faces
|
||||||
std::vector<FaceName> faces = {};
|
std::vector<FaceName> faces = {};
|
||||||
// Not valid face names
|
// Sorter set of Non valid face names in OS
|
||||||
std::vector<wxString> bad = {};
|
std::vector<wxString> bad = {};
|
||||||
|
|
||||||
// Configuration of font encoding
|
// Configuration of font encoding
|
||||||
|
@ -271,6 +274,9 @@ private:
|
||||||
// check when new font was installed
|
// check when new font was installed
|
||||||
size_t hash = 0;
|
size_t hash = 0;
|
||||||
} m_face_names;
|
} m_face_names;
|
||||||
|
static bool store(const Facenames &facenames);
|
||||||
|
static bool load(Facenames &facenames);
|
||||||
|
|
||||||
|
|
||||||
// Text to emboss
|
// Text to emboss
|
||||||
std::string m_text;
|
std::string m_text;
|
||||||
|
|
Loading…
Add table
Reference in a new issue