diff --git a/src/libslic3r/NSVGUtils.cpp b/src/libslic3r/NSVGUtils.cpp new file mode 100644 index 0000000000..e86451e490 --- /dev/null +++ b/src/libslic3r/NSVGUtils.cpp @@ -0,0 +1,73 @@ +#include "NSVGUtils.hpp" + +using namespace Slic3r; + +void NSVGUtils::flatten_cubic_bez(Polygon &polygon, + float tessTol, + Vec2f p1, + Vec2f p2, + Vec2f p3, + Vec2f p4, + int level) +{ + Vec2f p12 = (p1 + p2) * 0.5f; + Vec2f p23 = (p2 + p3) * 0.5f; + Vec2f p34 = (p3 + p4) * 0.5f; + Vec2f p123 = (p12 + p23) * 0.5f; + + Vec2f pd = p4 - p1; + Vec2f pd2 = p2 - p4; + float d2 = std::abs(pd2.x() * pd.y() - pd2.y() * pd.x()); + Vec2f pd3 = p3 - p4; + float d3 = std::abs(pd3.x() * pd.y() - pd3.y() * pd.x()); + float d23 = d2 + d3; + + if ((d23 * d23) < tessTol * (pd.x() * pd.x() + pd.y() * pd.y())) { + polygon.points.emplace_back(p4.x(), p4.y()); + return; + } + + --level; + if (level == 0) return; + Vec2f p234 = (p23 + p34) * 0.5f; + Vec2f p1234 = (p123 + p234) * 0.5f; + flatten_cubic_bez(polygon, tessTol, p1, p12, p123, p1234, level); + flatten_cubic_bez(polygon, tessTol, p1234, p234, p34, p4, level); +} + +ExPolygons NSVGUtils::to_ExPolygons(NSVGimage *image, + float tessTol, + int max_level) +{ + Polygons polygons; + for (NSVGshape *shape = image->shapes; shape != NULL; + shape = shape->next) { + if (!(shape->flags & NSVG_FLAGS_VISIBLE)) continue; + Slic3r::Polygon polygon; + if (shape->fill.type != NSVG_PAINT_NONE) { + for (NSVGpath *path = shape->paths; path != NULL; + path = path->next) { + // Flatten path + polygon.points.emplace_back(path->pts[0], path->pts[1]); + for (size_t i = 0; i < path->npts - 1; i += 3) { + float *p = &path->pts[i * 2]; + Vec2f p1(p[0], p[1]), p2(p[2], p[3]), p3(p[4], p[5]), + p4(p[6], p[7]); + flatten_cubic_bez(polygon, tessTol, p1, p2, p3, p4, + max_level); + } + if (path->closed) { + polygons.push_back(polygon); + polygon = Slic3r::Polygon(); + } + } + } + polygons.push_back(polygon); + } + + // Fix Y axis + for (Polygon &polygon : polygons) + for (Point &p : polygon.points) p.y() *= -1; + + return Slic3r::union_ex(polygons); +} \ No newline at end of file diff --git a/src/libslic3r/NSVGUtils.hpp b/src/libslic3r/NSVGUtils.hpp new file mode 100644 index 0000000000..308f11775c --- /dev/null +++ b/src/libslic3r/NSVGUtils.hpp @@ -0,0 +1,30 @@ +#ifndef slic3r_NSVGUtils_hpp_ +#define slic3r_NSVGUtils_hpp_ + +#include "Polygon.hpp" +#include "ExPolygon.hpp" +#include "nanosvg/nanosvg.h" // load SVG file + +namespace Slic3r { + +// Helper function to work with nano svg +class NSVGUtils +{ +public: + NSVGUtils() = delete; + + // inspired by nanosvgrast.h function nsvgRasterize->nsvg__flattenShape + static void flatten_cubic_bez(Polygon &polygon, + float tessTol, + Vec2f p1, + Vec2f p2, + Vec2f p3, + Vec2f p4, + int level); + // convert svg image to ExPolygons + static ExPolygons to_ExPolygons(NSVGimage *image, + float tessTol = 10., + int max_level = 10); +}; +} // namespace Slic3r +#endif // slic3r_NSVGUtils_hpp_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 8c56ddc75b..062dfc19c7 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -232,6 +232,8 @@ set(SLIC3R_GUI_SOURCES Utils/Duet.hpp Utils/FlashAir.cpp Utils/FlashAir.hpp + Utils/FontConfigHelp.cpp + Utils/FontConfigHelp.hpp Utils/AstroBox.cpp Utils/AstroBox.hpp Utils/Repetier.cpp @@ -253,6 +255,8 @@ set(SLIC3R_GUI_SOURCES Utils/TCPConsole.hpp Utils/MKS.cpp Utils/MKS.hpp + Utils/WxFontUtils.cpp + Utils/WxFontUtils.hpp ) if (APPLE) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 1443a72d22..5bd29702f2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -11,11 +11,13 @@ #include "slic3r/GUI/CameraUtils.hpp" #include "slic3r/GUI/Jobs/EmbossJob.hpp" #include "slic3r/GUI/Jobs/NotificationProgressIndicator.hpp" +#include "slic3r/Utils/WxFontUtils.hpp" // TODO: remove include #include "libslic3r/SVG.hpp" // debug store #include "libslic3r/Geometry.hpp" // covex hull 2d +#include "libslic3r/NSVGUtils.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/ClipperUtils.hpp" // union_ex #include "libslic3r/AppConfig.hpp" // store/load font list @@ -41,156 +43,12 @@ // Font Config must exist #include #include +#include "slic3r/Utils/FontConfigHelp.hpp" #endif // uncomment for easier debug //#define ALLOW_DEBUG_MODE -namespace Slic3r { -class WxFontUtils -{ -public: - WxFontUtils() = delete; - - // os specific load of wxFont - static std::optional load_font(const wxFont &font); - - static FontItem::Type get_actual_type(); - static FontItem get_font_item(const wxFont &font); - - // load font used by Operating system as default GUI - static FontItem get_os_font(); - static std::string get_human_readable_name(const wxFont &font); - - // serialize / deserialize font - static std::string store_wxFont(const wxFont &font); - static std::optional load_wxFont(const std::string &font_descriptor); - - // Try to create similar font, loaded from 3mf from different Computer - static std::optional create_wxFont(const FontItem &fi, const FontProp &fp); - // update font property by wxFont - static void update_property(FontProp& font_prop, const wxFont& font); - - // map to convert wxFont type to string and vice versa - static const std::map from_family; - static const std::map to_family; - - static const std::map from_style; - static const std::map to_style; - - static const std::map from_weight; - static const std::map to_weight; -}; - -class NSVGUtils -{ -public: - NSVGUtils() = delete; - - // inspired by nanosvgrast.h function nsvgRasterize->nsvg__flattenShape - static void flatten_cubic_bez(Polygon &polygon, float tessTol, - Vec2f p1, Vec2f p2, Vec2f p3,Vec2f p4, int level); - // convert svg image to ExPolygons - static ExPolygons to_ExPolygons(NSVGimage *image, float tessTol = 10., int max_level = 10); -}; - -#ifdef __linux__ -class FontConfigHelp -{ -public: - FontConfigHelp() { FcInit(); } - ~FontConfigHelp() { FcFini(); } - // inspired by wxpdfdoc - https://github.com/utelle/wxpdfdoc/blob/5bdcdb9953327d06dc50ec312685ccd9bc8400e0/src/pdffontmanager.cpp - std::string get_font_path(const wxFont &font) { - FcConfig *fc = FcInitLoadConfigAndFonts(); - if (fc == nullptr) return ""; - ScopeGuard sg_fc([fc]() { FcConfigDestroy(fc); }); - - wxString fontDesc = font.GetNativeFontInfoUserDesc(); - wxString faceName = font.GetFaceName(); - const wxScopedCharBuffer faceNameBuffer = faceName.ToUTF8(); - const char* fontFamily = faceNameBuffer; - - // Check font slant - int slant = FC_SLANT_ROMAN; - if (fontDesc.Find(wxS("Oblique")) != wxNOT_FOUND) slant = FC_SLANT_OBLIQUE; - else if (fontDesc.Find(wxS("Italic")) != wxNOT_FOUND) slant = FC_SLANT_ITALIC; - - // Check font weight - int weight = FC_WEIGHT_NORMAL; - if (fontDesc.Find(wxS("Book")) != wxNOT_FOUND) weight = FC_WEIGHT_BOOK; - else if (fontDesc.Find(wxS("Medium")) != wxNOT_FOUND) weight = FC_WEIGHT_MEDIUM; -#ifdef FC_WEIGHT_ULTRALIGHT - else if (fontDesc.Find(wxS("Ultra-Light")) != wxNOT_FOUND) weight = FC_WEIGHT_ULTRALIGHT; -#endif - else if (fontDesc.Find(wxS("Light")) != wxNOT_FOUND) weight = FC_WEIGHT_LIGHT; - else if (fontDesc.Find(wxS("Semi-Bold")) != wxNOT_FOUND) weight = FC_WEIGHT_DEMIBOLD; -#ifdef FC_WEIGHT_ULTRABOLD - else if (fontDesc.Find(wxS("Ultra-Bold")) != wxNOT_FOUND) weight = FC_WEIGHT_ULTRABOLD; -#endif - else if (fontDesc.Find(wxS("Bold")) != wxNOT_FOUND) weight = FC_WEIGHT_BOLD; - else if (fontDesc.Find(wxS("Heavy")) != wxNOT_FOUND) weight = FC_WEIGHT_BLACK; - - // Check font width - int width = FC_WIDTH_NORMAL; - if (fontDesc.Find(wxS("Ultra-Condensed")) != wxNOT_FOUND) width = FC_WIDTH_ULTRACONDENSED; - else if (fontDesc.Find(wxS("Extra-Condensed")) != wxNOT_FOUND) width = FC_WIDTH_EXTRACONDENSED; - else if (fontDesc.Find(wxS("Semi-Condensed")) != wxNOT_FOUND) width = FC_WIDTH_SEMICONDENSED; - else if (fontDesc.Find(wxS("Condensed")) != wxNOT_FOUND) width = FC_WIDTH_CONDENSED; - else if (fontDesc.Find(wxS("Ultra-Expanded")) != wxNOT_FOUND) width = FC_WIDTH_ULTRAEXPANDED; - else if (fontDesc.Find(wxS("Extra-Expanded")) != wxNOT_FOUND) width = FC_WIDTH_EXTRAEXPANDED; - else if (fontDesc.Find(wxS("Semi-Expanded")) != wxNOT_FOUND) width = FC_WIDTH_SEMIEXPANDED; - else if (fontDesc.Find(wxS("Expanded")) != wxNOT_FOUND) width = FC_WIDTH_EXPANDED; - - FcResult res; - FcPattern* matchPattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, (FcChar8*) fontFamily, NULL); - ScopeGuard sg_mp([matchPattern]() { FcPatternDestroy(matchPattern); }); - - FcPatternAddInteger(matchPattern, FC_SLANT, slant); - FcPatternAddInteger(matchPattern, FC_WEIGHT, weight); - FcPatternAddInteger(matchPattern, FC_WIDTH, width); - - FcConfigSubstitute (NULL, matchPattern, FcMatchPattern); - FcDefaultSubstitute (matchPattern); - - FcPattern* resultPattern = FcFontMatch (NULL, matchPattern, &res); - if (resultPattern == nullptr) return ""; - ScopeGuard sg_rp([resultPattern]() { FcPatternDestroy(resultPattern); }); - - - FcChar8 *fileName; - if (FcPatternGetString(resultPattern, FC_FILE, 0, &fileName) != FcResultMatch) return ""; - wxString fontFileName = wxString::FromUTF8((char *) fileName); - - if (fontFileName.IsEmpty()) return ""; - - // find full file path - wxFileName myFileName(fontFileName); - if (!myFileName.IsOk()) return ""; - - if (myFileName.IsRelative()) { - // Check whether the file is relative to the current working directory - if (!(myFileName.MakeAbsolute() && myFileName.FileExists())) { - return ""; - // File not found, search in given search paths - //wxString foundFileName = m_searchPaths.FindAbsoluteValidPath(fileName); - //if (!foundFileName.IsEmpty()) { - // myFileName.Assign(foundFileName); - //} - } - } - - if (!myFileName.FileExists() || !myFileName.IsFileReadable()) return ""; - - // File exists and is accessible - wxString fullFileName = myFileName.GetFullPath(); - return std::string(fullFileName.c_str()); - } -}; - -#endif // __linux__ -} // namespace Slic3r - using namespace Slic3r; using namespace Slic3r::GUI; @@ -1937,203 +1795,6 @@ void GLGizmoEmboss::update_emboss_volume(TriangleMesh && mesh, canvas->reload_scene(true); } -/// -/// WxFontUtils - Start definition -/// -std::optional WxFontUtils::load_font(const wxFont &font) -{ - if (!font.IsOk()) return {}; -#ifdef _WIN32 - return Emboss::load_font(font.GetHFONT()); -#elif defined(__APPLE__) - // use file path - const wxNativeFontInfo *info = font.GetNativeFontInfo(); - if (info == nullptr) return {}; - CTFontDescriptorRef descriptor = info->GetCTFontDescriptor(); - CFURLRef typeref = (CFURLRef) - CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute); - CFStringRef url = CFURLGetString(typeref); - if (url == NULL) return {}; - wxString file_uri; - wxCFTypeRef(url).GetValue(file_uri); - std::string file_path(wxURI::Unescape(file_uri).c_str()); - size_t start = std::string("file://").size(); - if (file_path.empty() || file_path.size() <= start) return {}; - file_path = file_path.substr(start, file_path.size() - start); - return Emboss::load_font(file_path.c_str()); -#elif defined(__linux__) - static FontConfigHelp help; - std::string font_path = help.get_font_path(font); - if (font_path.empty()) return {}; - return Emboss::load_font(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 {}; -#endif -} - -FontItem::Type WxFontUtils::get_actual_type() -{ -#ifdef _WIN32 - return FontItem::Type::wx_win_font_descr; -#elif defined(__APPLE__) - return FontItem::Type::wx_mac_font_descr; -#elif defined(__linux__) - return FontItem::Type::wx_mac_font_descr; -#else - return FontItem::Type::undefined; -#endif -} - -FontItem WxFontUtils::get_font_item(const wxFont &font) -{ - std::string name = get_human_readable_name(font); - std::string fontDesc = store_wxFont(font); - FontItem::Type type = get_actual_type(); - // wxFont f = font; // copy - return FontItem(name, fontDesc, type); -} - -FontItem WxFontUtils::get_os_font() -{ - wxSystemFont system_font = wxSYS_DEFAULT_GUI_FONT; - wxFont font = wxSystemSettings::GetFont(system_font); - FontItem fi = get_font_item(font); - fi.name += std::string(" (" + _u8L("OS default") + ")"); - return get_font_item(font); -} - -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(); - return std::string(font_descriptor.c_str()); -} - -std::optional WxFontUtils::load_wxFont( - const std::string &font_descriptor) -{ - wxString font_descriptor_wx(font_descriptor); - wxFont wx_font(font_descriptor_wx); - if (!wx_font.IsOk()) return {}; - return wx_font; -} - -const std::map WxFontUtils::from_family( - {{wxFONTFAMILY_DEFAULT, "default"}, - {wxFONTFAMILY_DECORATIVE, "decorative"}, - {wxFONTFAMILY_ROMAN, "roman"}, - {wxFONTFAMILY_SCRIPT, "script"}, - {wxFONTFAMILY_SWISS, "swiss"}, - {wxFONTFAMILY_MODERN, "modern"}, - {wxFONTFAMILY_TELETYPE, "teletype"}, - {wxFONTFAMILY_MAX, "max"}, - {wxFONTFAMILY_UNKNOWN, "unknown"}}); -const std::map WxFontUtils::to_family = - MapUtils::create_oposit(WxFontUtils::from_family); - -const std::map WxFontUtils::from_style( - {{wxFONTSTYLE_ITALIC, "italic"}, - {wxFONTSTYLE_SLANT, "slant"}, - {wxFONTSTYLE_NORMAL, "normal"}}); -const std::map WxFontUtils::to_style = - MapUtils::create_oposit(WxFontUtils::from_style); - -const std::map WxFontUtils::from_weight( - {{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"}}); -const std::map WxFontUtils::to_weight = - MapUtils::create_oposit(WxFontUtils::from_weight); - -std::optional WxFontUtils::create_wxFont(const FontItem &fi, - const FontProp &fp) -{ - double point_size = static_cast(fp.size_in_mm); - wxFontInfo info(point_size); - if (fp.family.has_value()) { - auto it = to_family.find(*fp.style); - if (it != to_family.end()) info.Family(it->second); - } - if (fp.face_name.has_value()) { - wxString face_name(*fp.face_name); - info.FaceName(face_name); - } - if (fp.style.has_value()) { - auto it = to_style.find(*fp.style); - if (it != to_style.end()) info.Style(it->second); - } - if (fp.weight.has_value()) { - auto it = to_weight.find(*fp.weight); - if (it != to_weight.end()) info.Weight(it->second); - } - - // Improve: load descriptor instead of store to font property to 3mf - // switch (fi.type) { - // case FontItem::Type::wx_lin_font_descr: - // case FontItem::Type::wx_win_font_descr: - // case FontItem::Type::file_path: - // case FontItem::Type::undefined: - // default: - //} - - wxFont font(info); - if (!font.IsOk()) return {}; - return font; -} - -void WxFontUtils::update_property(FontProp &font_prop, const wxFont &font) -{ - // 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; - - 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 = from_family.find(wx_family); - if (it != from_family.end()) font_prop.family = it->second; - } - - wxFontStyle wx_style = font.GetStyle(); - if (wx_style != wxFONTSTYLE_NORMAL) { - auto it = from_style.find(wx_style); - if (it != from_style.end()) font_prop.style = it->second; - } - - wxFontWeight wx_weight = font.GetWeight(); - if (wx_weight != wxFONTWEIGHT_NORMAL) { - auto it = from_weight.find(wx_weight); - if (it != from_weight.end()) font_prop.weight = it->second; - } -} - void NSVGUtils::flatten_cubic_bez(Polygon &polygon, float tessTol, Vec2f p1, diff --git a/src/slic3r/Utils/FontConfigHelp.cpp b/src/slic3r/Utils/FontConfigHelp.cpp new file mode 100644 index 0000000000..511c7af33a --- /dev/null +++ b/src/slic3r/Utils/FontConfigHelp.cpp @@ -0,0 +1,119 @@ +#include "FontConfigHelp.hpp" + +#ifdef EXIST_FONT_CONFIG_INCLUDE +#include + +using namespace Slic3r::GUI; + +FontConfigHelp::FontConfigHelp() { FcInit(); } +FontConfigHelp::~FontConfigHelp() { FcFini(); } + +std::string FontConfigHelp::get_font_path(const wxFont &font) +{ + FcConfig *fc = FcInitLoadConfigAndFonts(); + if (fc == nullptr) return ""; + ScopeGuard sg_fc([fc]() { FcConfigDestroy(fc); }); + + wxString fontDesc = font.GetNativeFontInfoUserDesc(); + wxString faceName = font.GetFaceName(); + const wxScopedCharBuffer faceNameBuffer = faceName.ToUTF8(); + const char * fontFamily = faceNameBuffer; + + // Check font slant + int slant = FC_SLANT_ROMAN; + if (fontDesc.Find(wxS("Oblique")) != wxNOT_FOUND) + slant = FC_SLANT_OBLIQUE; + else if (fontDesc.Find(wxS("Italic")) != wxNOT_FOUND) + slant = FC_SLANT_ITALIC; + + // Check font weight + int weight = FC_WEIGHT_NORMAL; + if (fontDesc.Find(wxS("Book")) != wxNOT_FOUND) + weight = FC_WEIGHT_BOOK; + else if (fontDesc.Find(wxS("Medium")) != wxNOT_FOUND) + weight = FC_WEIGHT_MEDIUM; +#ifdef FC_WEIGHT_ULTRALIGHT + else if (fontDesc.Find(wxS("Ultra-Light")) != wxNOT_FOUND) + weight = FC_WEIGHT_ULTRALIGHT; +#endif + else if (fontDesc.Find(wxS("Light")) != wxNOT_FOUND) + weight = FC_WEIGHT_LIGHT; + else if (fontDesc.Find(wxS("Semi-Bold")) != wxNOT_FOUND) + weight = FC_WEIGHT_DEMIBOLD; +#ifdef FC_WEIGHT_ULTRABOLD + else if (fontDesc.Find(wxS("Ultra-Bold")) != wxNOT_FOUND) + weight = FC_WEIGHT_ULTRABOLD; +#endif + else if (fontDesc.Find(wxS("Bold")) != wxNOT_FOUND) + weight = FC_WEIGHT_BOLD; + else if (fontDesc.Find(wxS("Heavy")) != wxNOT_FOUND) + weight = FC_WEIGHT_BLACK; + + // Check font width + int width = FC_WIDTH_NORMAL; + if (fontDesc.Find(wxS("Ultra-Condensed")) != wxNOT_FOUND) + width = FC_WIDTH_ULTRACONDENSED; + else if (fontDesc.Find(wxS("Extra-Condensed")) != wxNOT_FOUND) + width = FC_WIDTH_EXTRACONDENSED; + else if (fontDesc.Find(wxS("Semi-Condensed")) != wxNOT_FOUND) + width = FC_WIDTH_SEMICONDENSED; + else if (fontDesc.Find(wxS("Condensed")) != wxNOT_FOUND) + width = FC_WIDTH_CONDENSED; + else if (fontDesc.Find(wxS("Ultra-Expanded")) != wxNOT_FOUND) + width = FC_WIDTH_ULTRAEXPANDED; + else if (fontDesc.Find(wxS("Extra-Expanded")) != wxNOT_FOUND) + width = FC_WIDTH_EXTRAEXPANDED; + else if (fontDesc.Find(wxS("Semi-Expanded")) != wxNOT_FOUND) + width = FC_WIDTH_SEMIEXPANDED; + else if (fontDesc.Find(wxS("Expanded")) != wxNOT_FOUND) + width = FC_WIDTH_EXPANDED; + + FcResult res; + FcPattern *matchPattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, + (FcChar8 *) fontFamily, NULL); + ScopeGuard sg_mp([matchPattern]() { FcPatternDestroy(matchPattern); }); + + FcPatternAddInteger(matchPattern, FC_SLANT, slant); + FcPatternAddInteger(matchPattern, FC_WEIGHT, weight); + FcPatternAddInteger(matchPattern, FC_WIDTH, width); + + FcConfigSubstitute(NULL, matchPattern, FcMatchPattern); + FcDefaultSubstitute(matchPattern); + + FcPattern *resultPattern = FcFontMatch(NULL, matchPattern, &res); + if (resultPattern == nullptr) return ""; + ScopeGuard sg_rp([resultPattern]() { FcPatternDestroy(resultPattern); }); + + FcChar8 *fileName; + if (FcPatternGetString(resultPattern, FC_FILE, 0, &fileName) != + FcResultMatch) + return ""; + wxString fontFileName = wxString::FromUTF8((char *) fileName); + + if (fontFileName.IsEmpty()) return ""; + + // find full file path + wxFileName myFileName(fontFileName); + if (!myFileName.IsOk()) return ""; + + if (myFileName.IsRelative()) { + // Check whether the file is relative to the current working directory + if (!(myFileName.MakeAbsolute() && myFileName.FileExists())) { + return ""; + // File not found, search in given search paths + // wxString foundFileName = + // m_searchPaths.FindAbsoluteValidPath(fileName); if + // (!foundFileName.IsEmpty()) { + // myFileName.Assign(foundFileName); + //} + } + } + + if (!myFileName.FileExists() || !myFileName.IsFileReadable()) return ""; + + // File exists and is accessible + wxString fullFileName = myFileName.GetFullPath(); + return std::string(fullFileName.c_str()); +} + +#endif // EXIST_FONT_CONFIG_INCLUDE \ No newline at end of file diff --git a/src/slic3r/Utils/FontConfigHelp.hpp b/src/slic3r/Utils/FontConfigHelp.hpp new file mode 100644 index 0000000000..eb2da63977 --- /dev/null +++ b/src/slic3r/Utils/FontConfigHelp.hpp @@ -0,0 +1,40 @@ +#ifndef slic3r_FontConfigHelp_hpp_ +#define slic3r_FontConfigHelp_hpp_ + +#ifdef __linux__ +#define EXIST_FONT_CONFIG_INCLUDE +#endif + +#ifdef EXIST_FONT_CONFIG_INCLUDE +#include +namespace Slic3r::GUI { + +/// +/// helper object for RAII access to font config +/// initialize & finalize FontConfig +/// +class FontConfigHelp +{ +public: + /// + /// initialize font config + /// + FontConfigHelp(); + + /// + /// free font config resources + /// + ~FontConfigHelp(); + + /// + /// initialize font config + /// Convert wx widget font to file path + /// inspired by wxpdfdoc - + /// https://github.com/utelle/wxpdfdoc/blob/5bdcdb9953327d06dc50ec312685ccd9bc8400e0/src/pdffontmanager.cpp + /// + std::string get_font_path(const wxFont &font); +}; + +} // namespace Slic3r +#endif // EXIST_FONT_CONFIG_INCLUDE +#endif // slic3r_FontConfigHelp_hpp_ diff --git a/src/slic3r/Utils/WxFontUtils.cpp b/src/slic3r/Utils/WxFontUtils.cpp new file mode 100644 index 0000000000..d6aa5df496 --- /dev/null +++ b/src/slic3r/Utils/WxFontUtils.cpp @@ -0,0 +1,200 @@ +#include "WxFontUtils.hpp" +#include "libslic3r/MapUtils.hpp" + +using namespace Slic3r; +using namespace Slic3r::GUI; + +std::optional WxFontUtils::load_font(const wxFont &font) +{ + if (!font.IsOk()) return {}; +#ifdef _WIN32 + return Emboss::load_font(font.GetHFONT()); +#elif defined(__APPLE__) + // use file path + const wxNativeFontInfo *info = font.GetNativeFontInfo(); + if (info == nullptr) return {}; + CTFontDescriptorRef descriptor = info->GetCTFontDescriptor(); + CFURLRef typeref = (CFURLRef) + CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute); + CFStringRef url = CFURLGetString(typeref); + if (url == NULL) return {}; + wxString file_uri; + wxCFTypeRef(url).GetValue(file_uri); + std::string file_path(wxURI::Unescape(file_uri).c_str()); + size_t start = std::string("file://").size(); + if (file_path.empty() || file_path.size() <= start) return {}; + file_path = file_path.substr(start, file_path.size() - start); + return Emboss::load_font(file_path.c_str()); +#elif defined(__linux__) + static FontConfigHelp help; + std::string font_path = help.get_font_path(font); + if (font_path.empty()) return {}; + return Emboss::load_font(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 {}; +#endif +} + +FontItem::Type WxFontUtils::get_actual_type() +{ +#ifdef _WIN32 + return FontItem::Type::wx_win_font_descr; +#elif defined(__APPLE__) + return FontItem::Type::wx_mac_font_descr; +#elif defined(__linux__) + return FontItem::Type::wx_lin_font_descr; +#else + return FontItem::Type::undefined; +#endif +} + +FontItem WxFontUtils::get_font_item(const wxFont &font) +{ + std::string name = get_human_readable_name(font); + std::string fontDesc = store_wxFont(font); + FontItem::Type type = get_actual_type(); + // wxFont f = font; // copy + return FontItem(name, fontDesc, type); +} + +FontItem WxFontUtils::get_os_font() +{ + wxSystemFont system_font = wxSYS_DEFAULT_GUI_FONT; + wxFont font = wxSystemSettings::GetFont(system_font); + FontItem fi = get_font_item(font); + fi.name += std::string(" (OS default)"); + return get_font_item(font); +} + +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(); + return std::string(font_descriptor.c_str()); +} + +std::optional WxFontUtils::load_wxFont( + const std::string &font_descriptor) +{ + wxString font_descriptor_wx(font_descriptor); + wxFont wx_font(font_descriptor_wx); + if (!wx_font.IsOk()) return {}; + return wx_font; +} + +const std::map WxFontUtils::from_family( + {{wxFONTFAMILY_DEFAULT, "default"}, + {wxFONTFAMILY_DECORATIVE, "decorative"}, + {wxFONTFAMILY_ROMAN, "roman"}, + {wxFONTFAMILY_SCRIPT, "script"}, + {wxFONTFAMILY_SWISS, "swiss"}, + {wxFONTFAMILY_MODERN, "modern"}, + {wxFONTFAMILY_TELETYPE, "teletype"}, + {wxFONTFAMILY_MAX, "max"}, + {wxFONTFAMILY_UNKNOWN, "unknown"}}); +const std::map WxFontUtils::to_family = + MapUtils::create_oposit(WxFontUtils::from_family); + +const std::map WxFontUtils::from_style( + {{wxFONTSTYLE_ITALIC, "italic"}, + {wxFONTSTYLE_SLANT, "slant"}, + {wxFONTSTYLE_NORMAL, "normal"}}); +const std::map WxFontUtils::to_style = + MapUtils::create_oposit(WxFontUtils::from_style); + +const std::map WxFontUtils::from_weight( + {{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"}}); +const std::map WxFontUtils::to_weight = + MapUtils::create_oposit(WxFontUtils::from_weight); + +std::optional WxFontUtils::create_wxFont(const FontItem &fi, + const FontProp &fp) +{ + double point_size = static_cast(fp.size_in_mm); + wxFontInfo info(point_size); + if (fp.family.has_value()) { + auto it = to_family.find(*fp.style); + if (it != to_family.end()) info.Family(it->second); + } + if (fp.face_name.has_value()) { + wxString face_name(*fp.face_name); + info.FaceName(face_name); + } + if (fp.style.has_value()) { + auto it = to_style.find(*fp.style); + if (it != to_style.end()) info.Style(it->second); + } + if (fp.weight.has_value()) { + auto it = to_weight.find(*fp.weight); + if (it != to_weight.end()) info.Weight(it->second); + } + + // Improve: load descriptor instead of store to font property to 3mf + // switch (fi.type) { + // case FontItem::Type::wx_lin_font_descr: + // case FontItem::Type::wx_win_font_descr: + // case FontItem::Type::wx_mac_font_descr: + // case FontItem::Type::file_path: + // case FontItem::Type::undefined: + // default: + //} + + wxFont font(info); + if (!font.IsOk()) return {}; + return font; +} + +void WxFontUtils::update_property(FontProp &font_prop, const wxFont &font) +{ + // 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; + + 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 = from_family.find(wx_family); + if (it != from_family.end()) font_prop.family = it->second; + } + + wxFontStyle wx_style = font.GetStyle(); + if (wx_style != wxFONTSTYLE_NORMAL) { + auto it = from_style.find(wx_style); + if (it != from_style.end()) font_prop.style = it->second; + } + + wxFontWeight wx_weight = font.GetWeight(); + if (wx_weight != wxFONTWEIGHT_NORMAL) { + auto it = from_weight.find(wx_weight); + if (it != from_weight.end()) font_prop.weight = it->second; + } +} diff --git a/src/slic3r/Utils/WxFontUtils.hpp b/src/slic3r/Utils/WxFontUtils.hpp new file mode 100644 index 0000000000..a8b518ed9c --- /dev/null +++ b/src/slic3r/Utils/WxFontUtils.hpp @@ -0,0 +1,50 @@ +#ifndef slic3r_WxFontUtils_hpp_ +#define slic3r_WxFontUtils_hpp_ + +#include +#include +#include "libslic3r/Emboss.hpp" + +namespace Slic3r::GUI { + +// Help class to work with wx widget font object( wxFont ) +class WxFontUtils +{ +public: + // only static functions + WxFontUtils() = delete; + + // os specific load of wxFont + static std::optional load_font(const wxFont &font); + + static FontItem::Type get_actual_type(); + static FontItem get_font_item(const wxFont &font); + + // load font used by Operating system as default GUI + static FontItem get_os_font(); + static std::string get_human_readable_name(const wxFont &font); + + // serialize / deserialize font + static std::string store_wxFont(const wxFont &font); + static std::optional load_wxFont( + const std::string &font_descriptor); + + // Try to create similar font, loaded from 3mf from different Computer + static std::optional create_wxFont(const FontItem &fi, + const FontProp &fp); + // update font property by wxFont + static void update_property(FontProp &font_prop, const wxFont &font); + + // map to convert wxFont type to string and vice versa + static const std::map from_family; + static const std::map to_family; + + static const std::map from_style; + static const std::map to_style; + + static const std::map from_weight; + static const std::map to_weight; +}; + +} // namespace Slic3r::GUI +#endif // slic3r_WxFontUtils_hpp_