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:
parent
68c34210d2
commit
331d4d4557
11 changed files with 329 additions and 183 deletions
|
@ -22,6 +22,7 @@ class Private
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Private() = delete;
|
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<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);
|
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);
|
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(
|
std::optional<stbtt_fontinfo> Private::load_font_info(
|
||||||
const unsigned char *data, unsigned int index)
|
const unsigned char *data, unsigned int index)
|
||||||
{
|
{
|
||||||
int font_offset = stbtt_GetFontOffsetForIndex(data, index);
|
int font_offset = stbtt_GetFontOffsetForIndex(data, index);
|
||||||
if (font_offset < 0) {
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
stbtt_fontinfo font_info;
|
stbtt_fontinfo font_info;
|
||||||
if (stbtt_InitFont(&font_info, data, font_offset) == 0) {
|
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 {};
|
||||||
}
|
}
|
||||||
return font_info;
|
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);
|
int glyph_index = stbtt_FindGlyphIndex(&font_info, unicode_letter);
|
||||||
if (glyph_index == 0) {
|
if (glyph_index == 0) {
|
||||||
wchar_t wchar = static_cast<wchar_t>(unicode_letter);
|
//wchar_t wchar = static_cast<wchar_t>(unicode_letter);
|
||||||
BOOST_LOG_TRIVIAL(error) << "Character unicode letter ("
|
//<< "Character unicode letter ("
|
||||||
<< "decimal value = " << std::dec << unicode_letter << ", "
|
//<< "decimal value = " << std::dec << unicode_letter << ", "
|
||||||
<< "hexadecimal value = U+" << std::hex << unicode_letter << std::dec << ", "
|
//<< "hexadecimal value = U+" << std::hex << unicode_letter << std::dec << ", "
|
||||||
<< "wchar value = " << wchar
|
//<< "wchar value = " << wchar
|
||||||
<< ") is NOT defined inside of the font. \n";
|
//<< ") is NOT defined inside of the font. \n";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,17 +147,19 @@ const Emboss::Glyph* Private::get_glyph(
|
||||||
const double RESOLUTION = 0.0125; // TODO: read from printer configuration
|
const double RESOLUTION = 0.0125; // TODO: read from printer configuration
|
||||||
auto glyph_item = cache.find(unicode);
|
auto glyph_item = cache.find(unicode);
|
||||||
if (glyph_item != cache.end()) return &glyph_item->second;
|
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;
|
*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);
|
font_info_opt = Private::load_font_info(font.data->data(), font_index);
|
||||||
// can load font info?
|
// can load font info?
|
||||||
if (!font_info_opt.has_value()) return nullptr;
|
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 =
|
std::optional<Emboss::Glyph> glyph_opt =
|
||||||
Private::get_glyph(*font_info_opt, unicode, flatness);
|
Private::get_glyph(*font_info_opt, unicode, flatness);
|
||||||
|
|
||||||
|
@ -350,18 +363,19 @@ FontList Emboss::get_font_list_by_register() {
|
||||||
|
|
||||||
// Open Windows font registry key
|
// Open Windows font registry key
|
||||||
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, fontRegistryPath, 0, KEY_READ, &hKey);
|
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, fontRegistryPath, 0, KEY_READ, &hKey);
|
||||||
if (result != ERROR_SUCCESS) {
|
if (result != ERROR_SUCCESS) {
|
||||||
std::wcerr << L"Can not Open register key (" << fontRegistryPath << ")"
|
assert(false);
|
||||||
<< L", function 'RegOpenKeyEx' return code: " << result << std::endl;
|
//std::wcerr << L"Can not Open register key (" << fontRegistryPath << ")"
|
||||||
|
// << L", function 'RegOpenKeyEx' return code: " << result << std::endl;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD maxValueNameSize, maxValueDataSize;
|
DWORD maxValueNameSize, maxValueDataSize;
|
||||||
result = RegQueryInfoKey(hKey, 0, 0, 0, 0, 0, 0, 0, &maxValueNameSize,
|
result = RegQueryInfoKey(hKey, 0, 0, 0, 0, 0, 0, 0, &maxValueNameSize,
|
||||||
&maxValueDataSize, 0, 0);
|
&maxValueDataSize, 0, 0);
|
||||||
if (result != ERROR_SUCCESS) {
|
if (result != ERROR_SUCCESS) {
|
||||||
BOOST_LOG_TRIVIAL(error) << "Can not earn query key, function 'RegQueryInfoKey' return code: "
|
assert(false);
|
||||||
<< result << std::endl;
|
// Can not earn query key, function 'RegQueryInfoKey' return code: result
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,47 +489,61 @@ std::unique_ptr<Emboss::FontFile> Emboss::create_font_file(
|
||||||
int collection_size = stbtt_GetNumberOfFonts(data->data());
|
int collection_size = stbtt_GetNumberOfFonts(data->data());
|
||||||
// at least one font must be inside collection
|
// at least one font must be inside collection
|
||||||
if (collection_size < 1) {
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto font_info = Private::load_font_info(data->data());
|
|
||||||
if (!font_info.has_value()) return nullptr;
|
|
||||||
|
|
||||||
const stbtt_fontinfo *info = &(*font_info);
|
unsigned int c_size = static_cast<unsigned int>(collection_size);
|
||||||
// load information about line gap
|
std::vector<FontFile::Info> infos;
|
||||||
int ascent, descent, linegap;
|
infos.reserve(c_size);
|
||||||
stbtt_GetFontVMetrics(info, &ascent, &descent, &linegap);
|
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>(
|
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)
|
std::unique_ptr<Emboss::FontFile> Emboss::create_font_file(const char *file_path)
|
||||||
{
|
{
|
||||||
FILE *file = fopen(file_path, "rb");
|
FILE *file = fopen(file_path, "rb");
|
||||||
if (file == nullptr) {
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find size of file
|
// find size of file
|
||||||
if (fseek(file, 0L, SEEK_END) != 0) {
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
size_t size = ftell(file);
|
size_t size = ftell(file);
|
||||||
if (size == 0) {
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
rewind(file);
|
rewind(file);
|
||||||
auto buffer = std::make_unique<std::vector<unsigned char>>(size);
|
auto buffer = std::make_unique<std::vector<unsigned char>>(size);
|
||||||
size_t count_loaded_bytes = fread((void *) &buffer->front(), 1, size, file);
|
size_t count_loaded_bytes = fread((void *) &buffer->front(), 1, size, file);
|
||||||
if (count_loaded_bytes != size) {
|
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 nullptr;
|
||||||
}
|
}
|
||||||
return create_font_file(std::move(buffer));
|
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);
|
HDC hdc = ::CreateCompatibleDC(NULL);
|
||||||
if (hdc == 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;
|
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);
|
size_t loaded_size = ::GetFontData(hdc, dwTable, dwOffset, buffer->data(), size);
|
||||||
::DeleteDC(hdc);
|
::DeleteDC(hdc);
|
||||||
if (size != loaded_size) {
|
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 nullptr;
|
||||||
}
|
}
|
||||||
return create_font_file(std::move(buffer));
|
return create_font_file(std::move(buffer));
|
||||||
|
@ -585,10 +615,12 @@ std::unique_ptr<Emboss::FontFile> Emboss::create_font_file(HFONT hfont)
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
std::optional<Emboss::Glyph> Emboss::letter2glyph(const FontFile &font,
|
std::optional<Emboss::Glyph> Emboss::letter2glyph(const FontFile &font,
|
||||||
|
unsigned int font_index,
|
||||||
int letter,
|
int letter,
|
||||||
float flatness)
|
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 {};
|
if (!font_info_opt.has_value()) return {};
|
||||||
return Private::get_glyph(*font_info_opt, letter, flatness);
|
return Private::get_glyph(*font_info_opt, letter, flatness);
|
||||||
}
|
}
|
||||||
|
@ -603,11 +635,15 @@ ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache,
|
||||||
Point cursor(0, 0);
|
Point cursor(0, 0);
|
||||||
ExPolygons result;
|
ExPolygons result;
|
||||||
const FontFile& font = *font_with_cache.font_file;
|
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;
|
Emboss::Glyphs& cache = *font_with_cache.cache;
|
||||||
std::wstring ws = boost::nowide::widen(text);
|
std::wstring ws = boost::nowide::widen(text);
|
||||||
for (wchar_t wc: ws){
|
for (wchar_t wc: ws){
|
||||||
if (wc == '\n') {
|
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())
|
if (font_prop.line_gap.has_value())
|
||||||
line_height += *font_prop.line_gap;
|
line_height += *font_prop.line_gap;
|
||||||
line_height = static_cast<int>(line_height / SHAPE_SCALE);
|
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;
|
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,
|
indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
|
||||||
const IProject &projection)
|
const IProject &projection)
|
||||||
{
|
{
|
||||||
|
|
|
@ -71,32 +71,39 @@ public:
|
||||||
// count of fonts when data are collection of fonts
|
// count of fonts when data are collection of fonts
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
|
|
||||||
// vertical position is "scale*(ascent - descent + lineGap)"
|
struct Info
|
||||||
int ascent, descent, linegap;
|
{
|
||||||
|
// vertical position is "scale*(ascent - descent + lineGap)"
|
||||||
|
int ascent, descent, linegap;
|
||||||
|
|
||||||
// for convert font units to pixel
|
// for convert font units to pixel
|
||||||
int unit_per_em;
|
int unit_per_em;
|
||||||
|
};
|
||||||
|
// info for each font in data
|
||||||
|
std::vector<Info> infos;
|
||||||
|
|
||||||
FontFile(std::unique_ptr<std::vector<unsigned char>> data,
|
FontFile(std::unique_ptr<std::vector<unsigned char>> data,
|
||||||
unsigned int count,
|
unsigned int count,
|
||||||
int ascent,
|
std::vector<Info> &&infos)
|
||||||
int descent,
|
|
||||||
int linegap,
|
|
||||||
int unit_per_em)
|
|
||||||
: data(std::move(data))
|
: data(std::move(data))
|
||||||
, count(count)
|
, count(count)
|
||||||
, ascent(ascent)
|
, infos(std::move(infos))
|
||||||
, descent(descent)
|
|
||||||
, linegap(linegap)
|
|
||||||
, unit_per_em(unit_per_em)
|
|
||||||
{
|
{
|
||||||
assert(this->data != nullptr);
|
assert(this->data != nullptr);
|
||||||
|
assert(!this->data->empty());
|
||||||
|
assert(count == this->infos.size());
|
||||||
}
|
}
|
||||||
bool operator==(const FontFile &other) const {
|
bool operator==(const FontFile &other) const {
|
||||||
return count == other.count && ascent == other.ascent &&
|
if (count != other.count || data->size() != other.data->size())
|
||||||
descent == other.descent && linegap == other.linegap &&
|
return false;
|
||||||
data->size() == other.data->size();
|
//if(*data != *other.data) return false;
|
||||||
//&& *data == *other.data;
|
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
|
/// convert letter into polygons
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="font">Define fonts</param>
|
/// <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="letter">One character defined by unicode codepoint</param>
|
||||||
/// <param name="flatness">Precision of lettter outline curve in conversion to lines</param>
|
/// <param name="flatness">Precision of lettter outline curve in conversion to lines</param>
|
||||||
/// <returns>inner polygon cw(outer ccw)</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Convert text into polygons
|
/// Convert text into polygons
|
||||||
|
@ -152,9 +160,7 @@ public:
|
||||||
/// <param name="text">Characters to convert</param>
|
/// <param name="text">Characters to convert</param>
|
||||||
/// <param name="font_prop">User defined property of the font</param>
|
/// <param name="font_prop">User defined property of the font</param>
|
||||||
/// <returns>Inner polygon cw(outer ccw)</returns>
|
/// <returns>Inner polygon cw(outer ccw)</returns>
|
||||||
static ExPolygons text2shapes(FontFileWithCache &font,
|
static ExPolygons text2shapes(FontFileWithCache &font, const char *text, const FontProp &font_prop);
|
||||||
const char * text,
|
|
||||||
const FontProp &font_prop);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use data from font property to modify transformation
|
/// Use data from font property to modify transformation
|
||||||
|
@ -162,8 +168,7 @@ public:
|
||||||
/// <param name="font_prop">Z-move as surface distance(FontProp::distance)
|
/// <param name="font_prop">Z-move as surface distance(FontProp::distance)
|
||||||
/// Z-rotation as angle to Y axis(FontProp::angle)</param>
|
/// Z-rotation as angle to Y axis(FontProp::angle)</param>
|
||||||
/// <param name="transformation">In / Out transformation to modify by property</param>
|
/// <param name="transformation">In / Out transformation to modify by property</param>
|
||||||
static void apply_transformation(const FontProp &font_prop,
|
static void apply_transformation(const FontProp &font_prop, Transform3d &transformation);
|
||||||
Transform3d &transformation);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read information from naming table of font file
|
/// 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>
|
/// <returns>True when the font description contains italic/obligue otherwise False</returns>
|
||||||
static bool is_italic(const FontFile &font, unsigned int font_index);
|
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>
|
/// <summary>
|
||||||
/// Project 2d point into space
|
/// Project 2d point into space
|
||||||
/// Could be plane, sphere, cylindric, ...
|
/// Could be plane, sphere, cylindric, ...
|
||||||
|
|
|
@ -186,10 +186,8 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
|
||||||
{
|
{
|
||||||
if (mouse_event.Moving()) return false;
|
if (mouse_event.Moving()) return false;
|
||||||
|
|
||||||
m_rotate_gizmo.on_mouse(mouse_event);
|
bool used = use_grabbers(mouse_event);
|
||||||
use_grabbers(mouse_event);
|
if (!m_dragging) return used;
|
||||||
|
|
||||||
if (!m_dragging) return false;
|
|
||||||
|
|
||||||
assert(m_volume != nullptr);
|
assert(m_volume != nullptr);
|
||||||
assert(m_volume->text_configuration.has_value());
|
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()) {
|
if (m_font_manager.is_activ_font()) {
|
||||||
m_font_manager.get_font_prop().angle = angle_opt;
|
m_font_manager.get_font_prop().angle = angle_opt;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
} else if (mouse_event.LeftUp()) {
|
} else if (mouse_event.LeftUp()) {
|
||||||
// apply rotation
|
// apply rotation
|
||||||
m_parent.do_rotate(L("Text-Rotate"));
|
m_parent.do_rotate(L("Text-Rotate"));
|
||||||
start_angle.reset();
|
start_angle.reset();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return used;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
|
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)
|
// When fixing, move grabber above text (not on side)
|
||||||
m_rotate_gizmo.set_angle(0);
|
m_rotate_gizmo.set_angle(0);
|
||||||
}
|
}
|
||||||
|
void GLGizmoEmboss::on_dragging(const UpdateData &data) { m_rotate_gizmo.dragging(data); }
|
||||||
|
|
||||||
void GLGizmoEmboss::initialize()
|
void GLGizmoEmboss::initialize()
|
||||||
{
|
{
|
||||||
|
@ -768,6 +765,24 @@ ModelVolume *GLGizmoEmboss::get_selected_volume(const Selection &selection,
|
||||||
return get_model_volume(vol_gl, objects);
|
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()
|
bool GLGizmoEmboss::process()
|
||||||
{
|
{
|
||||||
// no volume is selected -> selection from right panel
|
// no volume is selected -> selection from right panel
|
||||||
|
@ -795,17 +810,8 @@ bool GLGizmoEmboss::process()
|
||||||
//*
|
//*
|
||||||
auto &worker = wxGetApp().plater()->get_ui_job_worker();
|
auto &worker = wxGetApp().plater()->get_ui_job_worker();
|
||||||
queue_job(worker, std::make_unique<EmbossUpdateJob>(std::move(data)));
|
queue_job(worker, std::make_unique<EmbossUpdateJob>(std::move(data)));
|
||||||
/*/
|
/*/ // Run Job on main thread (blocking) - ONLY DEBUG
|
||||||
// Run Job on main thread (blocking)
|
execute_job(std::make_shared<EmbossUpdateJob>(std::move(data)));
|
||||||
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);
|
|
||||||
// */
|
// */
|
||||||
|
|
||||||
// notification is removed befor object is changed by job
|
// notification is removed befor object is changed by job
|
||||||
|
@ -932,15 +938,28 @@ void GLGizmoEmboss::draw_window()
|
||||||
|
|
||||||
void GLGizmoEmboss::draw_text_input()
|
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 =
|
static const ImGuiInputTextFlags flags =
|
||||||
ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_AutoSelectAll;
|
ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_AutoSelectAll;
|
||||||
|
|
||||||
ImFont *imgui_font = (m_font_manager.is_activ_font())?
|
ImFont *imgui_font = m_font_manager.get_imgui_font();
|
||||||
m_font_manager.get_imgui_font(m_text) : nullptr;
|
if (imgui_font == nullptr) {
|
||||||
bool exist_font = imgui_font != nullptr && imgui_font->IsLoaded();
|
// 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);
|
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 window_height = ImGui::GetWindowHeight();
|
||||||
float minimal_height = get_minimal_window_size().y;
|
float minimal_height = get_minimal_window_size().y;
|
||||||
float extra_height = window_height - minimal_height;
|
float extra_height = window_height - minimal_height;
|
||||||
|
@ -948,7 +967,7 @@ void GLGizmoEmboss::draw_text_input()
|
||||||
m_gui_cfg->text_size.y + extra_height);
|
m_gui_cfg->text_size.y + extra_height);
|
||||||
if (ImGui::InputTextMultiline("##Text", &m_text, text_size, flags)) {
|
if (ImGui::InputTextMultiline("##Text", &m_text, text_size, flags)) {
|
||||||
process();
|
process();
|
||||||
exist_change = true;
|
range_text = create_range_text();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exist_font) ImGui::PopFont();
|
if (exist_font) ImGui::PopFont();
|
||||||
|
@ -971,34 +990,34 @@ void GLGizmoEmboss::draw_text_input()
|
||||||
tool_tip += t;
|
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();
|
const FontProp &prop = m_font_manager.get_font_prop();
|
||||||
if (prop.skew.has_value()) {
|
if (prop.skew.has_value())
|
||||||
append_warning(_u8L("Skew"),
|
append_warning(_u8L("Skew"),
|
||||||
_u8L("Unsupported visualization of font skew for text input."));
|
_u8L("Unsupported visualization of font skew for text input."));
|
||||||
}
|
if (prop.boldness.has_value())
|
||||||
if (prop.boldness.has_value()) {
|
|
||||||
append_warning(_u8L("Boldness"),
|
append_warning(_u8L("Boldness"),
|
||||||
_u8L("Unsupported visualization of font boldness for text input."));
|
_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"),
|
append_warning(_u8L("Line gap"),
|
||||||
_u8L("Unsupported visualization of gap between lines inside text input."));
|
_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());
|
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"),
|
append_warning(_u8L("To tall"),
|
||||||
_u8L("Diminished font height inside text input."));
|
_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"),
|
append_warning(_u8L("To small"),
|
||||||
_u8L("Enlarged font height inside text input."));
|
_u8L("Enlarged font height inside text input."));
|
||||||
}
|
if (!who.empty())
|
||||||
if (!who.empty()) {
|
|
||||||
warning = GUI::format(_u8L("%1% is NOT shown."), who);
|
warning = GUI::format(_u8L("%1% is NOT shown."), who);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!warning.empty()) {
|
if (!warning.empty()) {
|
||||||
|
if (ImGui::IsItemHovered() && !tool_tip.empty())
|
||||||
|
ImGui::SetTooltip("%s", tool_tip.c_str());
|
||||||
ImVec2 cursor = ImGui::GetCursorPos();
|
ImVec2 cursor = ImGui::GetCursorPos();
|
||||||
float width = ImGui::GetContentRegionAvailWidth();
|
float width = ImGui::GetContentRegionAvailWidth();
|
||||||
ImVec2 size = ImGui::CalcTextSize(warning.c_str());
|
ImVec2 size = ImGui::CalcTextSize(warning.c_str());
|
||||||
|
@ -1006,14 +1025,17 @@ void GLGizmoEmboss::draw_text_input()
|
||||||
ImGui::SetCursorPos(ImVec2(width - size.x + padding.x,
|
ImGui::SetCursorPos(ImVec2(width - size.x + padding.x,
|
||||||
cursor.y - size.y - padding.y));
|
cursor.y - size.y - padding.y));
|
||||||
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, warning);
|
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);
|
ImGui::SetCursorPos(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IMPROVE: only extend not clear
|
||||||
// Extend font ranges
|
// Extend font ranges
|
||||||
// imgui_font has to be unused
|
// 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
|
//#define DEBUG_NOT_LOADABLE_FONTS
|
||||||
|
@ -1057,8 +1079,8 @@ protected:
|
||||||
/*/
|
/*/
|
||||||
// Slow copy of font files to try load font
|
// Slow copy of font files to try load font
|
||||||
// After this all files are loadable
|
// After this all files are loadable
|
||||||
auto ff = WxFontUtils::create_font_file(wx_font);
|
auto font_file = WxFontUtils::create_font_file(wx_font);
|
||||||
if (ff == nullptr) {
|
if (font_file == nullptr) {
|
||||||
#ifdef DEBUG_NOT_LOADABLE_FONTS
|
#ifdef DEBUG_NOT_LOADABLE_FONTS
|
||||||
m_efacenames.emplace_back(facename.c_str());
|
m_efacenames.emplace_back(facename.c_str());
|
||||||
#endif // DEBUG_NOT_LOADABLE_FONTS
|
#endif // DEBUG_NOT_LOADABLE_FONTS
|
||||||
|
@ -1802,16 +1824,19 @@ void GLGizmoEmboss::draw_advanced()
|
||||||
}
|
}
|
||||||
|
|
||||||
FontProp &font_prop = m_font_manager.get_font_item().prop;
|
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
|
#ifdef SHOW_FONT_FILE_PROPERTY
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
auto& ff = m_font_manager.get_font().font_file_with_cache;
|
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 =
|
std::string ff_property =
|
||||||
"ascent=" + std::to_string(font_file->ascent) +
|
"ascent=" + std::to_string(font_info.ascent) +
|
||||||
", descent=" + std::to_string(font_file->descent) +
|
", descent=" + std::to_string(font_info.descent) +
|
||||||
", lineGap=" + std::to_string(font_file->linegap) +
|
", lineGap=" + std::to_string(font_info.linegap) +
|
||||||
", unitPerEm=" + std::to_string(font_file->unit_per_em) +
|
", unitPerEm=" + std::to_string(font_info.unit_per_em) +
|
||||||
", cache(" + std::to_string(cache_size) + " glyphs)";
|
", cache(" + std::to_string(cache_size) + " glyphs)";
|
||||||
if (font_file->count > 1) {
|
if (font_file->count > 1) {
|
||||||
unsigned int collection = font_prop.collection_number.has_value() ?
|
unsigned int collection = font_prop.collection_number.has_value() ?
|
||||||
|
@ -1830,8 +1855,9 @@ void GLGizmoEmboss::draw_advanced()
|
||||||
// input gap between letters
|
// input gap between letters
|
||||||
auto def_char_gap = m_stored_font_item.has_value() ?
|
auto def_char_gap = m_stored_font_item.has_value() ?
|
||||||
&m_stored_font_item->prop.char_gap : nullptr;
|
&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"),
|
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"))){
|
min_char_gap, max_char_gap, units_fmt, _L("Distance between letters"))){
|
||||||
// char gap is stored inside of imgui font atlas
|
// char gap is stored inside of imgui font atlas
|
||||||
|
@ -1842,8 +1868,7 @@ void GLGizmoEmboss::draw_advanced()
|
||||||
// input gap between lines
|
// input gap between lines
|
||||||
auto def_line_gap = m_stored_font_item.has_value() ?
|
auto def_line_gap = m_stored_font_item.has_value() ?
|
||||||
&m_stored_font_item->prop.line_gap : nullptr;
|
&m_stored_font_item->prop.line_gap : nullptr;
|
||||||
int min_line_gap = -font_file->ascent / 2,
|
int min_line_gap = -half_ascent, max_line_gap = half_ascent;
|
||||||
max_line_gap = font_file->ascent / 2;
|
|
||||||
if (rev_slider(tr.line_gap, font_prop.line_gap, def_line_gap, _u8L("Revert gap between lines"),
|
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"))){
|
min_line_gap, max_line_gap, units_fmt, _L("Distance between lines"))){
|
||||||
// char gap is stored inside of imgui font atlas
|
// char gap is stored inside of imgui font atlas
|
||||||
|
|
|
@ -65,6 +65,7 @@ protected:
|
||||||
void on_disable_grabber(unsigned int id) override { m_rotate_gizmo.disable_grabber(); }
|
void on_disable_grabber(unsigned int id) override { m_rotate_gizmo.disable_grabber(); }
|
||||||
void on_start_dragging() override;
|
void on_start_dragging() override;
|
||||||
void on_stop_dragging() override;
|
void on_stop_dragging() override;
|
||||||
|
void on_dragging(const UpdateData &data) override;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rotate by text on dragging rotate grabers
|
/// Rotate by text on dragging rotate grabers
|
||||||
|
@ -217,7 +218,10 @@ private:
|
||||||
void fill_stored_font_items();
|
void fill_stored_font_items();
|
||||||
void select_stored_font_item();
|
void select_stored_font_item();
|
||||||
|
|
||||||
|
// Text to emboss
|
||||||
std::string m_text;
|
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
|
// cancel for previous update of volume to cancel finalize part
|
||||||
std::shared_ptr<std::atomic<bool>> m_update_job_cancel;
|
std::shared_ptr<std::atomic<bool>> m_update_job_cancel;
|
||||||
|
|
|
@ -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__
|
#ifdef __APPLE__
|
||||||
static const ImWchar ranges_keyboard_shortcuts[] =
|
static const ImWchar ranges_keyboard_shortcuts[] =
|
||||||
{
|
{
|
||||||
|
|
|
@ -194,6 +194,17 @@ public:
|
||||||
ImU32 color = ImGui::GetColorU32(COL_ORANGE_LIGHT),
|
ImU32 color = ImGui::GetColorU32(COL_ORANGE_LIGHT),
|
||||||
float thickness = 3.f);
|
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; }
|
bool requires_extra_frame() const { return m_requires_extra_frame; }
|
||||||
void set_requires_extra_frame() { m_requires_extra_frame = true; }
|
void set_requires_extra_frame() { m_requires_extra_frame = true; }
|
||||||
void reset_requires_extra_frame() { m_requires_extra_frame = false; }
|
void reset_requires_extra_frame() { m_requires_extra_frame = false; }
|
||||||
|
|
|
@ -46,7 +46,9 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
|
||||||
// dot per inch for monitor
|
// dot per inch for monitor
|
||||||
int dpi = get_dpi_for_window(mf);
|
int dpi = get_dpi_for_window(mf);
|
||||||
double ppm = dpi / 25.4; // pixel per milimeter
|
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;
|
double scale = item.prop.size_in_mm / unit_per_em * Emboss::SHAPE_SCALE * ppm;
|
||||||
scales[index] = scale;
|
scales[index] = scale;
|
||||||
|
|
||||||
|
|
|
@ -312,7 +312,9 @@ TriangleMesh priv::create_mesh(const char *text,
|
||||||
if (shapes.empty()) return {};
|
if (shapes.empty()) return {};
|
||||||
if (was_canceled()) 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 scale = font_prop.size_in_mm / unit_per_em;
|
||||||
float depth = font_prop.emboss / scale;
|
float depth = font_prop.emboss / scale;
|
||||||
auto projectZ = std::make_unique<Emboss::ProjectZ>(depth);
|
auto projectZ = std::make_unique<Emboss::ProjectZ>(depth);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
|
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
|
||||||
#include "slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp"
|
#include "slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp"
|
||||||
|
#include "slic3r/GUI/ImGuiWrapper.hpp" // check of font ranges
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
using namespace Slic3r::GUI;
|
using namespace Slic3r::GUI;
|
||||||
|
@ -223,40 +224,41 @@ void FontManager::clear_imgui_font() {
|
||||||
// TODO: improove to clear only actual font
|
// TODO: improove to clear only actual font
|
||||||
if (!is_activ_font()) return;
|
if (!is_activ_font()) return;
|
||||||
free_imgui_fonts();
|
free_imgui_fonts();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ImFont *imgui_font = get_imgui_font(m_font_selected);
|
ImFont *imgui_font = get_imgui_font(m_font_selected);
|
||||||
m_font_list[m_font_selected].imgui_font_index.reset();
|
m_font_list[m_font_selected].imgui_font_index.reset();
|
||||||
if (imgui_font != nullptr) IM_DELETE(imgui_font);
|
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);
|
||||||
return get_imgui_font(m_font_selected, text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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];
|
Item &item = m_font_list[item_index];
|
||||||
// check is already loaded
|
// check is already loaded
|
||||||
if (!item.imgui_font_index.has_value())
|
if (!item.imgui_font_index.has_value()) return nullptr;
|
||||||
return load_imgui_font(item_index, text);
|
|
||||||
|
|
||||||
size_t index = *item.imgui_font_index;
|
size_t index = *item.imgui_font_index;
|
||||||
auto & fonts = m_imgui_font_atlas.Fonts;
|
ImVector<ImFont *> &fonts = m_imgui_font_atlas.Fonts;
|
||||||
|
|
||||||
// check correct index
|
// check correct index
|
||||||
int f_size = fonts.size();
|
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;
|
if (f_size <= 0 || index >= (size_t) f_size) return nullptr;
|
||||||
ImFont *font = fonts[index];
|
ImFont *font = fonts[index];
|
||||||
if (font == nullptr) return nullptr;
|
if (font == nullptr) return nullptr;
|
||||||
if (!font->IsLoaded()) return nullptr;
|
if (!font->IsLoaded()) return nullptr;
|
||||||
if (font->Scale <= 0.f) 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;
|
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);
|
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)
|
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;
|
auto &font_index_opt = m_font_list[m_font_selected].imgui_font_index;
|
||||||
if (!font_index_opt.has_value())
|
if (!font_index_opt.has_value())
|
||||||
return load_imgui_font(index, text);
|
return create_imgui_font(index, text);
|
||||||
|
|
||||||
// TODO: start using merge mode
|
// TODO: start using merge mode
|
||||||
// ImFontConfig::MergeMode = true;
|
// ImFontConfig::MergeMode = true;
|
||||||
|
|
||||||
free_imgui_fonts();
|
free_imgui_fonts();
|
||||||
return load_imgui_font(index, text);
|
return create_imgui_font(index, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp"
|
#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,
|
float FontManager::get_imgui_font_size(const FontProp &prop,
|
||||||
const Emboss::FontFile &file)
|
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
|
// 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):
|
// 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.
|
// It is approximately 0.0139 inch or 352.8 um.
|
||||||
return c1 * std::abs(prop.size_in_mm) / 0.3528f;
|
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
|
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;
|
ImFontConfig font_config;
|
||||||
// TODO: start using merge mode
|
// TODO: start using merge mode
|
||||||
//font_config.MergeMode = true;
|
//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()) {
|
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);
|
font_config.GlyphExtraSpacing.x = coef * (*font_prop.char_gap);
|
||||||
}
|
}
|
||||||
if (font_prop.line_gap.has_value()) {
|
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.GlyphExtraSpacing.y = coef * (*font_prop.line_gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
font_config.FontDataOwnedByAtlas = false;
|
font_config.FontDataOwnedByAtlas = false;
|
||||||
|
|
||||||
const std::vector<unsigned char> &buffer = *font_file.data;
|
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);
|
(void *) buffer.data(), buffer.size(), font_size, &font_config, item.font_ranges.Data);
|
||||||
|
|
||||||
unsigned char *pixels;
|
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;
|
m_imgui_font_atlas.TexID = (ImTextureID) (intptr_t) font_texture;
|
||||||
assert(!m_imgui_font_atlas.Fonts.empty());
|
assert(!m_imgui_font_atlas.Fonts.empty());
|
||||||
if (m_imgui_font_atlas.Fonts.empty()) return nullptr;
|
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;
|
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) {
|
bool FontManager::set_wx_font(size_t item_index, const wxFont &wx_font) {
|
||||||
|
|
|
@ -99,7 +99,9 @@ public:
|
||||||
// Getter on acitve font pointer for imgui
|
// Getter on acitve font pointer for imgui
|
||||||
// Initialize imgui font(generate texture) when doesn't exist yet.
|
// Initialize imgui font(generate texture) when doesn't exist yet.
|
||||||
// Extend font atlas when not in glyph range
|
// 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
|
// free used memory and font file data
|
||||||
void free_except_active_font();
|
void free_except_active_font();
|
||||||
|
@ -166,7 +168,7 @@ private:
|
||||||
|
|
||||||
void duplicate(size_t index);
|
void duplicate(size_t index);
|
||||||
// load actual selected font
|
// 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();
|
bool load_active_font();
|
||||||
|
|
||||||
|
@ -174,18 +176,13 @@ private:
|
||||||
|
|
||||||
// getter on index selected font pointer for imgui
|
// getter on index selected font pointer for imgui
|
||||||
// text could extend font atlas when not in glyph range
|
// 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
|
// extend actual imgui font when exist unknown char in text
|
||||||
// NOTE: imgui_font has to be unused
|
// NOTE: imgui_font has to be unused
|
||||||
// return true when extend range otherwise FALSE
|
// return true when extend range otherwise FALSE
|
||||||
ImFont *extend_imgui_font_range(size_t font_index, const std::string &text);
|
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();
|
void free_imgui_fonts();
|
||||||
|
|
||||||
bool set_up_font_file(size_t item_index);
|
bool set_up_font_file(size_t item_index);
|
||||||
|
|
|
@ -136,13 +136,15 @@ TEST_CASE("Read glyph C shape from font, stb library calls ONLY", "[Emboss]") {
|
||||||
TEST_CASE("Convert glyph % to model", "[Emboss]")
|
TEST_CASE("Convert glyph % to model", "[Emboss]")
|
||||||
{
|
{
|
||||||
std::string font_path = get_font_filepath();
|
std::string font_path = get_font_filepath();
|
||||||
|
unsigned int font_index = 0; // collection
|
||||||
char letter = '%';
|
char letter = '%';
|
||||||
float flatness = 2.;
|
float flatness = 2.;
|
||||||
|
|
||||||
auto font = Emboss::create_font_file(font_path.c_str());
|
auto font = Emboss::create_font_file(font_path.c_str());
|
||||||
REQUIRE(font != nullptr);
|
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());
|
REQUIRE(glyph.has_value());
|
||||||
|
|
||||||
ExPolygons shape = glyph->shape;
|
ExPolygons shape = glyph->shape;
|
||||||
|
@ -301,12 +303,13 @@ TEST_CASE("Cut surface", "[]")
|
||||||
std::string font_path = get_font_filepath();
|
std::string font_path = get_font_filepath();
|
||||||
char letter = '%';
|
char letter = '%';
|
||||||
float flatness = 2.;
|
float flatness = 2.;
|
||||||
|
unsigned int font_index = 0; // collection
|
||||||
|
|
||||||
auto font = Emboss::create_font_file(font_path.c_str());
|
auto font = Emboss::create_font_file(font_path.c_str());
|
||||||
REQUIRE(font != nullptr);
|
REQUIRE(font != nullptr);
|
||||||
|
|
||||||
std::optional<Emboss::Glyph> glyph = Emboss::letter2glyph(*font, letter,
|
std::optional<Emboss::Glyph> glyph =
|
||||||
flatness);
|
Emboss::letter2glyph(*font, font_index, letter, flatness);
|
||||||
REQUIRE(glyph.has_value());
|
REQUIRE(glyph.has_value());
|
||||||
|
|
||||||
ExPolygons shape = glyph->shape;
|
ExPolygons shape = glyph->shape;
|
||||||
|
@ -586,14 +589,15 @@ using MyMesh = Slic3r::MeshBoolean::cgal2::CGALMesh;
|
||||||
TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
||||||
{
|
{
|
||||||
std::string font_path = get_font_filepath();
|
std::string font_path = get_font_filepath();
|
||||||
|
unsigned int font_index = 0; // collection
|
||||||
char letter = '%';
|
char letter = '%';
|
||||||
float flatness = 2.;
|
float flatness = 2.;
|
||||||
|
|
||||||
auto font = Emboss::create_font_file(font_path.c_str());
|
auto font = Emboss::create_font_file(font_path.c_str());
|
||||||
REQUIRE(font != nullptr);
|
REQUIRE(font != nullptr);
|
||||||
|
|
||||||
std::optional<Emboss::Glyph> glyph = Emboss::letter2glyph(*font, letter,
|
std::optional<Emboss::Glyph> glyph =
|
||||||
flatness);
|
Emboss::letter2glyph(*font, font_index, letter, flatness);
|
||||||
REQUIRE(glyph.has_value());
|
REQUIRE(glyph.has_value());
|
||||||
|
|
||||||
ExPolygons shape = glyph->shape;
|
ExPolygons shape = glyph->shape;
|
||||||
|
|
Loading…
Reference in a new issue