RE: [fltk.general] Setting font for FL_Button - [General Use]

312 views
Skip to first unread message

MacArthur, Ian (Leonardo, UK)

unread,
Sep 27, 2017, 5:19:46 AM9/27/17
to fltkg...@googlegroups.com

Hi Håvard


> I want to use font "Liberation Sans Narrow" for a FL_Button widget.

> Use the 'fonts' program in the examples directory and identify font 151
> to look ok.

OK - never do that.

Font indexes are generated at runtime and are (at best) host specific, since they relate directly to the number of fonts on a given machine, and the order in which they are "discovered" by the font engine when it loads all its font handles...

If you run the same code twice on a particular machine, you will *probably* get the same font indices both times, but it is not guaranteed.

Anything that alters the order of font discovery can change it (you might install a new, unrelated, program that adds a few custom fonts; bang, your indices all change.)

Better to load the font face by name, always.

> However when I transfer my program to another (linux mint) machine, the
> font has changes.

> What is really happening here and how can I make sure I use the
> "Liberation Sans Narrow" font ?


Others have suggested a few options; what I usually do in practice depends on whether I know for certain all the target machines will have the font face or not. "Liberation Sans", for example, is widely supported, but not all machines will have "Liberation Sans Narrow".

In the "easy" case, I usually just load the font by name, then assign it to a widget, e.g.:

static Fl_Font our_font = (FL_FREE_FONT + 1);

Fl::set_font(our_font, "Liberation Sans Narrow");
button->labelfont(our_font);

And that pretty much ought to do the job.



If you are not sure that every target machine actually has the font face you want, and you absolutely have to have that face, then you can load a private font into your app, separate from the system fonts provided from the host system.

You do that as follows (see code below, which is a bit of a guddle to understand, but does work.)

Note that this code assumes the following things (which may not be true in every case!)

- The CWD when your app runs is the dir its exe exists in, so it can find the font folder; usually true under WIN32, often true under OSX, sometimes true under X11. If in doubt, set the font load path to a well-known path e.g. "~/.font"..." or set the CWD to something suitable.
- The app CWD directory has a sub-folder called "font" with the font in it
- The font used is in a file called "fltk-test-regular.otf"
- The name of the font, stored in the font file and read when it is loaded, is "fltk"

I have a test font that I use for this, that meets those criteria, but it is 64K so unless you want to test it, I'll not post it here.

This code loads (and unloads) a private font at runtime for the test app to use. It works on WIN32, OSX, X11.

///////////////////////////////////////////////////////////

/* Test the loading of application specific fonts.
* Uses platform-specific coding and API's. */

#ifdef _WIN32
# define _WIN32_WINNT 0x0500
# include <windows.h>
#elif __APPLE__
/* Nothing to add for OSX? Could (instead) load font from bundle resource. */
#else /* Assume X11 with XFT/fontconfig - will break on systems using legacy Xlib fonts */
//# include <X11/Xft/Xft.h>
# include <fontconfig/fontconfig.h>
# define USE_XFT 1
#endif

/* Fltk headers */
#include <FL/Fl.H>
#include <FL/x.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/fl_draw.H>

#include <stdio.h>
#include <stdint.h>

static int loaded_font = 0;

static Fl_Font our_font = (FL_FREE_FONT + 1);

/*************************************************************/
// Create some portability wrappers
#ifdef _WIN32
# define LOAD_FONT_API(PATH) AddFontResourceEx((PATH),FR_PRIVATE,0)
# define UNLOAD_FONT_API(PATH) RemoveFontResourceEx((PATH),FR_PRIVATE,0)
#elif __APPLE__
/* Nothing to add for OSX? Should load font from bundle resource. */
# define LOAD_FONT_API(PATH) (0)
# define UNLOAD_FONT_API(PATH)
#else /* Assume X11 with XFT/fontconfig - will break on systems using legacy Xlib fonts */
# define LOAD_FONT_API(PATH) (int)FcConfigAppFontAddFile(NULL,(const FcChar8 *)(PATH))
# define UNLOAD_FONT_API(PATH) FcConfigAppFontClear(NULL)
#endif

/*************************************************************/
class pg_preview : public Fl_Box
{
public:
// constructor
pg_preview(int x, int y, int w, int h) : Fl_Box(x,y,w,h) {}
private:
void draw(void); // draw method
};
/*************************************************************/
void pg_preview::draw(void)
{
static const char notes[] = ":\xf0\x9d\x85\xa0-\xf0\x9d\x85\xa1-\xf0\x9d\x85\xa2-\xf0\x9d\x85\xa1-\xf0\x9d\x85\xa0:";
const char solo[] = "\xf0\x9d\x85\xa0";
char clefG[8]; // 1D11E

int ll = fl_utf8encode(0x1d11e, clefG);
clefG[ll] = 0;

int x0 = x(); // origin is current window position for Fl_Box
int y0 = y();
int w0 = w();
int h0 = h();

// set the background colour
fl_color(FL_WHITE);
fl_rectf(x0, y0, w0, h0);


// Basic string in default font test
fl_font(FL_HELVETICA, 40);
fl_color(FL_BLACK);
fl_draw("This is the default font", 25, 100);

// Test the "private" font the app has loaded for its own use
fl_font(our_font, 40);
fl_color(FL_BLACK);
fl_draw("This is the private font we loaded", 25, 200);
fl_font(our_font, 30);
fl_draw("Here are some glyphs from the SMP using the test font", 25, 250);

fl_font(our_font, 40);
// Special notes test
fl_draw(notes, 25, 300);
// SINGLE note test
fl_draw(solo, 225, 300);
// CLEF test
fl_draw(clefG, 300, 300); // clefG
} // draw method

/*****************************************************************************/
static void load_extra_font(void)
{
/* Load the font using the appropriate platform API */
loaded_font = LOAD_FONT_API("./font/fltk-test-regular.otf");

/* set the extra font */
if (loaded_font)
{
Fl::set_font(our_font, "fltk");
}
} // load_extra_font

/*****************************************************************************/
static void free_extra_font(void)
{
if (loaded_font)
{
UNLOAD_FONT_API("./font/fltk-test-regular.otf");
}
loaded_font = 0;
} // free_extra_font

/*****************************************************************************/
int main(int argc, char **argv)
{
Fl_Window *main_win = new Fl_Window(600, 400, "Load Special Font");

/* Load our additional font for musical symbols */
load_extra_font();

pg_preview *box = new pg_preview(5, 5, (main_win->w() - 10), (main_win->h() - 10));
box->type(FL_FLAT_BOX);

main_win->end();
main_win->show(argc, argv);
main_win->resizable(box);

/* Now run the main event loop */
int result = Fl::run();

/* Unload the rendering font */
free_extra_font();

return result;
}
/* end of file */







Leonardo MW Ltd
Registered Office: Sigma House, Christopher Martin Road, Basildon, Essex SS14 3EL
A company registered in England & Wales. Company no. 02426132
********************************************************************
This email and any attachments are confidential to the intended
recipient and may also be privileged. If you are not the intended
recipient please delete it from your system and notify the sender.
You should not copy it or use it for any purpose nor disclose or
distribute its contents to any other person.
********************************************************************

MacArthur, Ian (Leonardo, UK)

unread,
Sep 27, 2017, 5:27:58 AM9/27/17
to fltkg...@googlegroups.com

> This code loads (and unloads) a private font at runtime for the test
> app to use. It works on WIN32, OSX, X11.

Doh!

Wrong version - this one doesn’t load the OSX font locally, it expects the private font to be loaded through the OSX bundle...

WIN32 and X11 should be fine though!

I'll see if I can find the version that loads the OSX private font directly; I don’t have it here, nor any Mac readily to hand...

MacArthur, Ian (Leonardo, UK)

unread,
Sep 27, 2017, 6:53:22 AM9/27/17
to fltkg...@googlegroups.com
> > This code loads (and unloads) a private font at runtime for the test
> > app to use. It works on WIN32, OSX, X11.
>
> Doh!
>
> Wrong version - this one doesn’t load the OSX font locally, it expects
> the private font to be loaded through the OSX bundle...
>
> WIN32 and X11 should be fine though!
>
> I'll see if I can find the version that loads the OSX private font
> directly; I don’t have it here, nor any Mac readily to hand...
>

Hah! Found it - I did have a copy of the Mac-compatible version after all.

Basically the same test harness as before, but with support for loading private fonts under OSX too.
In case anyone (other than me) actually wants this ability!

//////////////////////////////////////////////////////////////////////////////

/* Test the loading of application specific fonts.
* Uses platform-specific coding and API's. */

#ifdef _WIN32
# define _WIN32_WINNT 0x0501 /* need at least WinXP for this API, I think */
# include <windows.h>
#elif __APPLE__
# include <ApplicationServices/ApplicationServices.h>
#else /* Assume X11 with XFT/fontconfig - this will break on systems using legacy Xlib fonts */
# include <fontconfig/fontconfig.h>
# define USE_XFT 1
#endif

/* Fltk headers */
#include <FL/Fl.H>
#include <FL/x.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/fl_draw.H>

/*************************************************************/
static int loaded_font = 0;
static Fl_Font test_font = (FL_FREE_FONT + 1);

/*************************************************************/
// Create some portability wrappers
#ifdef _WIN32

# define i_load_private_font(PATH) AddFontResourceEx((PATH),FR_PRIVATE,0)
# define v_unload_private_font(PATH) RemoveFontResourceEx((PATH),FR_PRIVATE,0)

#elif __APPLE__
# include <stdio.h> // I use printf for error reporting in the Apple specific code!
/* For the Apple case, we need to do a bit more work, since we need to convert
* the PATH into a CFURLRef before we can call CTFontManagerRegisterFontsForURL()
* with it.
* Otherwise, all three systems would have basically the same structure here! */
static int i_load_private_font (const char *pf)
{
int result = 0;
CFErrorRef err;
// Make a URL from the font name given
CFURLRef fontURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
(const UInt8*)pf,
strlen(pf),
false);
// Try to load the font file
if (CTFontManagerRegisterFontsForURL(fontURL, kCTFontManagerScopeProcess, &err))
{
result = 1; // OK, we loaded the font, set this non-zero
}
else
{
printf ("Failed loading font: %s\n", pf);
}
// discard the fontURL
if (fontURL) CFRelease (fontURL);
return result;
} // i_load_private_font

static void v_unload_private_font (const char *pf)
{
CFErrorRef err;
// Make a URL from the font name given
CFURLRef fontURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
(const UInt8*)pf,
strlen(pf),
false);
// Try to unregister the font
CTFontManagerUnregisterFontsForURL(fontURL, kCTFontManagerScopeProcess, &err);
if (fontURL) CFRelease (fontURL);
} // v_unload_private_font

#else /* Assume X11 with XFT/fontconfig - will break on systems using legacy Xlib fonts */

# define i_load_private_font(PATH) (int)FcConfigAppFontAddFile(NULL,(const FcChar8 *)(PATH))
# define v_unload_private_font(PATH) FcConfigAppFontClear(NULL)

#endif

/*************************************************************/
class page_view : public Fl_Box
{
public:
// constructor
page_view(int x, int y, int w, int h) : Fl_Box(x,y,w,h) {}
private:
void draw(void); // draw method
};
/*************************************************************/
void page_view::draw(void)
{
/* This is the "raw" UTF8 encoding for some symbols from the Supplementary Plane. Musical Notation in this case */
static const char notes[] = ":\xf0\x9d\x85\xa0-\xf0\x9d\x85\xa1-\xf0\x9d\x85\xa2-\xf0\x9d\x85\xa1-\xf0\x9d\x85\xa0:";
const char solo[] = "\xf0\x9d\x85\xa0";
char clefG[8]; // 1D11E

/* Convert a Unicode value, 0x1D11E in this case (a G clef), into a UTF8 string using the fltk helper function */
int ll = fl_utf8encode(0x1d11e, clefG);
clefG[ll] = 0; // make sure UTF8 string is NULL terminated

int x0 = x(); // origin is w.r.t. the Fl_Box
int y0 = y();
int w0 = w();
int h0 = h();
fl_push_clip (x0, y0, w0, h0); // clip to the current page view

// set the background colour
fl_color(FL_WHITE);
fl_rectf(x0, y0, w0, h0);

// Basic string in default font test
fl_font(FL_HELVETICA, 30);
fl_color(FL_BLACK);
fl_draw("This is the default font (FL_HELVETICA)", x0+25, y0+100);

// Test the "private" font the application has loaded for its own use
fl_font(test_font, 40);
fl_color(FL_BLACK);
fl_draw("This is the private font we loaded", x0+25, y0+200);
fl_font(test_font, 25);
fl_color(FL_DARK_RED);
fl_draw("Here are some glyphs from the SMP using the test font", x0+25, y0+250);

fl_font(test_font, 40);
fl_color(FL_BLACK);
// Special notes test
fl_draw(notes, x0+25, y0+300);
// SINGLE note test
fl_draw(solo, x0+225, y0+300);
// CLEF test
fl_draw(clefG, x0+300, y0+300); // clefG

fl_pop_clip(); // restore the default clip region
} // draw method

/*****************************************************************************/
static void load_extra_font(void)
{
/* Load the font using the appropriate platform API */
loaded_font = i_load_private_font("./font/fltk-test-regular.otf");

/* set the extra font */
if (loaded_font)
{
Fl::set_font(test_font, "fltk");
}
} // load_extra_font

/*****************************************************************************/
static void free_extra_font(void)
{
if (loaded_font)
{
v_unload_private_font("./font/fltk-test-regular.otf");
}
loaded_font = 0;
} // free_extra_font

/*****************************************************************************/
int main(int argc, char **argv)
{
Fl_Window *main_win = new Fl_Window(650, 400, "Load Special Font");

/* Load our additional font for musical symbols from the Supplementary Plane */
load_extra_font();

page_view *test_page = new page_view(5, 5, (main_win->w() - 10), (main_win->h() - 10));
test_page->type(FL_FLAT_BOX);

main_win->end();
main_win->resizable(test_page);

// Show the test window
main_win->show(argc, argv);

/* Run the fltk event loop */
int result = Fl::run();

/* Unload the private font we loaded earlier */
free_extra_font();

return result;
}
/* end of file */



K Dmitrij

unread,
Sep 27, 2017, 7:44:57 AM9/27/17
to fltkg...@googlegroups.com

Ian, very cool snippet ! thanks for share it  !




From: MacArthur, Ian
 
--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Google Groups allows you to create and participate in online forums and email-based groups with a rich experience for community conversations.


Reply all
Reply to author
Forward
0 new messages