FIX: get suitable font size to draw the texts

jira: [STUDIO-10067]
Change-Id: I589fd6a271ae177e4630e403b64c18090aab9471
This commit is contained in:
xin.zhang 2025-02-19 18:25:14 +08:00 committed by lane.wei
parent 6621fd0e20
commit d036c92a37
6 changed files with 490 additions and 15 deletions

View File

@ -550,6 +550,8 @@ set(SLIC3R_GUI_SOURCES
Utils/FontConfigHelp.hpp
Utils/FontUtils.cpp
Utils/FontUtils.hpp
Utils/WxFontUtils.hpp
Utils/WxFontUtils.cpp
)
add_subdirectory(GUI/DeviceTab)

View File

@ -3,6 +3,9 @@
#include "libslic3r/Utils.hpp"
#include "libslic3r/Thread.hpp"
#include "slic3r/Utils/WxFontUtils.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "GUI_Preview.hpp"
@ -1027,6 +1030,7 @@ PinCodePanel::PinCodePanel(wxWindow* parent, int type, wxWindowID winid /*= wxID
if (m_type == 0) {txt = _L("Bind with Pin Code");}
else if (m_type == 1) {txt = _L("Bind with Access Code");}
WxFontUtils::get_suitable_font_size(0.5 * size.GetHeight(), dc);
auto txt_size = dc.GetTextExtent(txt);
dc.DrawText(txt, wxPoint(FromDIP(28), (size.y - txt_size.y) / 2));

View File

@ -3,6 +3,7 @@
#include "../BitmapCache.hpp"
#include "../I18N.hpp"
#include "../GUI_App.hpp"
#include "../Utils/WxFontUtils.hpp"
#include <wx/simplebook.h>
#include <wx/dcgraph.h>
@ -229,9 +230,11 @@ void AMSExtText::doRender(wxDC& dc)
auto size = GetSize();
dc.SetPen(wxPen(StateColor::darkModeColorFor(AMS_CONTROL_GRAY800), 2, wxPENSTYLE_SOLID));
auto tsize = dc.GetMultiLineTextExtent(_L("Ext"));
dc.SetFont(Label::Body_13);
dc.SetTextForeground(StateColor::darkModeColorFor(AMS_CONTROL_GRAY800));
dc.SetFont(Label::Body_13);
WxFontUtils::get_suitable_font_size(0.7 * size.GetHeight(), dc);
auto tsize = dc.GetMultiLineTextExtent(_L("Ext"));
wxPoint pot(FromDIP((size.x - tsize.x) / 2), FromDIP((size.y - tsize.y) / 2));
dc.DrawText(_L("Ext"), pot);
}
@ -2873,24 +2876,28 @@ void AMSHumidity::doRender(wxDC& dc)
pot = wxPoint(FromDIP(5), ((size.y - hum_img.GetBmpSize().y) / 2));
dc.DrawBitmap(hum_img.bmp(), pot);
pot.x += hum_img.GetBmpSize().x + FromDIP(3);
// percentage
wxString hum_percentage(std::to_string(m_amsinfo.humidity_raw));
auto tsize = dc.GetMultiLineTextExtent(hum_percentage);
dc.SetPen(wxPen(*wxTRANSPARENT_PEN));
dc.SetFont(Label::Body_14);
dc.SetTextForeground(StateColor::darkModeColorFor(AMS_CONTROL_BLACK_COLOUR));
// pot = wxPoint(FromDIP(size.x * 0.3), FromDIP((size.y - tsize.y) / 2));
pot.x = pot.x + hum_img.GetBmpSize().x + FromDIP(3);
dc.DrawText(hum_percentage, pot);
pot.x += (tsize.x + FromDIP(5));
WxFontUtils::get_suitable_font_size(0.7 * size.GetHeight(), dc);
auto tsize1 = dc.GetMultiLineTextExtent(hum_percentage);
pot.y = (size.y - tsize1.y) / 2;
dc.DrawText(hum_percentage, pot);
pot.x += (tsize1.x + FromDIP(3));
// percentage sign
dc.SetFont(Label::Body_12);
tsize = dc.GetMultiLineTextExtent(_L("%"));
pot.y += (tsize.y / 2 - FromDIP(4));
WxFontUtils::get_suitable_font_size(0.5 * size.GetHeight(), dc);
auto tsize2 = dc.GetMultiLineTextExtent(_L("%"));
pot.y = pot.y + ((tsize1.y - tsize2.y) / 2) + FromDIP(2);
dc.DrawText(_L("%"), pot);
pot.x = pot.x + tsize.x + FromDIP(2);
pot.x += tsize2.x + FromDIP(3);
}
else /*image with number*/
{
@ -2916,8 +2923,8 @@ void AMSHumidity::doRender(wxDC& dc)
//sun image
/*pot.x = FromDIP(size.x * 0.69);
pot.y = FromDIP((size.y - ams_sun_img.GetBmpHeight()) / 2);*/
pot.x = pot.x + FromDIP(ams_sun_img.GetBmpWidth() / 2);
pot.y = FromDIP((size.y - ams_sun_img.GetBmpHeight()) / 2);
pot.x = pot.x + (ams_sun_img.GetBmpWidth() / 2);
pot.y = (size.y - ams_sun_img.GetBmpHeight()) / 2;
dc.SetPen(wxPen(*wxTRANSPARENT_PEN));
dc.DrawBitmap(ams_sun_img.bmp(), pot);

View File

@ -4,6 +4,7 @@
#include "../wxExtensions.hpp"
#include "../Utils/MacDarkMode.hpp"
#include "../Utils/WxFontUtils.hpp"
#include <wx/dcclient.h>
#include <wx/dcgraph.h>
@ -239,14 +240,16 @@ void SwitchBoard::doRender(wxDC &dc)
dc.DrawRoundedRectangle(0, 0, GetSize().x / 2, GetSize().y, 8);
}
auto left_txt_size = dc.GetTextExtent(leftLabel);
dc.SetFont(::Label::Body_13);
if (switch_left) {
dc.SetTextForeground(*wxWHITE);
} else {
dc.SetTextForeground(0x333333);
}
dc.SetFont(::Label::Body_13);
Slic3r::GUI::WxFontUtils::get_suitable_font_size(0.6 * GetSize().GetHeight(), dc);
auto left_txt_size = dc.GetTextExtent(leftLabel);
dc.DrawText(leftLabel, wxPoint((GetSize().x / 2 - left_txt_size.x) / 2, (GetSize().y - left_txt_size.y) / 2));
/*right*/
@ -257,7 +260,6 @@ void SwitchBoard::doRender(wxDC &dc)
}
auto right_txt_size = dc.GetTextExtent(rightLabel);
dc.SetFont(::Label::Body_13);
if (switch_right) {
dc.SetTextForeground(*wxWHITE);
} else {

View File

@ -0,0 +1,386 @@
#include "WxFontUtils.hpp"
#include <boost/assign.hpp>
#include <boost/log/trivial.hpp>
#include "libslic3r/Utils.hpp"
#if defined(__APPLE__)
#include <CoreText/CTFont.h>
#include <wx/uri.h>
#include <wx/fontutil.h> // wxNativeFontInfo
#include <wx/osx/core/cfdictionary.h>
#elif defined(__linux__)
#include "slic3r/Utils/FontConfigHelp.hpp"
#endif
using namespace Slic3r;
using namespace Slic3r::GUI;
#ifdef __APPLE__
namespace {
bool is_valid_ttf(std::string_view file_path)
{
if (file_path.empty()) return false;
auto const pos_point = file_path.find_last_of('.');
if (pos_point == std::string_view::npos) return false;
// use point only after last directory delimiter
auto const pos_directory_delimiter = file_path.find_last_of("/\\");
if (pos_directory_delimiter != std::string_view::npos &&
pos_point < pos_directory_delimiter)
return false; // point is before directory delimiter
// check count of extension chars
size_t extension_size = file_path.size() - pos_point;
if (extension_size >= 5) return false; // a lot of symbols for extension
if (extension_size <= 1) return false; // few letters for extension
std::string_view extension = file_path.substr(pos_point + 1, extension_size);
// Because of MacOs - Courier, Geneva, Monaco
if (extension == std::string_view("dfont")) return false;
return true;
}
// get filepath from wxFont on Mac OsX
std::string get_file_path(const wxFont& font) {
const wxNativeFontInfo *info = font.GetNativeFontInfo();
if (info == nullptr) return {};
CTFontDescriptorRef descriptor = info->GetCTFontDescriptor();
CFURLRef typeref = (CFURLRef)CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);
if (typeref == NULL) return {};
ScopeGuard sg([&typeref]() { CFRelease(typeref); });
CFStringRef url = CFURLGetString(typeref);
if (url == NULL) return {};
wxString file_uri(wxCFStringRef::AsString(url));
wxURI uri(file_uri);
const wxString &path = uri.GetPath();
wxString path_unescaped = wxURI::Unescape(path);
std::string path_str = path_unescaped.ToUTF8().data();
BOOST_LOG_TRIVIAL(trace) << "input uri(" << file_uri.c_str() << ") convert to path(" << path.c_str() << ") string(" << path_str << ").";
return path_str;
}
} // namespace
#endif // __APPLE__
bool WxFontUtils::can_load(const wxFont &font)
{
if (!font.IsOk()) return false;
#ifdef _WIN32
return Emboss::can_load(font.GetHFONT()) != nullptr;
#elif defined(__APPLE__)
return true;
//return is_valid_ttf(get_file_path(font));
#elif defined(__linux__)
return true;
// font config check file path take about 4000ms for chech them all
//std::string font_path = Slic3r::GUI::get_font_path(font);
//return !font_path.empty();
#endif
return false;
}
std::unique_ptr<Emboss::FontFile> WxFontUtils::create_font_file(const wxFont &font)
{
#ifdef _WIN32
return Emboss::create_font_file(font.GetHFONT());
#elif defined(__APPLE__)
std::string file_path = get_file_path(font);
if (!is_valid_ttf(file_path)) {
BOOST_LOG_TRIVIAL(error) << "Can not process font('" << get_human_readable_name(font) << "'), "
<< "file in path('" << file_path << "') is not valid TTF.";
return nullptr;
}
return Emboss::create_font_file(file_path.c_str());
#elif defined(__linux__)
std::string font_path = Slic3r::GUI::get_font_path(font);
if (font_path.empty()){
BOOST_LOG_TRIVIAL(error) << "Can not read font('" << get_human_readable_name(font) << "'), "
<< "file path is empty.";
return nullptr;
}
return Emboss::create_font_file(font_path.c_str());
#else
// HERE is place to add implementation for another platform
// to convert wxFont to font data as windows or font file path as linux
return nullptr;
#endif
}
EmbossStyle::Type WxFontUtils::get_current_type()
{
#ifdef _WIN32
return EmbossStyle::Type::wx_win_font_descr;
#elif defined(__APPLE__)
return EmbossStyle::Type::wx_mac_font_descr;
#elif defined(__linux__)
return EmbossStyle::Type::wx_lin_font_descr;
#else
return EmbossStyle::Type::undefined;
#endif
}
EmbossStyle WxFontUtils::create_emboss_style(const wxFont &font, const std::string& name)
{
std::string name_item = name.empty()? get_human_readable_name(font) : name;
std::string fontDesc = store_wxFont(font);
EmbossStyle::Type type = get_current_type();
// synchronize font property with actual font
FontProp font_prop;
// 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. But it is too small, so I
// decide use point size as mm for emboss
font_prop.size_in_mm = font.GetPointSize(); // *0.3528f;
WxFontUtils::update_property(font_prop, font);
return { name_item, fontDesc, type, font_prop };
}
// NOT working on linux GTK2
// load font used by Operating system as default GUI
//EmbossStyle WxFontUtils::get_os_font()
//{
// wxSystemFont system_font = wxSYS_DEFAULT_GUI_FONT;
// wxFont font = wxSystemSettings::GetFont(system_font);
// EmbossStyle es = create_emboss_style(font);
// es.name += std::string(" (OS default)");
// return es;
//}
std::string WxFontUtils::get_human_readable_name(const wxFont &font)
{
if (!font.IsOk()) return "Font is NOT ok.";
// Face name is optional in wxFont
if (!font.GetFaceName().empty()) {
return std::string(font.GetFaceName().c_str());
} else {
return std::string((font.GetFamilyString() + " " +
font.GetStyleString() + " " +
font.GetWeightString())
.c_str());
}
}
std::string WxFontUtils::store_wxFont(const wxFont &font)
{
// wxString os = wxPlatformInfo::Get().GetOperatingSystemIdName();
wxString font_descriptor = font.GetNativeFontInfoDesc();
BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor << "' wx string get from GetNativeFontInfoDesc. wxFont " <<
"IsOk(" << font.IsOk() << "), " <<
"isNull(" << font.IsNull() << ")" <<
// "IsFree(" << font.IsFree() << "), " << // on MacOs is no function is free
"IsFixedWidth(" << font.IsFixedWidth() << "), " <<
"IsUsingSizeInPixels(" << font.IsUsingSizeInPixels() << "), " <<
"Encoding(" << (int)font.GetEncoding() << "), " ;
return std::string(font_descriptor.ToUTF8().data());
}
wxFont WxFontUtils::load_wxFont(const std::string &font_descriptor)
{
BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor << "'font descriptor string param of load_wxFont()";
wxString font_descriptor_wx = wxString::FromUTF8(font_descriptor);
BOOST_LOG_TRIVIAL(trace) << "'" << font_descriptor_wx.c_str() << "' wx string descriptor";
wxFont wx_font(font_descriptor_wx);
BOOST_LOG_TRIVIAL(trace) << "loaded font is '" << get_human_readable_name(wx_font) << "'.";
return wx_font;
}
using TypeToFamily = boost::bimap<wxFontFamily, std::string_view>;
const TypeToFamily WxFontUtils::type_to_family =
boost::assign::list_of<TypeToFamily::relation>
(wxFONTFAMILY_DEFAULT, "default")
(wxFONTFAMILY_DECORATIVE, "decorative")
(wxFONTFAMILY_ROMAN, "roman")
(wxFONTFAMILY_SCRIPT, "script")
(wxFONTFAMILY_SWISS, "swiss")
(wxFONTFAMILY_MODERN, "modern")
(wxFONTFAMILY_TELETYPE, "teletype");
using TypeToStyle = boost::bimap<wxFontStyle, std::string_view>;
const TypeToStyle WxFontUtils::type_to_style =
boost::assign::list_of<TypeToStyle::relation>
(wxFONTSTYLE_ITALIC, "italic")
(wxFONTSTYLE_SLANT, "slant")
(wxFONTSTYLE_NORMAL, "normal");
using TypeToWeight = boost::bimap<wxFontWeight, std::string_view>;
const TypeToWeight WxFontUtils::type_to_weight =
boost::assign::list_of<TypeToWeight::relation>
(wxFONTWEIGHT_THIN, "thin")
(wxFONTWEIGHT_EXTRALIGHT, "extraLight")
(wxFONTWEIGHT_LIGHT, "light")
(wxFONTWEIGHT_NORMAL, "normal")
(wxFONTWEIGHT_MEDIUM, "medium")
(wxFONTWEIGHT_SEMIBOLD, "semibold")
(wxFONTWEIGHT_BOLD, "bold")
(wxFONTWEIGHT_EXTRABOLD, "extraBold")
(wxFONTWEIGHT_HEAVY, "heavy")
(wxFONTWEIGHT_EXTRAHEAVY, "extraHeavy");
wxFont WxFontUtils::create_wxFont(const EmbossStyle &style)
{
const FontProp &fp = style.prop;
double point_size = static_cast<double>(fp.size_in_mm);
wxFontInfo info(point_size);
if (fp.family.has_value()) {
auto it = type_to_family.right.find(*fp.family);
if (it != type_to_family.right.end()) info.Family(it->second);
}
// Face names are not portable, so prefer to use Family() in portable code.
/* if (fp.face_name.has_value()) {
wxString face_name(*fp.face_name);
info.FaceName(face_name);
}*/
if (fp.style.has_value()) {
auto it = type_to_style.right.find(*fp.style);
if (it != type_to_style.right.end()) info.Style(it->second);
}
if (fp.weight.has_value()) {
auto it = type_to_weight.right.find(*fp.weight);
if (it != type_to_weight.right.end()) info.Weight(it->second);
}
// Improve: load descriptor instead of store to font property to 3mf
// switch (es.type) {
// case EmbossStyle::Type::wx_lin_font_descr:
// case EmbossStyle::Type::wx_win_font_descr:
// case EmbossStyle::Type::wx_mac_font_descr:
// case EmbossStyle::Type::file_path:
// case EmbossStyle::Type::undefined:
// default:
//}
wxFont wx_font(info);
// Check if exist font file
std::unique_ptr<Emboss::FontFile> ff = create_font_file(wx_font);
if (ff == nullptr) return {};
return wx_font;
}
void WxFontUtils::update_property(FontProp &font_prop, const wxFont &font)
{
wxString wx_face_name = font.GetFaceName();
std::string face_name((const char *) wx_face_name.ToUTF8());
if (!face_name.empty()) font_prop.face_name = face_name;
wxFontFamily wx_family = font.GetFamily();
if (wx_family != wxFONTFAMILY_DEFAULT) {
auto it = type_to_family.left.find(wx_family);
if (it != type_to_family.left.end()) font_prop.family = it->second;
}
wxFontStyle wx_style = font.GetStyle();
if (wx_style != wxFONTSTYLE_NORMAL) {
auto it = type_to_style.left.find(wx_style);
if (it != type_to_style.left.end()) font_prop.style = it->second;
}
wxFontWeight wx_weight = font.GetWeight();
if (wx_weight != wxFONTWEIGHT_NORMAL) {
auto it = type_to_weight.left.find(wx_weight);
if (it != type_to_weight.left.end()) font_prop.weight = it->second;
}
}
bool WxFontUtils::is_italic(const wxFont &font) {
wxFontStyle wx_style = font.GetStyle();
return wx_style == wxFONTSTYLE_ITALIC ||
wx_style == wxFONTSTYLE_SLANT;
}
bool WxFontUtils::is_bold(const wxFont &font) {
wxFontWeight wx_weight = font.GetWeight();
return wx_weight != wxFONTWEIGHT_NORMAL;
}
void Slic3r::GUI::WxFontUtils::get_suitable_font_size(int max_height, wxDC &dc)
{
wxFont font = dc.GetFont();
if (!font.IsOk()) return;
int font_size = font.GetPointSize();
int height = dc.GetFontMetrics().height;
if (height < max_height) /*go smaller*/
{
while (height < max_height) {
font_size++;
font.SetPointSize(font_size);
dc.SetFont(font);
height = dc.GetFontMetrics().height;
}
if (height > max_height)
{
font_size--;
font.SetPointSize(font_size);
dc.SetFont(font);
}
}
else if (height > max_height) /*go bigger*/
{
while (height > max_height && font_size > 1) {
font_size--;
font.SetPointSize(font_size);
dc.SetFont(font);
height = dc.GetFontMetrics().height;
}
}
}
std::unique_ptr<Emboss::FontFile> WxFontUtils::set_italic(wxFont &font, const Emboss::FontFile &font_file)
{
static std::vector<wxFontStyle> italic_styles = {
wxFontStyle::wxFONTSTYLE_ITALIC,
wxFontStyle::wxFONTSTYLE_SLANT
};
wxFontStyle orig_style = font.GetStyle();
for (wxFontStyle style : italic_styles) {
font.SetStyle(style);
std::unique_ptr<Emboss::FontFile> new_font_file =
WxFontUtils::create_font_file(font);
// can create italic font?
if (new_font_file == nullptr)
continue;
// is still same font file pointer?
if (font_file == *new_font_file)
continue;
return new_font_file;
}
// There is NO italic font by wx
font.SetStyle(orig_style);
return nullptr;
}
std::unique_ptr<Emboss::FontFile> WxFontUtils::set_bold(wxFont &font, const Emboss::FontFile& font_file)
{
static std::vector<wxFontWeight> bold_weight = {
wxFontWeight::wxFONTWEIGHT_BOLD,
wxFontWeight::wxFONTWEIGHT_HEAVY,
wxFontWeight::wxFONTWEIGHT_EXTRABOLD,
wxFontWeight::wxFONTWEIGHT_EXTRAHEAVY
};
wxFontWeight orig_weight = font.GetWeight();
for (wxFontWeight weight : bold_weight) {
font.SetWeight(weight);
std::unique_ptr<Emboss::FontFile> new_font_file =
WxFontUtils::create_font_file(font);
// can create bold font file?
if (new_font_file == nullptr) continue;
// is still same font file pointer?
if (font_file == *new_font_file) continue;
return new_font_file;
}
// There is NO bold font by wx
font.SetWeight(orig_weight);
return nullptr;
}

View File

@ -0,0 +1,74 @@
#ifndef slic3r_WxFontUtils_hpp_
#define slic3r_WxFontUtils_hpp_
#include <memory>
#include <optional>
#include <string_view>
#include <boost/bimap.hpp>
#include <wx/font.h>
#include "libslic3r/Emboss.hpp"
namespace Slic3r {
namespace GUI {
// Help class to work with wx widget font object( wxFont )
class WxFontUtils
{
public:
// only static functions
WxFontUtils() = delete;
// check if exist file for wxFont
// return pointer on data or nullptr when can't load
static bool can_load(const wxFont &font);
// os specific load of wxFont
static std::unique_ptr<::Slic3r::Emboss::FontFile> create_font_file(const wxFont &font);
static EmbossStyle::Type get_current_type();
static EmbossStyle create_emboss_style(const wxFont &font, const std::string &name = "");
static std::string get_human_readable_name(const wxFont &font);
// serialize / deserialize font
static std::string store_wxFont(const wxFont &font);
static wxFont load_wxFont(const std::string &font_descriptor);
// Try to create similar font, loaded from 3mf from different Computer
static wxFont create_wxFont(const EmbossStyle &style);
// update font property by wxFont - without emboss depth and font size
static void update_property(FontProp &font_prop, const wxFont &font);
static bool is_italic(const wxFont &font);
static bool is_bold(const wxFont &font);
static void get_suitable_font_size(int max_height, wxDC &dc);
/// <summary>
/// Set italic into wx font
/// When italic font is same as original return nullptr.
/// To not load font file twice on success is font_file returned.
/// </summary>
/// <param name="font">wx descriptor of font</param>
/// <param name="font_file">file described in wx font</param>
/// <returns>New created font fileon success otherwise nullptr</returns>
static std::unique_ptr<::Slic3r::Emboss::FontFile> set_italic(wxFont &font, const ::Slic3r::Emboss::FontFile &prev_font_file);
/// <summary>
/// Set boldness into wx font
/// When bolded font is same as original return nullptr.
/// To not load font file twice on success is font_file returned.
/// </summary>
/// <param name="font">wx descriptor of font</param>
/// <param name="font_file">file described in wx font</param>
/// <returns>New created font fileon success otherwise nullptr</returns>
static std::unique_ptr<::Slic3r::Emboss::FontFile> set_bold(wxFont &font, const ::Slic3r::Emboss::FontFile &font_file);
// convert wxFont types to string and vice versa
static const boost::bimap<wxFontFamily, std::string_view> type_to_family;
static const boost::bimap<wxFontStyle, std::string_view> type_to_style;
static const boost::bimap<wxFontWeight, std::string_view> type_to_weight;
};
}
} // namespace Slic3r::GUI
#endif // slic3r_WxFontUtils_hpp_