From 547e40b114f44c528107afd28135fcc964c45619 Mon Sep 17 00:00:00 2001 From: Arthur Norman Date: Tue, 7 Nov 2017 17:41:11 +0100 Subject: [PATCH] Add support for loading fonts from files. wxFont::AddPrivateFont() can now be used to load a font from a file for the applications private use. Update the font sample to show this. Closes #13568. --- docs/changes.txt | 1 + docs/doxygen/mainpages/const_cpp.h | 1 + docs/doxygen/overviews/font.h | 15 +++++ include/wx/font.h | 6 ++ include/wx/gtk/font.h | 4 ++ include/wx/msw/font.h | 2 + include/wx/osx/font.h | 2 + interface/wx/font.h | 38 ++++++++++++ samples/font/Makefile.in | 16 ++++- samples/font/font.bkl | 7 +++ samples/font/font.cpp | 57 +++++++++++++++++- samples/font/makefile.bcc | 6 +- samples/font/makefile.gcc | 8 ++- samples/font/makefile.vc | 6 +- samples/font/wxprivate.ttf | Bin 0 -> 21428 bytes src/common/fontcmn.cpp | 16 +++++ src/gtk/font.cpp | 60 +++++++++++++++++++ src/msw/graphics.cpp | 91 ++++++++++++++++++++++++++++- src/osx/fontutil.cpp | 38 ++++++++++++ 19 files changed, 366 insertions(+), 8 deletions(-) create mode 100644 samples/font/wxprivate.ttf diff --git a/docs/changes.txt b/docs/changes.txt index 5e464c7632..b35ec1a63f 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -109,6 +109,7 @@ All (GUI): - Allow wxWebView::RunScript() return values (Jose Lorenzo, GSoC 2017). - Allow using fractional pen widths with wxGraphicsContext (Adrien Tétar). +- Add support for loading fonts from external files (Arthur Norman). - Improve wxSVGFileDC to support more of wxDC API (Maarten Bent). - Add support for wxAuiManager and wxAuiPaneInfo to XRC (Andrea Zanellato). - Add support for wxSL_MIN_MAX_LABELS and wxSL_VALUE_LABEL to XRC (ousnius). diff --git a/docs/doxygen/mainpages/const_cpp.h b/docs/doxygen/mainpages/const_cpp.h index 1756d18a82..3fd074fb70 100644 --- a/docs/doxygen/mainpages/const_cpp.h +++ b/docs/doxygen/mainpages/const_cpp.h @@ -188,6 +188,7 @@ Currently the following symbols exist: @itemdef{wxHAS_IMAGES_IN_RESOURCES, Defined if Windows resource files or OS/2 resource files are available on the current platform.} @itemdef{wxHAS_POWER_EVENTS, Defined if wxPowerEvent are ever generated on the current platform.} +@itemdef{wxHAS_PRIVATE_FONTS, Defined if wxFont::AddPrivateFont() is implemented.} @itemdef{wxHAS_RADIO_MENU_ITEMS, Defined if the current port supports radio menu items (see wxMenu::AppendRadioItem).} @itemdef{wxHAS_RAW_BITMAP, Defined if direct access to bitmap data using the classes in @c wx/rawbmp.h is supported.} diff --git a/docs/doxygen/overviews/font.h b/docs/doxygen/overviews/font.h index d303c3960c..49635f37ec 100644 --- a/docs/doxygen/overviews/font.h +++ b/docs/doxygen/overviews/font.h @@ -75,4 +75,19 @@ implemented for Windows and Unix (GTK+ and Motif) ports only, all the methods are available for all the ports and should be used to make your program work correctly when they are implemented later. +@section overview_font_privateinfo Private font information + +Sometimes an application needs fonts that are not globally installed on the +system. On Macintosh/OSX this can be arranged by placing the desired fonts +within the Application Bundle in Contents/Resources/Fonts and using +the ATSApplicationFontsPath key to point there. The full details of the +procedure there can be found as OSX developer resources. For the GTK+ and +Windows ports it is possible to add TrueType fonts at run-time using +a sequence of calls to wxFont::AddPrivateFont() to give the names of files +containing font data, followed by a call to wxFont::ActivatePrivateFonts() +to complete the process of making the fonts available. These functions +return false if they fail. They should be called just once before any +graphics contexts have been created or other activity liable to use fonts +has happened. + */ diff --git a/include/wx/font.h b/include/wx/font.h index 3e0ee14102..f3731179f8 100644 --- a/include/wx/font.h +++ b/include/wx/font.h @@ -328,6 +328,12 @@ public: // from the string representation of wxNativeFontInfo static wxFont *New(const wxString& strNativeFontDesc); + // These functions can be used to load private fonts if supported by this + // platform (wxHAS_PRIVATE_FONTS is defined): first add one or more files + // and then activate all of them at once. + static bool AddPrivateFont(const wxString& filename); + static bool ActivatePrivateFonts(); + // comparison bool operator==(const wxFont& font) const; bool operator!=(const wxFont& font) const { return !(*this == font); } diff --git a/include/wx/gtk/font.h b/include/wx/gtk/font.h index 4d1220617d..669055c2c3 100644 --- a/include/wx/gtk/font.h +++ b/include/wx/gtk/font.h @@ -118,4 +118,8 @@ private: wxDECLARE_DYNAMIC_CLASS(wxFont); }; +#ifndef __WXMSW__ + #define wxHAS_PRIVATE_FONTS 1 +#endif + #endif // _WX_GTK_FONT_H_ diff --git a/include/wx/msw/font.h b/include/wx/msw/font.h index eec24baedb..16dae838a9 100644 --- a/include/wx/msw/font.h +++ b/include/wx/msw/font.h @@ -169,4 +169,6 @@ private: wxDECLARE_DYNAMIC_CLASS(wxFont); }; +#define wxHAS_PRIVATE_FONTS 1 + #endif // _WX_FONT_H_ diff --git a/include/wx/osx/font.h b/include/wx/osx/font.h index fcd82013f5..bd29cdf538 100644 --- a/include/wx/osx/font.h +++ b/include/wx/osx/font.h @@ -167,4 +167,6 @@ private: wxDECLARE_DYNAMIC_CLASS(wxFont); }; +#define wxHAS_PRIVATE_FONTS 1 + #endif // _WX_FONT_H_ diff --git a/interface/wx/font.h b/interface/wx/font.h index b16e12b507..392849b2d3 100644 --- a/interface/wx/font.h +++ b/interface/wx/font.h @@ -664,6 +664,44 @@ public: const wxNativeFontInfo *GetNativeFontInfo() const; + /** + Specify the name of a file containing a TrueType font to be + made available to the current application. + + This method can be used to allow this application to use the font from + the given file even if it is not globally installed on the system. + + Under OS X this method actually doesn't do anything other than check + for the existence of the file in the "Fonts" subdirectory of the + application bundle "Resources" directory. You are responsible for + actually making the font file available in this directory and setting + @c ATSApplicationFontsPath to @c Fonts value in your @c Info.plist + file. See also wxStandardPaths::GetResourcesDir(). + + Notice that this method must be called before any graphics contexts + have been created. + + Currently this method is implemented for all major platforms but you + may also test for @c wxHAS_PRIVATE_FONTS preprocessor symbol: if it is + not defined, this function and ActivatePrivatefonts() are not + implemented at all and always simply return false. + + @return @true if the font was added and ActivatePrivatefonts() should + be called next or @false if adding it failed. + + @since 2.9.5 + */ + static bool AddPrivateFont(const wxString& filename); + + /** + Make all fonts registered by AddPrivateFont() actually available. + + @return @true if the private fonts can now be used or @false on error. + + @since 2.9.5 + */ + static bool ActivatePrivatefonts(); + /** Gets the point size. diff --git a/samples/font/Makefile.in b/samples/font/Makefile.in index d4b565c83e..3291284be0 100644 --- a/samples/font/Makefile.in +++ b/samples/font/Makefile.in @@ -128,7 +128,7 @@ COND_wxUSE_REGEX_builtin___LIB_REGEX_p = \ ### Targets: ### -all: font$(EXEEXT) $(__font_bundle___depname) +all: font$(EXEEXT) $(__font_bundle___depname) data install: @@ -171,6 +171,18 @@ font$(EXEEXT): $(FONT_OBJECTS) $(__font___win32rc) @COND_PLATFORM_MACOSX_1@font_bundle: $(____font_BUNDLE_TGT_REF_DEP) +data: + @mkdir -p . + @for f in wxprivate.ttf; do \ + if test ! -f ./$$f -a ! -d ./$$f ; \ + then x=yep ; \ + else x=`find $(srcdir)/$$f -newer ./$$f -print` ; \ + fi; \ + case "$$x" in ?*) \ + cp -pRf $(srcdir)/$$f . ;; \ + esac; \ + done + font_sample_rc.o: $(srcdir)/../../samples/sample.rc $(WINDRES) -i$< -o$@ --define __WX$(TOOLKIT)__ $(__WXUNIV_DEFINE_p_1) $(__DEBUG_DEFINE_p_1) $(__EXCEPTIONS_DEFINE_p_1) $(__RTTI_DEFINE_p_1) $(__THREAD_DEFINE_p_1) --include-dir $(srcdir) $(__DLLFLAG_p_1) --include-dir $(srcdir)/../../samples $(__RCDEFDIR_p) --include-dir $(top_srcdir)/include @@ -181,4 +193,4 @@ font_font.o: $(srcdir)/font.cpp # Include dependency info, if present: @IF_GNU_MAKE@-include ./.deps/*.d -.PHONY: all install uninstall clean distclean font_bundle +.PHONY: all install uninstall clean distclean font_bundle data diff --git a/samples/font/font.bkl b/samples/font/font.bkl index cb88a72e12..bfa8333cb2 100644 --- a/samples/font/font.bkl +++ b/samples/font/font.bkl @@ -9,4 +9,11 @@ base + + + wxprivate.ttf + + + + diff --git a/samples/font/font.cpp b/samples/font/font.cpp index ed31b2dc26..b02544644e 100644 --- a/samples/font/font.cpp +++ b/samples/font/font.cpp @@ -29,6 +29,7 @@ #include "wx/fontmap.h" #include "wx/encconv.h" #include "wx/splitter.h" +#include "wx/stdpaths.h" #include "wx/textfile.h" #include "wx/settings.h" @@ -130,6 +131,7 @@ public: void OnSetFamily(wxCommandEvent& event); void OnSetFaceName(wxCommandEvent& event); void OnSetEncoding(wxCommandEvent& event); + void OnPrivateFont(wxCommandEvent& event); protected: bool DoEnumerateFamilies(bool fixedWidthOnly, @@ -212,6 +214,8 @@ enum Font_SetFamily, Font_SetFaceName, Font_SetEncoding, + + Font_Private, Font_Max }; @@ -265,6 +269,7 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(Font_EnumFamilies, MyFrame::OnEnumerateFamilies) EVT_MENU(Font_EnumFixedFamilies, MyFrame::OnEnumerateFixedFamilies) EVT_MENU(Font_EnumEncodings, MyFrame::OnEnumerateEncodings) + EVT_MENU(Font_Private, MyFrame::OnPrivateFont) wxEND_EVENT_TABLE() // Create a new application object: this macro will allow wxWidgets to create @@ -287,6 +292,7 @@ bool MyApp::OnInit() { if ( !wxApp::OnInit() ) return false; + wxString privfont = argv[0].BeforeLast('/'); // Create the main application window MyFrame *frame = new MyFrame(wxT("Font wxWidgets demo"), @@ -381,7 +387,6 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) wxT("Default font for user interface objects such as menus and dialog boxes. ")); menuSelect->Append(Font_SystemSettings, wxT("System fonts"), menuSettingFonts); - menuSelect->AppendSeparator(); menuSelect->Append(Font_EnumFamilies, wxT("Enumerate font &families\tCtrl-F")); menuSelect->Append(Font_EnumFixedFamilies, @@ -392,6 +397,42 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) wxT("Find font for en&coding...\tCtrl-C"), wxT("Find font families for given encoding")); +#ifdef wxHAS_PRIVATE_FONTS + // Try to use a private font, under most platforms we just look for it in + // the current directory but under OS X it must be in a specific location + // so look for it there. + // + // For OS X you also need to ensure that you actually do put wxprivate.ttf + // in font.app/Contents/Resources/Fonts and add the following snippet + // + // + // + // ... + // ATSApplicationFontsPath + // Fonts + // ... + // + // + // + // to your font.app/Contents/Info.plist. + + wxString privfont; +#ifdef __WXOSX__ + privfont << wxStandardPaths::Get().GetResourcesDir() << "/Fonts/"; +#endif + privfont << "wxprivate.ttf"; + + if ( wxFont::AddPrivateFont(privfont) && + wxFont::ActivatePrivateFonts() ) + { + menuSelect->AppendSeparator(); + menuSelect->Append(Font_Private, + "Select private font", + "Select a font available only in this application"); + } +#endif // wxHAS_PRIVATE_FONTS + + // now append the freshly created menu to the menu bar... wxMenuBar *menuBar = new wxMenuBar; menuBar->Append(menuFile, wxT("&File")); @@ -873,6 +914,20 @@ void MyFrame::OnSelectFont(wxCommandEvent& WXUNUSED(event)) } } +void MyFrame::OnPrivateFont(wxCommandEvent& WXUNUSED(event)) +{ + wxFont font(GetCanvas()->GetTextFont()); + if (font.SetFaceName("wxprivate")) + { + wxASSERT_MSG( font.IsOk(), wxT("The font should now be valid")) ; + DoChangeFont(font); + } + else + { + wxLogError("Failed to use private font."); + } +} + void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event)) { // true is to force the frame to close diff --git a/samples/font/makefile.bcc b/samples/font/makefile.bcc index 083765af16..65f9a92a1f 100644 --- a/samples/font/makefile.bcc +++ b/samples/font/makefile.bcc @@ -213,7 +213,7 @@ $(OBJS): ### Targets: ### -all: $(OBJS)\font.exe +all: $(OBJS)\font.exe data clean: -if exist $(OBJS)\*.obj del $(OBJS)\*.obj @@ -231,6 +231,10 @@ $(OBJS)\font.exe: $(FONT_OBJECTS) $(OBJS)\font_sample.res c0w32.obj $(FONT_OBJECTS),$@,, $(__WXLIB_CORE_p) $(__WXLIB_BASE_p) $(__WXLIB_MONO_p) $(__LIB_SCINTILLA_IF_MONO_p) $(__LIB_TIFF_p) $(__LIB_JPEG_p) $(__LIB_PNG_p) wxzlib$(WXDEBUGFLAG).lib wxregex$(WXUNICODEFLAG)$(WXDEBUGFLAG).lib wxexpat$(WXDEBUGFLAG).lib $(EXTRALIBS_FOR_BASE) $(__CAIRO_LIB_p) ole2w32.lib oleacc.lib import32.lib cw32$(__THREADSFLAG_5)$(__RUNTIME_LIBS_8).lib,, $(OBJS)\font_sample.res | +data: + if not exist $(OBJS) mkdir $(OBJS) + for %f in (wxprivate.ttf) do if not exist $(OBJS)\%f copy .\%f $(OBJS) + $(OBJS)\font_sample.res: .\..\..\samples\sample.rc brcc32 -32 -r -fo$@ -i$(BCCDIR)\include -d__WXMSW__ $(__WXUNIV_DEFINE_p_1) $(__DEBUG_DEFINE_p_1) $(__NDEBUG_DEFINE_p_1) $(__EXCEPTIONS_DEFINE_p_1) $(__RTTI_DEFINE_p_1) $(__THREAD_DEFINE_p_1) $(__UNICODE_DEFINE_p_1) -i$(SETUPHDIR) -i.\..\..\include $(____CAIRO_INCLUDEDIR_FILENAMES_1_p) -i. $(__DLLFLAG_p_1) -i.\..\..\samples -i$(BCCDIR)\include\windows\sdk -dNOPCH .\..\..\samples\sample.rc diff --git a/samples/font/makefile.gcc b/samples/font/makefile.gcc index bcad6221d1..47f208ff18 100644 --- a/samples/font/makefile.gcc +++ b/samples/font/makefile.gcc @@ -210,7 +210,7 @@ $(OBJS): ### Targets: ### -all: $(OBJS)\font.exe +all: $(OBJS)\font.exe data clean: -if exist $(OBJS)\*.o del $(OBJS)\*.o @@ -220,13 +220,17 @@ clean: $(OBJS)\font.exe: $(FONT_OBJECTS) $(OBJS)\font_sample_rc.o $(CXX) -o $@ $(FONT_OBJECTS) $(__DEBUGINFO) $(__THREADSFLAG) -L$(LIBDIRNAME) -Wl,--subsystem,windows -mwindows $(____CAIRO_LIBDIR_FILENAMES_p) $(LDFLAGS) $(__WXLIB_CORE_p) $(__WXLIB_BASE_p) $(__WXLIB_MONO_p) $(__LIB_SCINTILLA_IF_MONO_p) $(__LIB_TIFF_p) $(__LIB_JPEG_p) $(__LIB_PNG_p) -lwxzlib$(WXDEBUGFLAG) -lwxregex$(WXUNICODEFLAG)$(WXDEBUGFLAG) -lwxexpat$(WXDEBUGFLAG) $(EXTRALIBS_FOR_BASE) $(__CAIRO_LIB_p) -lkernel32 -luser32 -lgdi32 -lcomdlg32 -lwinspool -lwinmm -lshell32 -lshlwapi -lcomctl32 -lole32 -loleaut32 -luuid -lrpcrt4 -ladvapi32 -lversion -lwsock32 -lwininet -loleacc +data: + if not exist $(OBJS) mkdir $(OBJS) + for %%f in (wxprivate.ttf) do if not exist $(OBJS)\%%f copy .\%%f $(OBJS) + $(OBJS)\font_sample_rc.o: ./../../samples/sample.rc $(WINDRES) -i$< -o$@ --define __WXMSW__ $(__WXUNIV_DEFINE_p_1) $(__DEBUG_DEFINE_p_1) $(__NDEBUG_DEFINE_p_1) $(__EXCEPTIONS_DEFINE_p_1) $(__RTTI_DEFINE_p_1) $(__THREAD_DEFINE_p_1) $(__UNICODE_DEFINE_p_1) --include-dir $(SETUPHDIR) --include-dir ./../../include $(__CAIRO_INCLUDEDIR_p) --include-dir . $(__DLLFLAG_p_1) --include-dir ./../../samples --define NOPCH $(OBJS)\font_font.o: ./font.cpp $(CXX) -c -o $@ $(FONT_CXXFLAGS) $(CPPDEPS) $< -.PHONY: all clean +.PHONY: all clean data SHELL := $(COMSPEC) diff --git a/samples/font/makefile.vc b/samples/font/makefile.vc index 10c1cccd58..431edcac5a 100644 --- a/samples/font/makefile.vc +++ b/samples/font/makefile.vc @@ -339,7 +339,7 @@ $(OBJS): ### Targets: ### -all: $(OBJS)\font.exe +all: $(OBJS)\font.exe data clean: -if exist $(OBJS)\*.obj del $(OBJS)\*.obj @@ -354,6 +354,10 @@ $(OBJS)\font.exe: $(FONT_OBJECTS) $(OBJS)\font_sample.res $(FONT_OBJECTS) $(FONT_RESOURCES) $(__WXLIB_CORE_p) $(__WXLIB_BASE_p) $(__WXLIB_MONO_p) $(__LIB_SCINTILLA_IF_MONO_p) $(__LIB_TIFF_p) $(__LIB_JPEG_p) $(__LIB_PNG_p) wxzlib$(WXDEBUGFLAG).lib wxregex$(WXUNICODEFLAG)$(WXDEBUGFLAG).lib wxexpat$(WXDEBUGFLAG).lib $(EXTRALIBS_FOR_BASE) $(__CAIRO_LIB_p) kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib shlwapi.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib version.lib wsock32.lib wininet.lib << +data: + if not exist $(OBJS) mkdir $(OBJS) + for %f in (wxprivate.ttf) do if not exist $(OBJS)\%f copy .\%f $(OBJS) + $(OBJS)\font_sample.res: .\..\..\samples\sample.rc rc /fo$@ /d WIN32 $(____DEBUGRUNTIME_3_p_1) /d _CRT_SECURE_NO_DEPRECATE=1 /d _CRT_NON_CONFORMING_SWPRINTFS=1 /d _SCL_SECURE_NO_WARNINGS=1 $(__NO_VC_CRTDBG_p_1) /d __WXMSW__ $(__WXUNIV_DEFINE_p_1) $(__DEBUG_DEFINE_p_1) $(__NDEBUG_DEFINE_p_1) $(__EXCEPTIONS_DEFINE_p_1) $(__RTTI_DEFINE_p_1) $(__THREAD_DEFINE_p_1) $(__UNICODE_DEFINE_p_1) /i $(SETUPHDIR) /i .\..\..\include $(____CAIRO_INCLUDEDIR_FILENAMES_1_p) /i . $(__DLLFLAG_p_1) /d _WINDOWS /i .\..\..\samples /d NOPCH .\..\..\samples\sample.rc diff --git a/samples/font/wxprivate.ttf b/samples/font/wxprivate.ttf new file mode 100644 index 0000000000000000000000000000000000000000..82d77fe5f3bd0fa3ed78eb5ce51cf9aae94c042a GIT binary patch literal 21428 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 z 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 literal 0 HcmV?d00001 diff --git a/src/common/fontcmn.cpp b/src/common/fontcmn.cpp index ae9b82607f..f17d045167 100644 --- a/src/common/fontcmn.cpp +++ b/src/common/fontcmn.cpp @@ -1137,4 +1137,20 @@ bool wxFromString(const wxString& str, wxFontBase *font) return font->SetNativeFontInfo(str); } +#ifndef wxHAS_PRIVATE_FONTS +// Adding private fonts is not supported on this platform, so provide the +// functions that would be used, but make them no-ops that return a code +// that indicates failure. + +bool wxFontBase::AddPrivateFont(const wxString& WXUNUSED(filename)) +{ + return false; +} + +bool wxFontBase::ActivatePrivateFonts() +{ + return false; +} + +#endif // !wxHAS_PRIVATE_FONTS diff --git a/src/gtk/font.cpp b/src/gtk/font.cpp index 54ec1b403c..297c567e25 100644 --- a/src/gtk/font.cpp +++ b/src/gtk/font.cpp @@ -31,6 +31,11 @@ #include "wx/gtk/private.h" +#ifdef GDK_WINDOWING_X11 + #include + #include +#endif + // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- @@ -551,3 +556,58 @@ bool wxFont::GTKSetPangoAttrs(PangoLayout* layout) const return true; } + + +// ---------------------------------------------------------------------------- +// Support for adding private fonts +// ---------------------------------------------------------------------------- + +#ifdef wxHAS_PRIVATE_FONTS + +namespace +{ + FcConfig* gs_fcConfig = NULL; +} + +bool wxFontBase::AddPrivateFont(const wxString& filename) +{ + if ( !gs_fcConfig ) + { + gs_fcConfig = FcConfigGetCurrent(); + if ( !gs_fcConfig ) + { + gs_fcConfig = FcConfigCreate(); + if ( !gs_fcConfig ) + { + wxLogError(_("Failed to add custom font \"%s\" as " + "font configuration object creation failed."), + filename); + return false; + } + } + } + + if ( !FcConfigAppFontAddFile(gs_fcConfig, + reinterpret_cast( + static_cast(filename.utf8_str()) + )) ) + { + wxLogError(_("Failed to add custom font \"%s\"."), filename); + return false; + } + + return true; +} + +bool wxFontBase::ActivatePrivateFonts() +{ + if ( !FcConfigSetCurrent(gs_fcConfig) ) + { + wxLogError(_("Failed to update current font configuration.")); + return false; + } + + return true; +} + +#endif // wxHAS_PRIVATE_FONTS diff --git a/src/msw/graphics.cpp b/src/msw/graphics.cpp index b518dbdd99..fd5957e59a 100644 --- a/src/msw/graphics.cpp +++ b/src/msw/graphics.cpp @@ -37,6 +37,7 @@ #include "wx/dcprint.h" #endif +#include "wx/filename.h" #include "wx/stack.h" #include "wx/private/graphics.h" @@ -959,6 +960,47 @@ wxGDIPlusBrushData::CreateRadialGradientBrush(wxDouble xo, wxDouble yo, SetGradientStops(brush, stops, true); } +//----------------------------------------------------------------------------- +// Support for adding private fonts +//----------------------------------------------------------------------------- + +namespace +{ + +wxArrayString gs_privateFontFileNames; +Gdiplus::PrivateFontCollection* gs_privateFonts = NULL; +Gdiplus::FontFamily* gs_pFontFamily = NULL; + +} // anonymous namespace + +bool wxFontBase::AddPrivateFont(const wxString& filename) +{ + if ( !wxFileName::FileExists(filename) ) + { + wxLogError(_("Font file \"%s\" doesn't exist."), filename); + return false; + } + + gs_privateFontFileNames.Add(filename); + return true; +} + +bool wxFontBase::ActivatePrivateFonts() +{ + const int n = gs_privateFontFileNames.size(); + for ( int i = 0 ; i < n; i++ ) + { + const wxString& fname = gs_privateFontFileNames[i]; + if ( !AddFontResourceEx(fname.t_str(), FR_PRIVATE, 0) ) + { + wxLogSysError(_("Font file \"%s\" couldn't be loaded"), + fname); + } + } + + return true; +} + //----------------------------------------------------------------------------- // wxGDIPlusFont implementation //----------------------------------------------------------------------------- @@ -970,7 +1012,31 @@ wxGDIPlusFontData::Init(const wxString& name, const wxColour& col, Unit fontUnit) { - m_font = new Font(name.wc_str(), size, style, fontUnit); + // If the user has registered any private fonts, they should be used in + // preference to any system-wide ones. + m_font = NULL; + if ( gs_privateFonts ) + { + const int count = gs_privateFonts->GetFamilyCount(); + + // We should find all the families, i.e. "found" should be "count". + int found = 0; + gs_privateFonts->GetFamilies(count, gs_pFontFamily, &found); + + for ( int j = 0 ; j < found; j++ ) + { + wchar_t familyName[LF_FACESIZE]; + int rc = gs_pFontFamily[j].GetFamilyName(familyName); + if ( rc == 0 && lstrcmp(name, familyName) == 0 ) + { + m_font = new Font(&gs_pFontFamily[j], size, style, fontUnit); + break; + } + } + } + + if ( !m_font ) + m_font = new Font(name, size, style, fontUnit); m_textBrush = new SolidBrush(wxColourToColor(col)); } @@ -2275,6 +2341,20 @@ void wxGDIPlusRenderer::Load() { wxLogTrace("gdiplus", "successfully initialized GDI+"); m_loaded = 1; + + // Make private fonts available to GDI+, if any. + const int n = gs_privateFontFileNames.size(); + if ( n ) + { + gs_privateFonts = new Gdiplus::PrivateFontCollection(); + for ( int i = 0 ; i < n; i++ ) + { + const wxString& fname = gs_privateFontFileNames[i]; + gs_privateFonts->AddFontFile(fname.t_str()); + } + + gs_pFontFamily = new Gdiplus::FontFamily[n]; + } } else { @@ -2289,6 +2369,15 @@ void wxGDIPlusRenderer::Unload() { GdiplusShutdown(m_gditoken); m_gditoken = 0; + + if ( gs_privateFonts ) + { + delete gs_privateFonts; + gs_privateFonts = NULL; + + delete[] gs_pFontFamily; + gs_pFontFamily = NULL; + } } m_loaded = -1; // next Load() will try again } diff --git a/src/osx/fontutil.cpp b/src/osx/fontutil.cpp index 5828657448..dd67c6322f 100644 --- a/src/osx/fontutil.cpp +++ b/src/osx/fontutil.cpp @@ -24,6 +24,8 @@ #include "wx/fontutil.h" #include "wx/fontmap.h" #include "wx/encinfo.h" +#include "wx/filename.h" +#include "wx/stdpaths.h" #include "wx/tokenzr.h" @@ -63,6 +65,42 @@ wxString wxNativeEncodingInfo::ToString() const return s; } +// ---------------------------------------------------------------------------- +// Private Fonts +// ---------------------------------------------------------------------------- + +// On OSX one can provide private fonts simply by putting the font files in +// with the resources in your application bundle. So the API for adding fonts +// does not do anything except checking that the file you pass to it actually +// does exist and is in the correct directory. + +bool wxFontBase::AddPrivateFont(const wxString& filename) +{ + wxFileName fn(filename); + if ( !fn.FileExists() ) + { + wxLogError(_("Font file \"%s\" doesn't exist."), filename); + return false; + } + + wxString fontsDir; + fontsDir << wxStandardPaths::Get().GetResourcesDir() << "/Fonts"; + if ( fn.GetPath() != fontsDir ) + { + wxLogError(_("Font file \"%s\" cannot be used as it is not inside " + "the font directory \"%s\"."), filename, fontsDir); + return false; + } + + return true; +} + +bool wxFontBase::ActivatePrivateFonts() +{ + // Nothing to do here. + return true; +} + // ---------------------------------------------------------------------------- // helper functions // ----------------------------------------------------------------------------