Show user warning about unknown symbols by font inside of input text

Fix correct selection of collection ascent, descent, ...
Remove boost log from emboss --> not work properly on thread
This commit is contained in:
Filip Sykala 2022-04-07 16:34:14 +02:00
parent 68c34210d2
commit 331d4d4557
11 changed files with 329 additions and 183 deletions

View File

@ -22,6 +22,7 @@ class Private
{
public:
Private() = delete;
static bool is_valid(const Emboss::FontFile &font, unsigned int index);
static std::optional<stbtt_fontinfo> load_font_info(const unsigned char *data, unsigned int index = 0);
static std::optional<Emboss::Glyph> get_glyph(const stbtt_fontinfo &font_info, int unicode_letter, float flatness);
@ -43,17 +44,27 @@ public:
static Point to_point(const stbtt__point &point);
};
bool Private::is_valid(const Emboss::FontFile &font, unsigned int index) {
if (font.data == nullptr) return false;
if (font.data->empty()) return false;
if (font.count == 0) return false;
if (index >= font.count) return false;
return true;
}
std::optional<stbtt_fontinfo> Private::load_font_info(
const unsigned char *data, unsigned int index)
{
int font_offset = stbtt_GetFontOffsetForIndex(data, index);
if (font_offset < 0) {
BOOST_LOG_TRIVIAL(error) << "Font index(" << index << ") doesn't exist." << std::endl;
assert(false);
// "Font index(" << index << ") doesn't exist.";
return {};
}
stbtt_fontinfo font_info;
if (stbtt_InitFont(&font_info, data, font_offset) == 0) {
BOOST_LOG_TRIVIAL(error) << "Can't initialize font." << std::endl;
// Can't initialize font.
assert(false);
return {};
}
return font_info;
@ -63,12 +74,12 @@ std::optional<Emboss::Glyph> Private::get_glyph(const stbtt_fontinfo &font_info,
{
int glyph_index = stbtt_FindGlyphIndex(&font_info, unicode_letter);
if (glyph_index == 0) {
wchar_t wchar = static_cast<wchar_t>(unicode_letter);
BOOST_LOG_TRIVIAL(error) << "Character unicode letter ("
<< "decimal value = " << std::dec << unicode_letter << ", "
<< "hexadecimal value = U+" << std::hex << unicode_letter << std::dec << ", "
<< "wchar value = " << wchar
<< ") is NOT defined inside of the font. \n";
//wchar_t wchar = static_cast<wchar_t>(unicode_letter);
//<< "Character unicode letter ("
//<< "decimal value = " << std::dec << unicode_letter << ", "
//<< "hexadecimal value = U+" << std::hex << unicode_letter << std::dec << ", "
//<< "wchar value = " << wchar
//<< ") is NOT defined inside of the font. \n";
return {};
}
@ -136,17 +147,19 @@ const Emboss::Glyph* Private::get_glyph(
const double RESOLUTION = 0.0125; // TODO: read from printer configuration
auto glyph_item = cache.find(unicode);
if (glyph_item != cache.end()) return &glyph_item->second;
if (!font_info_opt.has_value()) {
unsigned int font_index = font_prop.collection_number.has_value()?
unsigned int font_index = font_prop.collection_number.has_value()?
*font_prop.collection_number : 0;
if (font_index >= font.count) return nullptr;
if (!is_valid(font, font_index)) return nullptr;
if (!font_info_opt.has_value()) {
font_info_opt = Private::load_font_info(font.data->data(), font_index);
// can load font info?
if (!font_info_opt.has_value()) return nullptr;
}
float flatness = static_cast<float>(
font.ascent * RESOLUTION / font_prop.size_in_mm);
float flatness = static_cast<float>(font.infos[font_index].ascent * RESOLUTION / font_prop.size_in_mm);
std::optional<Emboss::Glyph> glyph_opt =
Private::get_glyph(*font_info_opt, unicode, flatness);
@ -350,18 +363,19 @@ FontList Emboss::get_font_list_by_register() {
// Open Windows font registry key
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, fontRegistryPath, 0, KEY_READ, &hKey);
if (result != ERROR_SUCCESS) {
std::wcerr << L"Can not Open register key (" << fontRegistryPath << ")"
<< L", function 'RegOpenKeyEx' return code: " << result << std::endl;
if (result != ERROR_SUCCESS) {
assert(false);
//std::wcerr << L"Can not Open register key (" << fontRegistryPath << ")"
// << L", function 'RegOpenKeyEx' return code: " << result << std::endl;
return {};
}
DWORD maxValueNameSize, maxValueDataSize;
result = RegQueryInfoKey(hKey, 0, 0, 0, 0, 0, 0, 0, &maxValueNameSize,
&maxValueDataSize, 0, 0);
if (result != ERROR_SUCCESS) {
BOOST_LOG_TRIVIAL(error) << "Can not earn query key, function 'RegQueryInfoKey' return code: "
<< result << std::endl;
if (result != ERROR_SUCCESS) {
assert(false);
// Can not earn query key, function 'RegQueryInfoKey' return code: result
return {};
}
@ -475,47 +489,61 @@ std::unique_ptr<Emboss::FontFile> Emboss::create_font_file(
int collection_size = stbtt_GetNumberOfFonts(data->data());
// at least one font must be inside collection
if (collection_size < 1) {
BOOST_LOG_TRIVIAL(error) << "There is no font collection inside data." << std::endl;
assert(false);
// There is no font collection inside font data
return nullptr;
}
auto font_info = Private::load_font_info(data->data());
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);
unsigned int c_size = static_cast<unsigned int>(collection_size);
std::vector<FontFile::Info> infos;
infos.reserve(c_size);
for (unsigned int i = 0; i < c_size; ++i) {
auto font_info = Private::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<int>(std::round(pixels / em_pixels));
infos.emplace_back(FontFile::Info{ascent, descent, linegap, units_per_em});
}
float pixels = 1000.; // value is irelevant
float em_pixels = stbtt_ScaleForMappingEmToPixels(info, pixels);
int units_per_em = static_cast<int>(std::round(pixels / em_pixels));
return std::make_unique<Emboss::FontFile>(
std::move(data), collection_size, ascent, descent, linegap, units_per_em);
std::move(data), collection_size, std::move(infos));
}
std::unique_ptr<Emboss::FontFile> Emboss::create_font_file(const char *file_path)
{
FILE *file = fopen(file_path, "rb");
if (file == nullptr) {
BOOST_LOG_TRIVIAL(error) << "Couldn't open " << file_path << " for reading." << std::endl;
assert(false);
// BOOST_LOG_TRIVIAL(error) << "Couldn't open " << file_path << " for reading." << std::endl;
return nullptr;
}
// find size of file
if (fseek(file, 0L, SEEK_END) != 0) {
BOOST_LOG_TRIVIAL(error) << "Couldn't fseek file " << file_path << " for size measure." << std::endl;
assert(false);
// BOOST_LOG_TRIVIAL(error) << "Couldn't fseek file " << file_path << " for size measure." << std::endl;
return nullptr;
}
size_t size = ftell(file);
if (size == 0) {
BOOST_LOG_TRIVIAL(error) << "Size of font file is zero. Can't read." << std::endl;
assert(false);
// BOOST_LOG_TRIVIAL(error) << "Size of font file is zero. Can't read." << std::endl;
return nullptr;
}
rewind(file);
auto buffer = std::make_unique<std::vector<unsigned char>>(size);
size_t count_loaded_bytes = fread((void *) &buffer->front(), 1, size, file);
if (count_loaded_bytes != size) {
BOOST_LOG_TRIVIAL(error) << "Different loaded(from file) data size." << std::endl;
assert(false);
// BOOST_LOG_TRIVIAL(error) << "Different loaded(from file) data size." << std::endl;
return nullptr;
}
return create_font_file(std::move(buffer));
@ -563,7 +591,8 @@ std::unique_ptr<Emboss::FontFile> Emboss::create_font_file(HFONT hfont)
{
HDC hdc = ::CreateCompatibleDC(NULL);
if (hdc == NULL) {
BOOST_LOG_TRIVIAL(error) << "Can't create HDC by CreateCompatibleDC(NULL)." << std::endl;
assert(false);
// BOOST_LOG_TRIVIAL(error) << "Can't create HDC by CreateCompatibleDC(NULL)." << std::endl;
return nullptr;
}
@ -577,7 +606,8 @@ std::unique_ptr<Emboss::FontFile> Emboss::create_font_file(HFONT hfont)
size_t loaded_size = ::GetFontData(hdc, dwTable, dwOffset, buffer->data(), size);
::DeleteDC(hdc);
if (size != loaded_size) {
BOOST_LOG_TRIVIAL(error) << "Different loaded(from HFONT) data size." << std::endl;
assert(false);
// BOOST_LOG_TRIVIAL(error) << "Different loaded(from HFONT) data size." << std::endl;
return nullptr;
}
return create_font_file(std::move(buffer));
@ -585,10 +615,12 @@ std::unique_ptr<Emboss::FontFile> Emboss::create_font_file(HFONT hfont)
#endif // _WIN32
std::optional<Emboss::Glyph> Emboss::letter2glyph(const FontFile &font,
unsigned int font_index,
int letter,
float flatness)
{
auto font_info_opt = Private::load_font_info(font.data->data(), 0);
if (!Private::is_valid(font, font_index)) return {};
auto font_info_opt = Private::load_font_info(font.data->data(), font_index);
if (!font_info_opt.has_value()) return {};
return Private::get_glyph(*font_info_opt, letter, flatness);
}
@ -603,11 +635,15 @@ ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache,
Point cursor(0, 0);
ExPolygons result;
const FontFile& font = *font_with_cache.font_file;
unsigned int font_index = font_prop.collection_number.has_value()?
*font_prop.collection_number : 0;
if (!Private::is_valid(font, font_index)) return {};
const FontFile::Info& info = font.infos[font_index];
Emboss::Glyphs& cache = *font_with_cache.cache;
std::wstring ws = boost::nowide::widen(text);
for (wchar_t wc: ws){
if (wc == '\n') {
int line_height = font.ascent - font.descent + font.linegap;
int line_height = info.ascent - info.descent + info.linegap;
if (font_prop.line_gap.has_value())
line_height += *font_prop.line_gap;
line_height = static_cast<int>(line_height / SHAPE_SCALE);
@ -693,6 +729,40 @@ bool Emboss::is_italic(const FontFile &font, unsigned int font_index)
return false;
}
std::string Emboss::create_range_text(const std::string &text,
const FontFile &font,
unsigned int font_index,
bool *exist_unknown)
{
if (!Private::is_valid(font, font_index)) return {};
std::wstring ws = boost::nowide::widen(text);
// need remove symbols not contained in font
std::sort(ws.begin(), ws.end());
auto font_info_opt = Private::load_font_info(font.data->data(), 0);
if (!font_info_opt.has_value()) return {};
const stbtt_fontinfo *font_info = &(*font_info_opt);
if (exist_unknown != nullptr) *exist_unknown = false;
int prev_unicode = -1;
ws.erase(std::remove_if(ws.begin(), ws.end(),
[&prev_unicode, font_info, exist_unknown](wchar_t wc) -> bool {
int unicode = static_cast<int>(wc);
// is duplicit
if (prev_unicode == unicode) return true;
prev_unicode = unicode;
bool is_unknown = !stbtt_FindGlyphIndex(font_info, unicode);
if (is_unknown && exist_unknown != nullptr)
*exist_unknown = true;
return is_unknown;
}), ws.end());
return boost::nowide::narrow(ws);
}
indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
const IProject &projection)
{

View File

@ -71,32 +71,39 @@ public:
// count of fonts when data are collection of fonts
unsigned int count;
// vertical position is "scale*(ascent - descent + lineGap)"
int ascent, descent, linegap;
struct Info
{
// vertical position is "scale*(ascent - descent + lineGap)"
int ascent, descent, linegap;
// for convert font units to pixel
int unit_per_em;
// for convert font units to pixel
int unit_per_em;
};
// info for each font in data
std::vector<Info> infos;
FontFile(std::unique_ptr<std::vector<unsigned char>> data,
unsigned int count,
int ascent,
int descent,
int linegap,
int unit_per_em)
unsigned int count,
std::vector<Info> &&infos)
: data(std::move(data))
, count(count)
, ascent(ascent)
, descent(descent)
, linegap(linegap)
, unit_per_em(unit_per_em)
, infos(std::move(infos))
{
assert(this->data != nullptr);
assert(!this->data->empty());
assert(count == this->infos.size());
}
bool operator==(const FontFile &other) const {
return count == other.count && ascent == other.ascent &&
descent == other.descent && linegap == other.linegap &&
data->size() == other.data->size();
//&& *data == *other.data;
if (count != other.count || data->size() != other.data->size())
return false;
//if(*data != *other.data) return false;
for (unsigned int i = 0; i < count; i++)
if (infos[i].ascent != other.infos[i].ascent ||
infos[i].descent == other.infos[i].descent ||
infos[i].linegap == other.infos[i].linegap)
return false;
return true;
}
};
@ -140,10 +147,11 @@ public:
/// convert letter into polygons
/// </summary>
/// <param name="font">Define fonts</param>
/// <param name="font_index">Index of font in collection</param>
/// <param name="letter">One character defined by unicode codepoint</param>
/// <param name="flatness">Precision of lettter outline curve in conversion to lines</param>
/// <returns>inner polygon cw(outer ccw)</returns>
static std::optional<Glyph> letter2glyph(const FontFile &font, int letter, float flatness);
static std::optional<Glyph> letter2glyph(const FontFile &font, unsigned int font_index, int letter, float flatness);
/// <summary>
/// Convert text into polygons
@ -152,9 +160,7 @@ public:
/// <param name="text">Characters to convert</param>
/// <param name="font_prop">User defined property of the font</param>
/// <returns>Inner polygon cw(outer ccw)</returns>
static ExPolygons text2shapes(FontFileWithCache &font,
const char * text,
const FontProp &font_prop);
static ExPolygons text2shapes(FontFileWithCache &font, const char *text, const FontProp &font_prop);
/// <summary>
/// Use data from font property to modify transformation
@ -162,8 +168,7 @@ public:
/// <param name="font_prop">Z-move as surface distance(FontProp::distance)
/// Z-rotation as angle to Y axis(FontProp::angle)</param>
/// <param name="transformation">In / Out transformation to modify by property</param>
static void apply_transformation(const FontProp &font_prop,
Transform3d &transformation);
static void apply_transformation(const FontProp &font_prop, Transform3d &transformation);
/// <summary>
/// Read information from naming table of font file
@ -174,6 +179,16 @@ public:
/// <returns>True when the font description contains italic/obligue otherwise False</returns>
static bool is_italic(const FontFile &font, unsigned int font_index);
/// <summary>
/// Create unique character set from string with filtered from text with only character from font
/// </summary>
/// <param name="text">Source vector of glyphs</param>
/// <param name="font">Font descriptor</param>
/// <param name="font_index">Define font in collection</param>
/// <param name="exist_unknown">True when text contain glyph unknown in font</param>
/// <returns>Unique set of character from text contained in font</returns>
static std::string create_range_text(const std::string &text, const FontFile &font, unsigned int font_index, bool* exist_unknown = nullptr);
/// <summary>
/// Project 2d point into space
/// Could be plane, sphere, cylindric, ...

View File

@ -186,10 +186,8 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
{
if (mouse_event.Moving()) return false;
m_rotate_gizmo.on_mouse(mouse_event);
use_grabbers(mouse_event);
if (!m_dragging) return false;
bool used = use_grabbers(mouse_event);
if (!m_dragging) return used;
assert(m_volume != nullptr);
assert(m_volume->text_configuration.has_value());
@ -220,14 +218,12 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
if (m_font_manager.is_activ_font()) {
m_font_manager.get_font_prop().angle = angle_opt;
}
return true;
} else if (mouse_event.LeftUp()) {
// apply rotation
m_parent.do_rotate(L("Text-Rotate"));
start_angle.reset();
return true;
}
return false;
return used;
}
bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
@ -515,6 +511,7 @@ void GLGizmoEmboss::on_stop_dragging()
// When fixing, move grabber above text (not on side)
m_rotate_gizmo.set_angle(0);
}
void GLGizmoEmboss::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); }
void GLGizmoEmboss::initialize()
{
@ -768,6 +765,24 @@ ModelVolume *GLGizmoEmboss::get_selected_volume(const Selection &selection,
return get_model_volume(vol_gl, objects);
}
static inline void execute_job(std::shared_ptr<Job> j)
{
struct MyCtl : public Job::Ctl
{
void update_status(int st, const std::string &msg = "") override{};
bool was_canceled() const override { return false; }
std::future<void> call_on_main_thread(std::function<void()> fn) override
{
return std::future<void>{};
}
} ctl;
j->process(ctl);
wxGetApp().plater()->CallAfter([j]() {
std::exception_ptr e_ptr = nullptr;
j->finalize(false, e_ptr);
});
}
bool GLGizmoEmboss::process()
{
// no volume is selected -> selection from right panel
@ -795,17 +810,8 @@ bool GLGizmoEmboss::process()
//*
auto &worker = wxGetApp().plater()->get_ui_job_worker();
queue_job(worker, std::make_unique<EmbossUpdateJob>(std::move(data)));
/*/
// Run Job on main thread (blocking)
EmbossUpdateJob j(std::move(data));
struct MyCtl:public Job::Ctl{
void update_status(int st, const std::string &msg = "") override{};
bool was_canceled() const override { return false; }
std::future<void> call_on_main_thread(std::function<void()> fn) override{return std::future<void>{};}
} ctl;
j.process(ctl);
std::exception_ptr e_ptr = nullptr;
j.finalize(false, e_ptr);
/*/ // Run Job on main thread (blocking) - ONLY DEBUG
execute_job(std::make_shared<EmbossUpdateJob>(std::move(data)));
// */
// notification is removed befor object is changed by job
@ -932,15 +938,28 @@ void GLGizmoEmboss::draw_window()
void GLGizmoEmboss::draw_text_input()
{
auto create_range_text = [&manager = m_font_manager, &text = m_text, &exist_unknown = m_text_contain_unknown_glyph]() {
auto &font_file = manager.get_font_file();
const auto &cn = manager.get_font_prop().collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
return Emboss::create_range_text(text, *font_file, font_index, &exist_unknown);
};
static const ImGuiInputTextFlags flags =
ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_AutoSelectAll;
ImFont *imgui_font = (m_font_manager.is_activ_font())?
m_font_manager.get_imgui_font(m_text) : nullptr;
bool exist_font = imgui_font != nullptr && imgui_font->IsLoaded();
ImFont *imgui_font = m_font_manager.get_imgui_font();
if (imgui_font == nullptr) {
// try create new imgui font
imgui_font = m_font_manager.create_imgui_font(create_range_text());
}
bool exist_font = imgui_font != nullptr;
assert(!exist_font || imgui_font->IsLoaded());
if (exist_font) ImGui::PushFont(imgui_font);
bool exist_change = false;
// flag for extend font ranges if neccessary
// ranges can't be extend during font is activ(pushed)
std::string range_text;
float window_height = ImGui::GetWindowHeight();
float minimal_height = get_minimal_window_size().y;
float extra_height = window_height - minimal_height;
@ -948,7 +967,7 @@ void GLGizmoEmboss::draw_text_input()
m_gui_cfg->text_size.y + extra_height);
if (ImGui::InputTextMultiline("##Text", &m_text, text_size, flags)) {
process();
exist_change = true;
range_text = create_range_text();
}
if (exist_font) ImGui::PopFont();
@ -971,34 +990,34 @@ void GLGizmoEmboss::draw_text_input()
tool_tip += t;
}
};
if (m_text_contain_unknown_glyph)
append_warning(_u8L("Bad symbol"),
_u8L("Text contain character glyph (represented by '?') unknown by font."));
const FontProp &prop = m_font_manager.get_font_prop();
if (prop.skew.has_value()) {
if (prop.skew.has_value())
append_warning(_u8L("Skew"),
_u8L("Unsupported visualization of font skew for text input."));
}
if (prop.boldness.has_value()) {
if (prop.boldness.has_value())
append_warning(_u8L("Boldness"),
_u8L("Unsupported visualization of font boldness for text input."));
}
if (prop.line_gap.has_value()) {
if (prop.line_gap.has_value())
append_warning(_u8L("Line gap"),
_u8L("Unsupported visualization of gap between lines inside text input."));
}
float imgui_size = FontManager::get_imgui_font_size(prop, *m_font_manager.get_font_file());
if (imgui_size > FontManager::max_imgui_font_size) {
if (imgui_size > FontManager::max_imgui_font_size)
append_warning(_u8L("To tall"),
_u8L("Diminished font height inside text input."));
}
if (imgui_size < FontManager::min_imgui_font_size) {
if (imgui_size < FontManager::min_imgui_font_size)
append_warning(_u8L("To small"),
_u8L("Enlarged font height inside text input."));
}
if (!who.empty()) {
if (!who.empty())
warning = GUI::format(_u8L("%1% is NOT shown."), who);
}
}
if (!warning.empty()) {
if (ImGui::IsItemHovered() && !tool_tip.empty())
ImGui::SetTooltip("%s", tool_tip.c_str());
ImVec2 cursor = ImGui::GetCursorPos();
float width = ImGui::GetContentRegionAvailWidth();
ImVec2 size = ImGui::CalcTextSize(warning.c_str());
@ -1006,14 +1025,17 @@ void GLGizmoEmboss::draw_text_input()
ImGui::SetCursorPos(ImVec2(width - size.x + padding.x,
cursor.y - size.y - padding.y));
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, warning);
if (ImGui::IsItemHovered() && !tool_tip.empty())
ImGui::SetTooltip("%s", tool_tip.c_str());
ImGui::SetCursorPos(cursor);
}
// IMPROVE: only extend not clear
// Extend font ranges
// imgui_font has to be unused
if (exist_change) m_font_manager.clear_imgui_font();
if (!range_text.empty() &&
!m_imgui->contain_all_glyphs(imgui_font, range_text) ) {
m_font_manager.clear_imgui_font();
m_font_manager.create_imgui_font(range_text);
}
}
//#define DEBUG_NOT_LOADABLE_FONTS
@ -1057,8 +1079,8 @@ protected:
/*/
// Slow copy of font files to try load font
// After this all files are loadable
auto ff = WxFontUtils::create_font_file(wx_font);
if (ff == nullptr) {
auto font_file = WxFontUtils::create_font_file(wx_font);
if (font_file == nullptr) {
#ifdef DEBUG_NOT_LOADABLE_FONTS
m_efacenames.emplace_back(facename.c_str());
#endif // DEBUG_NOT_LOADABLE_FONTS
@ -1802,16 +1824,19 @@ void GLGizmoEmboss::draw_advanced()
}
FontProp &font_prop = m_font_manager.get_font_item().prop;
const auto &cn = m_font_manager.get_font_prop().collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
const auto &font_info = font_file->infos[font_index];
#ifdef SHOW_FONT_FILE_PROPERTY
ImGui::SameLine();
auto& ff = m_font_manager.get_font().font_file_with_cache;
int cache_size = ff.has_value()? (int)ff.cache->size() : 0;
int cache_size = ff.has_value()? (int)ff.cache->size() : 0;
std::string ff_property =
"ascent=" + std::to_string(font_file->ascent) +
", descent=" + std::to_string(font_file->descent) +
", lineGap=" + std::to_string(font_file->linegap) +
", unitPerEm=" + std::to_string(font_file->unit_per_em) +
"ascent=" + std::to_string(font_info.ascent) +
", descent=" + std::to_string(font_info.descent) +
", lineGap=" + std::to_string(font_info.linegap) +
", unitPerEm=" + std::to_string(font_info.unit_per_em) +
", cache(" + std::to_string(cache_size) + " glyphs)";
if (font_file->count > 1) {
unsigned int collection = font_prop.collection_number.has_value() ?
@ -1830,8 +1855,9 @@ void GLGizmoEmboss::draw_advanced()
// input gap between letters
auto def_char_gap = m_stored_font_item.has_value() ?
&m_stored_font_item->prop.char_gap : nullptr;
int min_char_gap = -font_file->ascent / 2,
max_char_gap = font_file->ascent / 2;
int half_ascent = font_info.ascent / 2;
int min_char_gap = -half_ascent, max_char_gap = half_ascent;
if (rev_slider(tr.char_gap, font_prop.char_gap, def_char_gap, _u8L("Revert gap between letters"),
min_char_gap, max_char_gap, units_fmt, _L("Distance between letters"))){
// char gap is stored inside of imgui font atlas
@ -1842,8 +1868,7 @@ void GLGizmoEmboss::draw_advanced()
// input gap between lines
auto def_line_gap = m_stored_font_item.has_value() ?
&m_stored_font_item->prop.line_gap : nullptr;
int min_line_gap = -font_file->ascent / 2,
max_line_gap = font_file->ascent / 2;
int min_line_gap = -half_ascent, max_line_gap = half_ascent;
if (rev_slider(tr.line_gap, font_prop.line_gap, def_line_gap, _u8L("Revert gap between lines"),
min_line_gap, max_line_gap, units_fmt, _L("Distance between lines"))){
// char gap is stored inside of imgui font atlas

View File

@ -65,6 +65,7 @@ protected:
void on_disable_grabber(unsigned int id) override { m_rotate_gizmo.disable_grabber(); }
void on_start_dragging() override;
void on_stop_dragging() override;
void on_dragging(const UpdateData &data) override;
/// <summary>
/// Rotate by text on dragging rotate grabers
@ -217,7 +218,10 @@ private:
void fill_stored_font_items();
void select_stored_font_item();
// Text to emboss
std::string m_text;
// True when m_text contain character unknown by selected font
bool m_text_contain_unknown_glyph = false;
// cancel for previous update of volume to cancel finalize part
std::shared_ptr<std::atomic<bool>> m_update_job_cancel;

View File

@ -1460,6 +1460,44 @@ void ImGuiWrapper::draw(
}
}
bool ImGuiWrapper::contain_all_glyphs(const ImFont *font,
const std::string &text)
{
if (font == nullptr) return false;
if (!font->IsLoaded()) return false;
const ImFontConfig *fc = font->ConfigData;
if (fc == nullptr) return false;
if (text.empty()) return true;
return is_chars_in_ranges(fc->GlyphRanges, text.c_str());
}
bool ImGuiWrapper::is_char_in_ranges(const ImWchar *ranges,
unsigned int letter)
{
for (const ImWchar *range = ranges; range[0] && range[1]; range += 2) {
ImWchar from = range[0];
ImWchar to = range[1];
if (from <= letter && letter <= to) return true;
if (letter < to) return false; // ranges should be sorted
}
return false;
};
bool ImGuiWrapper::is_chars_in_ranges(const ImWchar *ranges,
const char *chars_ptr)
{
while (*chars_ptr) {
unsigned int c = 0;
// UTF-8 to 32-bit character need imgui_internal
int c_len = ImTextCharFromUtf8(&c, chars_ptr, NULL);
chars_ptr += c_len;
if (c_len == 0) break;
if (!is_char_in_ranges(ranges, c)) return false;
}
return true;
}
#ifdef __APPLE__
static const ImWchar ranges_keyboard_shortcuts[] =
{

View File

@ -194,6 +194,17 @@ public:
ImU32 color = ImGui::GetColorU32(COL_ORANGE_LIGHT),
float thickness = 3.f);
/// <summary>
/// Check that font ranges contain all chars in string
/// (rendered Unicodes are stored in GlyphRanges)
/// </summary>
/// <param name="font">Contain glyph ranges</param>
/// <param name="text">Vector of character to check</param>
/// <returns>True when all glyphs from text are in font ranges</returns>
static bool contain_all_glyphs(const ImFont *font, const std::string &text);
static bool is_chars_in_ranges(const ImWchar *ranges, const char *chars_ptr);
static bool is_char_in_ranges(const ImWchar *ranges, unsigned int letter);
bool requires_extra_frame() const { return m_requires_extra_frame; }
void set_requires_extra_frame() { m_requires_extra_frame = true; }
void reset_requires_extra_frame() { m_requires_extra_frame = false; }

View File

@ -46,7 +46,9 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
// dot per inch for monitor
int dpi = get_dpi_for_window(mf);
double ppm = dpi / 25.4; // pixel per milimeter
double unit_per_em = item.font.font_file->unit_per_em;
const auto &cn = item.prop.collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
double unit_per_em = item.font.font_file->infos[font_index].unit_per_em;
double scale = item.prop.size_in_mm / unit_per_em * Emboss::SHAPE_SCALE * ppm;
scales[index] = scale;

View File

@ -312,7 +312,9 @@ TriangleMesh priv::create_mesh(const char *text,
if (shapes.empty()) return {};
if (was_canceled()) return {};
int unit_per_em = font.font_file->unit_per_em;
const auto &cn = font_prop.collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
int unit_per_em = font.font_file->infos[font_index].unit_per_em;
float scale = font_prop.size_in_mm / unit_per_em;
float depth = font_prop.emboss / scale;
auto projectZ = std::make_unique<Emboss::ProjectZ>(depth);

View File

@ -7,6 +7,7 @@
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
#include "slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp"
#include "slic3r/GUI/ImGuiWrapper.hpp" // check of font ranges
using namespace Slic3r;
using namespace Slic3r::GUI;
@ -223,40 +224,41 @@ void FontManager::clear_imgui_font() {
// TODO: improove to clear only actual font
if (!is_activ_font()) return;
free_imgui_fonts();
return;
return;
ImFont *imgui_font = get_imgui_font(m_font_selected);
m_font_list[m_font_selected].imgui_font_index.reset();
if (imgui_font != nullptr) IM_DELETE(imgui_font);
}
ImFont *FontManager::get_imgui_font(const std::string &text)
ImFont *FontManager::get_imgui_font()
{
if (!is_activ_font()) return nullptr;
return get_imgui_font(m_font_selected, text);
return get_imgui_font(m_font_selected);
}
ImFont *FontManager::get_imgui_font(size_t item_index, const std::string &text)
{
ImFont *FontManager::create_imgui_font(const std::string &text)
{
return create_imgui_font(m_font_selected, text);
}
ImFont *FontManager::get_imgui_font(size_t item_index)
{
if (!is_activ_font()) return nullptr;
Item &item = m_font_list[item_index];
// check is already loaded
if (!item.imgui_font_index.has_value())
return load_imgui_font(item_index, text);
if (!item.imgui_font_index.has_value()) return nullptr;
size_t index = *item.imgui_font_index;
auto & fonts = m_imgui_font_atlas.Fonts;
size_t index = *item.imgui_font_index;
ImVector<ImFont *> &fonts = m_imgui_font_atlas.Fonts;
// check correct index
int f_size = fonts.size();
assert(f_size > 0 && index < (size_t)f_size);
assert(f_size > 0 && index < (size_t) f_size);
if (f_size <= 0 || index >= (size_t) f_size) return nullptr;
ImFont *font = fonts[index];
if (font == nullptr) return nullptr;
if (!font->IsLoaded()) return nullptr;
if (font->Scale <= 0.f) return nullptr;
// automatic extend range
if (!text.empty() && !is_text_in_ranges(font, text))
return extend_imgui_font_range(item_index, text);
return font;
}
@ -327,51 +329,17 @@ bool FontManager::set_up_font_file(size_t item_index)
return set_wx_font(item_index, *item.wx_font);
}
bool FontManager::is_text_in_ranges(const ImFont *font, const std::string &text)
{
if (font == nullptr) return false;
if (!font->IsLoaded()) return false;
const ImFontConfig *fc = font->ConfigData;
if (fc == nullptr) return false;
return is_text_in_ranges(fc->GlyphRanges, text);
}
bool FontManager::is_char_in_ranges(const ImWchar *ranges, unsigned int letter)
{
for (const ImWchar *range = ranges; range[0] && range[1]; range += 2) {
ImWchar from = range[0];
ImWchar to = range[1];
if (from <= letter && letter <= to) return true;
if (letter < to) return false; // ranges should be sorted
}
return false;
};
bool FontManager::is_text_in_ranges(const ImWchar *ranges, const std::string &text)
{
const char *text_char_ptr = text.c_str();
while (*text_char_ptr) {
unsigned int c = 0;
// UTF-8 to 32-bit character need imgui_internal
int c_len = ImTextCharFromUtf8(&c, text_char_ptr, NULL);
text_char_ptr += c_len;
if (c_len == 0) break;
if (!is_char_in_ranges(ranges, c)) return false;
}
return true;
}
ImFont* FontManager::extend_imgui_font_range(size_t index, const std::string& text)
{
auto &font_index_opt = m_font_list[m_font_selected].imgui_font_index;
if (!font_index_opt.has_value())
return load_imgui_font(index, text);
return create_imgui_font(index, text);
// TODO: start using merge mode
// ImFontConfig::MergeMode = true;
free_imgui_fonts();
return load_imgui_font(index, text);
return create_imgui_font(index, text);
}
#include "slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp"
@ -460,15 +428,19 @@ float FontManager::max_imgui_font_size = 60.f;
float FontManager::get_imgui_font_size(const FontProp &prop,
const Emboss::FontFile &file)
{
const auto &cn = prop.collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
const auto &font_info = file.infos[font_index];
// coeficient for convert line height to font size
float c1 = (file.ascent - file.descent + file.linegap) / (float) file.unit_per_em;
float c1 = (font_info.ascent - font_info.descent + font_info.linegap) /
(float) font_info.unit_per_em;
// The point size is defined as 1/72 of the Anglo-Saxon inch (25.4 mm):
// It is approximately 0.0139 inch or 352.8 um.
return c1 * std::abs(prop.size_in_mm) / 0.3528f;
}
ImFont * FontManager::load_imgui_font(size_t index, const std::string &text)
ImFont *FontManager::create_imgui_font(size_t index, const std::string &text)
{
free_imgui_fonts(); // TODO: remove it after correct initialization
@ -499,19 +471,23 @@ ImFont * FontManager::load_imgui_font(size_t index, const std::string &text)
ImFontConfig font_config;
// TODO: start using merge mode
//font_config.MergeMode = true;
const auto &cn = font_prop.collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
const auto &font_info = font_file.infos[font_index];
if (font_prop.char_gap.has_value()) {
float coef = font_size / (double) font_file.unit_per_em;
float coef = font_size / (double) font_info.unit_per_em;
font_config.GlyphExtraSpacing.x = coef * (*font_prop.char_gap);
}
if (font_prop.line_gap.has_value()) {
float coef = font_size / (double) font_file.unit_per_em;
float coef = font_size / (double) font_info.unit_per_em;
font_config.GlyphExtraSpacing.y = coef * (*font_prop.line_gap);
}
font_config.FontDataOwnedByAtlas = false;
const std::vector<unsigned char> &buffer = *font_file.data;
m_imgui_font_atlas.AddFontFromMemoryTTF(
ImFont * font = m_imgui_font_atlas.AddFontFromMemoryTTF(
(void *) buffer.data(), buffer.size(), font_size, &font_config, item.font_ranges.Data);
unsigned char *pixels;
@ -538,8 +514,10 @@ ImFont * FontManager::load_imgui_font(size_t index, const std::string &text)
m_imgui_font_atlas.TexID = (ImTextureID) (intptr_t) font_texture;
assert(!m_imgui_font_atlas.Fonts.empty());
if (m_imgui_font_atlas.Fonts.empty()) return nullptr;
assert(font == m_imgui_font_atlas.Fonts.back());
item.imgui_font_index = m_imgui_font_atlas.Fonts.size() - 1;
return m_imgui_font_atlas.Fonts.back();
assert(font->IsLoaded());
return font;
}
bool FontManager::set_wx_font(size_t item_index, const wxFont &wx_font) {

View File

@ -99,7 +99,9 @@ public:
// Getter on acitve font pointer for imgui
// Initialize imgui font(generate texture) when doesn't exist yet.
// Extend font atlas when not in glyph range
ImFont *get_imgui_font(const std::string &text);
ImFont *get_imgui_font();
// initialize font range by unique symbols in text
ImFont *create_imgui_font(const std::string& text);
// free used memory and font file data
void free_except_active_font();
@ -166,7 +168,7 @@ private:
void duplicate(size_t index);
// load actual selected font
ImFont *load_imgui_font(size_t index, const std::string &text);
ImFont *create_imgui_font(size_t index, const std::string &text);
bool load_active_font();
@ -174,18 +176,13 @@ private:
// getter on index selected font pointer for imgui
// text could extend font atlas when not in glyph range
ImFont *get_imgui_font(size_t item_index, const std::string &text = "");
ImFont *get_imgui_font(size_t item_index);
// extend actual imgui font when exist unknown char in text
// NOTE: imgui_font has to be unused
// return true when extend range otherwise FALSE
ImFont *extend_imgui_font_range(size_t font_index, const std::string &text);
// Move to imgui utils
static bool is_text_in_ranges(const ImFont *font, const std::string &text);
static bool is_text_in_ranges(const ImWchar *ranges, const std::string &text);
static bool is_char_in_ranges(const ImWchar *ranges, unsigned int letter);
void free_imgui_fonts();
bool set_up_font_file(size_t item_index);

View File

@ -136,13 +136,15 @@ TEST_CASE("Read glyph C shape from font, stb library calls ONLY", "[Emboss]") {
TEST_CASE("Convert glyph % to model", "[Emboss]")
{
std::string font_path = get_font_filepath();
unsigned int font_index = 0; // collection
char letter = '%';
float flatness = 2.;
auto font = Emboss::create_font_file(font_path.c_str());
REQUIRE(font != nullptr);
std::optional<Emboss::Glyph> glyph = Emboss::letter2glyph(*font, letter, flatness);
std::optional<Emboss::Glyph> glyph =
Emboss::letter2glyph(*font, font_index, letter, flatness);
REQUIRE(glyph.has_value());
ExPolygons shape = glyph->shape;
@ -301,12 +303,13 @@ TEST_CASE("Cut surface", "[]")
std::string font_path = get_font_filepath();
char letter = '%';
float flatness = 2.;
unsigned int font_index = 0; // collection
auto font = Emboss::create_font_file(font_path.c_str());
REQUIRE(font != nullptr);
std::optional<Emboss::Glyph> glyph = Emboss::letter2glyph(*font, letter,
flatness);
std::optional<Emboss::Glyph> glyph =
Emboss::letter2glyph(*font, font_index, letter, flatness);
REQUIRE(glyph.has_value());
ExPolygons shape = glyph->shape;
@ -586,14 +589,15 @@ using MyMesh = Slic3r::MeshBoolean::cgal2::CGALMesh;
TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
{
std::string font_path = get_font_filepath();
unsigned int font_index = 0; // collection
char letter = '%';
float flatness = 2.;
auto font = Emboss::create_font_file(font_path.c_str());
REQUIRE(font != nullptr);
std::optional<Emboss::Glyph> glyph = Emboss::letter2glyph(*font, letter,
flatness);
std::optional<Emboss::Glyph> glyph =
Emboss::letter2glyph(*font, font_index, letter, flatness);
REQUIRE(glyph.has_value());
ExPolygons shape = glyph->shape;