#include "FontUtils.hpp" #include "imgui/imstb_truetype.h" #include "libslic3r/Utils.hpp" #include #if defined(__APPLE__) #include #include #include // wxNativeFontInfo #include #elif defined(__linux__) #include "FontConfigHelp.hpp" #endif namespace Slic3r { #ifdef __APPLE__ bool is_valid_ttf(std::string_view file_path) { if (file_path.empty()) return false; auto const pos_point = file_path.find_last_of('.'); if (pos_point == std::string_view::npos) return false; // use point only after last directory delimiter auto const pos_directory_delimiter = file_path.find_last_of("/\\"); if (pos_directory_delimiter != std::string_view::npos && pos_point < pos_directory_delimiter) return false; // point is before directory delimiter // check count of extension chars size_t extension_size = file_path.size() - pos_point; if (extension_size >= 5) return false; // a lot of symbols for extension if (extension_size <= 1) return false; // few letters for extension std::string_view extension = file_path.substr(pos_point + 1, extension_size); // Because of MacOs - Courier, Geneva, Monaco if (extension == std::string_view("dfont")) return false; return true; } // get filepath from wxFont on Mac OsX std::string get_file_path(const wxFont &font) { const wxNativeFontInfo *info = font.GetNativeFontInfo(); if (info == nullptr) return {}; CTFontDescriptorRef descriptor = info->GetCTFontDescriptor(); CFURLRef typeref = (CFURLRef) CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute); if (typeref == NULL) return {}; ScopeGuard sg([&typeref]() { CFRelease(typeref); }); CFStringRef url = CFURLGetString(typeref); if (url == NULL) return {}; wxString file_uri(wxCFStringRef::AsString(url)); wxURI uri(file_uri); const wxString &path = uri.GetPath(); wxString path_unescaped = wxURI::Unescape(path); std::string path_str = path_unescaped.ToUTF8().data(); BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ") string(" << path_str << ")."; return path_str; } #endif // __APPLE__ using fontinfo_opt = std::optional; std::string get_human_readable_name(const wxFont &font) { if (!font.IsOk()) return "Font is NOT ok."; // Face name is optional in wxFont if (!font.GetFaceName().empty()) { return std::string(font.GetFaceName().c_str()); } else { return std::string((font.GetFamilyString() + " " + font.GetStyleString() + " " + font.GetWeightString()).c_str()); } } fontinfo_opt load_font_info(const unsigned char *data, unsigned int index) { int font_offset = stbtt_GetFontOffsetForIndex(data, index); if (font_offset < 0) { assert(false); // "Font index(" << index << ") doesn't exist."; return {}; } stbtt_fontinfo font_info; if (stbtt_InitFont(&font_info, data, font_offset) == 0) { // Can't initialize font. //assert(false); return {}; } return font_info; } std::unique_ptr create_font_file(std::unique_ptr> data) { int collection_size = stbtt_GetNumberOfFonts(data->data()); // at least one font must be inside collection if (collection_size < 1) { assert(false); // There is no font collection inside font data return nullptr; } unsigned int c_size = static_cast(collection_size); std::vector infos; infos.reserve(c_size); for (unsigned int i = 0; i < c_size; ++i) { auto font_info = load_font_info(data->data(), i); if (!font_info.has_value()) return nullptr; const stbtt_fontinfo *info = &(*font_info); // load information about line gap int ascent, descent, linegap; stbtt_GetFontVMetrics(info, &ascent, &descent, &linegap); float pixels = 1000.; // value is irelevant float em_pixels = stbtt_ScaleForMappingEmToPixels(info, pixels); int units_per_em = static_cast(std::round(pixels / em_pixels)); infos.emplace_back(FontFile::Info{ascent, descent, linegap, units_per_em}); } return std::make_unique(std::move(data), std::move(infos)); } std::unique_ptr create_font_file(const char *file_path) { FILE *file = std::fopen(file_path, "rb"); if (file == nullptr) { assert(false); BOOST_LOG_TRIVIAL(error) << "Couldn't open " << file_path << " for reading."; return nullptr; } ScopeGuard sg([&file]() { std::fclose(file); }); // find size of file if (fseek(file, 0L, SEEK_END) != 0) { assert(false); BOOST_LOG_TRIVIAL(error) << "Couldn't fseek file " << file_path << " for size measure."; return nullptr; } size_t size = ftell(file); if (size == 0) { assert(false); BOOST_LOG_TRIVIAL(error) << "Size of font file is zero. Can't read."; return nullptr; } rewind(file); auto buffer = std::make_unique>(size); size_t count_loaded_bytes = fread((void *) &buffer->front(), 1, size, file); if (count_loaded_bytes != size) { assert(false); BOOST_LOG_TRIVIAL(error) << "Different loaded(from file) data size."; return nullptr; } return create_font_file(std::move(buffer)); } #ifdef _WIN32 bool load_hfont(void *hfont, DWORD &dwTable, DWORD &dwOffset, size_t &size, HDC hdc = nullptr) { bool del_hdc = false; if (hdc == nullptr) { del_hdc = true; hdc = ::CreateCompatibleDC(NULL); if (hdc == NULL) { DWORD errorCode = GetLastError(); LPVOID lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); BOOST_LOG_TRIVIAL(error) << "CreateCompatibleDC failed in load_hfont : " << "Error: " << reinterpret_cast(lpMsgBuf); return false; } } // To retrieve the data from the beginning of the file for TrueType // Collection files specify 'ttcf' (0x66637474). dwTable = 0x66637474; dwOffset = 0; ::SelectObject(hdc, hfont); size = ::GetFontData(hdc, dwTable, dwOffset, NULL, 0); if (size == GDI_ERROR) { // HFONT is NOT TTC(collection) dwTable = 0; size = ::GetFontData(hdc, dwTable, dwOffset, NULL, 0); } if (size == 0 || size == GDI_ERROR) { if (del_hdc) ::DeleteDC(hdc); return false; } if (del_hdc) ::DeleteDC(hdc); return true; } std::unique_ptr create_font_file(void *hfont) { HDC hdc = ::CreateCompatibleDC(NULL); if (hdc == NULL) { assert(false); DWORD errorCode = GetLastError(); LPVOID lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); BOOST_LOG_TRIVIAL(error) << "CreateCompatibleDC failed in create_font_file : " << "Error: " << reinterpret_cast(lpMsgBuf); return nullptr; } DWORD dwTable = 0, dwOffset = 0; size_t size; if (!load_hfont(hfont, dwTable, dwOffset, size, hdc)) { ::DeleteDC(hdc); return nullptr; } auto buffer = std::make_unique>(size); size_t loaded_size = ::GetFontData(hdc, dwTable, dwOffset, buffer->data(), size); ::DeleteDC(hdc); if (size != loaded_size) { assert(false); BOOST_LOG_TRIVIAL(error) << "Different loaded(from HFONT) data size."; return nullptr; } return create_font_file(std::move(buffer)); } #endif std::unique_ptr create_font_file(const wxFont &font) { #ifdef _WIN32 return create_font_file(font.GetHFONT()); #elif defined(__APPLE__) std::string file_path = get_file_path(font); if (!is_valid_ttf(file_path)) { BOOST_LOG_TRIVIAL(error) << "Can not process font('" << get_human_readable_name(font) << "'), " << "file in path('" << file_path << "') is not valid TTF."; return nullptr; } return create_font_file(file_path.c_str()); #elif defined(__linux__) std::string font_path = Slic3r::GUI::get_font_path(font); if (font_path.empty()) { BOOST_LOG_TRIVIAL(error) << "Can not read font('" << get_human_readable_name(font) << "'), " << "file path is empty."; return nullptr; } return create_font_file(font_path.c_str()); #else // HERE is place to add implementation for another platform // to convert wxFont to font data as windows or font file path as linux return nullptr; #endif } bool can_generate_text_shape_from_font(const stbtt_fontinfo &font_info) { const float flatness = 0.0125f; // [in mm] wchar_t letter = 'A'; int unicode_letter = static_cast(letter); int glyph_index = stbtt_FindGlyphIndex(&font_info, unicode_letter); if (glyph_index == 0) { return false; } int advance_width=0, left_side_bearing=0; stbtt_GetGlyphHMetrics(&font_info, glyph_index, &advance_width, &left_side_bearing); stbtt_vertex *vertices; int num_verts = stbtt_GetGlyphShape(&font_info, glyph_index, &vertices); if (num_verts <= 0) return false; return true; } bool can_generate_text_shape(const std::string& font_name) { wxFont wx_font(wxFontInfo().FaceName(font_name.c_str()).Encoding(wxFontEncoding::wxFONTENCODING_SYSTEM)); std::unique_ptr font = create_font_file(wx_font); if (!font) return false; fontinfo_opt font_info_opt = load_font_info(font->data->data(), 0); if (!font_info_opt.has_value()) return false; return can_generate_text_shape_from_font(*font_info_opt); } bool can_load(const wxFont &font) { #ifdef _WIN32 DWORD dwTable = 0, dwOffset = 0; size_t size = 0; void * hfont = font.GetHFONT(); if (!load_hfont(hfont, dwTable, dwOffset, size)) return false; return hfont != nullptr; #elif defined(__APPLE__) return true; #elif defined(__linux__) return true; #endif return false; } }