Lazarus

Programming => General => Topic started by: lainz on February 02, 2023, 04:08:47 pm

Title: Help Needed: Cross Platform Third Party Fonts
Post by: lainz on February 02, 2023, 04:08:47 pm
Hi, seems that's possible to use third party fonts on Windows, Linux and macOS.

Solutions in this thread

for Windows
https://forum.lazarus.freepascal.org/index.php/topic,62134.msg469606.html#msg469606

for Linux
https://forum.lazarus.freepascal.org/index.php/topic,62134.msg469714.html#msg469714

Unfortunatelly my Linux VM's are failing sometimes with Lazarus, and my macOS version is old, failing lazarus as well.

But I have the links:

macOS, how to bundle Fonts and use
https://stackoverflow.com/a/2444772/8852689 (https://stackoverflow.com/a/2444772/8852689)

Maybe a script can be created to put the font in the bundle, and register it too...

Windows, how to embed Fonts and use
https://forum.lazarus.freepascal.org/index.php/topic,21032.msg252219.html#msg252219 (https://forum.lazarus.freepascal.org/index.php/topic,21032.msg252219.html#msg252219)

The code is already there, maybe pack it into a single unit...
Edit: and to test it in Windows 11, we had problems with this or other similar code, the app won't start with this code or something like that...

Maybe just installing the font in the system with a dll call?

Linux
https://itsfoss.com/install-fonts-ubuntu/ (https://itsfoss.com/install-fonts-ubuntu/)

Basically on Ubuntu / Mint one can create HOME/.fonts directory and put the fonts there, maybe that can be done easily from an FPC program... extracting the fonts from the binary, or using a folder where then we copy it to the hidden fonts folder.

Anyone interested with all the systems, to make a definitive solution for this endless problem?

Thanks.
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: Thaddy on February 02, 2023, 04:18:36 pm
1. The definitive solution for Lazarus and/or FPC is including the font as a resource. (Also works on Mac)
2. Linux has /usr/shared/fonts subs. ~/.fonts. The latter is local.
3. Windows has \Windows\Fonts

In case of problems, use 1. Accessing resource fonts does not require unpacking.

Note that 1. should work, but today I have only access to FPC main/trunk, which can call fpcres inline (transparent), which in turn can use rc and res on one line.
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: lainz on February 03, 2023, 12:54:26 am
@Thaddy what if the HOME/.fonts can't be created. I have a Linux system with the HOME folder blocked.

Or they refer as HOME as HOME/username?
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: domasz on February 03, 2023, 12:58:34 am
3. Windows has \Windows\Fonts

That's not a good solution. User might not want to have the fonts installed for every running application. The better solution:
Code: Pascal  [Select][+][-]
  1.  procedure TForm1.FormCreate(Sender: TObject) ;
  2.  begin
  3.    AddFontResource('c:FONTSMyFont.TTF') ;
  4.    SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0) ;
  5.  end;
  6.  {Remove font again on program termination}
  7.  procedure TForm1.FormDestroy(Sender: TObject; var Action: TCloseAction) ;
  8.  begin
  9.    RemoveFontResource('C:FONTSMyFont.TTF') ;
  10.    SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0) ;
  11.  end;
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: lainz on February 03, 2023, 03:37:49 am
3. Windows has \Windows\Fonts

That's not a good solution. User might not want to have the fonts installed for every running application. The better solution:
Code: Pascal  [Select][+][-]
  1.  procedure TForm1.FormCreate(Sender: TObject) ;
  2.  begin
  3.    AddFontResource('c:FONTSMyFont.TTF') ;
  4.    SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0) ;
  5.  end;
  6.  {Remove font again on program termination}
  7.  procedure TForm1.FormDestroy(Sender: TObject; var Action: TCloseAction) ;
  8.  begin
  9.    RemoveFontResource('C:FONTSMyFont.TTF') ;
  10.    SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0) ;
  11.  end;

Thanks this code looks easy to use hope it works on Windows 11?
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: lainz on February 03, 2023, 02:30:42 pm
3. Windows has \Windows\Fonts

That's not a good solution. User might not want to have the fonts installed for every running application. The better solution:
Code: Pascal  [Select][+][-]
  1.  procedure TForm1.FormCreate(Sender: TObject) ;
  2.  begin
  3.    AddFontResource('c:FONTSMyFont.TTF') ;
  4.    SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0) ;
  5.  end;
  6.  {Remove font again on program termination}
  7.  procedure TForm1.FormDestroy(Sender: TObject; var Action: TCloseAction) ;
  8.  begin
  9.    RemoveFontResource('C:FONTSMyFont.TTF') ;
  10.    SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0) ;
  11.  end;

Tested and works on Windows 11. Just cast to
Code: Pascal  [Select][+][-]
  1. Pchar('C:\FONTS\MyFont.TTF')
and you're good to go.

Unfortunately the wide version didn't work...

Code: Pascal  [Select][+][-]
  1. RemoveFontRresourceW(PWideChar(...));

Maybe casting PWideChar directly to a String is not working...
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: domasz on February 03, 2023, 02:50:33 pm
Tested and works on Windows 11.
I don't have Win11 but on Windows7 and Windows 10 it works. It also works in Delphi. So seems like a solid solution.
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: PascalDragon on February 03, 2023, 03:40:28 pm
3. Windows has \Windows\Fonts

That's not a good solution. User might not want to have the fonts installed for every running application. The better solution:
Code: Pascal  [Select][+][-]
  1.  procedure TForm1.FormCreate(Sender: TObject) ;
  2.  begin
  3.    AddFontResource('c:FONTSMyFont.TTF') ;
  4.    SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0) ;
  5.  end;
  6.  {Remove font again on program termination}
  7.  procedure TForm1.FormDestroy(Sender: TObject; var Action: TCloseAction) ;
  8.  begin
  9.    RemoveFontResource('C:FONTSMyFont.TTF') ;
  10.    SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0) ;
  11.  end;

You should not do this as part of OnCreate and OnDestroy of the form as a form might be created multiple times (you might not do so now, but you might have a reason for it later on). Instead do this at the start of the main program and the end of the main program.

Linux
https://itsfoss.com/install-fonts-ubuntu/ (https://itsfoss.com/install-fonts-ubuntu/)

Basically on Ubuntu / Mint one can create HOME/.fonts directory and put the fonts there, maybe that can be done easily from an FPC program... extracting the fonts from the binary, or using a folder where then we copy it to the hidden fonts folder.

There is no guarantee that FontConfig is configured that way. It's very likely that it is, but the system administrator might decide that users don't get to install their own fonts.

@Thaddy what if the HOME/.fonts can't be created. I have a Linux system with the HOME folder blocked.

Or they refer as HOME as HOME/username?

Thaddy didn't write anything about HOME, he wrote ~/.fonts which is the .fonts directory in the user's folder (e.g. /home/pascaldragon/.fonts). If you can't create this as long as your application is running as the same user then something is seriously wrong, because a user always is supposed to have write access in their own home directory.
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: Bogen85 on February 03, 2023, 04:00:34 pm
@Thaddy what if the HOME/.fonts can't be created. I have a Linux system with the HOME folder blocked.

Or they refer as HOME as HOME/username?

Not completely on topic, but...
(keep in mind, the following are just cursory details, there is a bit more involved...)

By HOME I assume you mean the environment variable. Which is set by your login environment and points to your home directory.

This is usually /home/username (both case sensitive) but that is not always the case.

Tilde / (~/) is expanded by the command shell (bash as least, not sure about others) to $HOME/ which might by /home/username/, but might be some other location that does have have home or your username in it.

As @PascalDragon pointed out, you should have write access on most Linux setups to what $HOME/ expands to, if you don't, that is almost always a problem.
If this is a normal desktop Linux install (not some highly custom one for some specific appliance or device) you will always have write access to your $HOME/.
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: Bogen85 on February 03, 2023, 04:11:49 pm
@Thaddy what if the HOME/.fonts can't be created. I have a Linux system with the HOME folder blocked.

Or they refer as HOME as HOME/username?

In what context are you referencing HOME/.fonts?

Per my previous reply, $HOME/ and ~/ expansion will only be in your shell, or in some other environment that understands how to do that expansion.
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: lainz on February 03, 2023, 04:14:01 pm
I'm not Linux expert, for that I ask. AFAIK our custom Linux might have support for that .fonts directory, that was not removed AFAIK. Is based on Mint.

I have HOME folder and that's not writable, but yes HOME/username is writable.

So I need to create the folder in HOME/username/.fonts/

How I can do that with FPC?

Edit: Say I have the fonts in my program directory I access with this function:
Code: Pascal  [Select][+][-]
  1. DirectorioAplicacion + 'myfont.ttf'
I want to copy to HOME/username/.fonts/myfont.ttf
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: Bogen85 on February 03, 2023, 04:25:40 pm
I have HOME folder and that's not writable, but yes HOME/username is writable.

Just to be clear, you mean /home on your system is not writable by you, but what might be /home/lainz (assumption of username for case of this discussion) is writable.

That is a normal setup.

I'm not Linux expert, for that I ask. AFAIK our custom Linux might have support for that .fonts directory, that was not removed AFAIK. Is based on Mint.

In this context I would call Mint completely standard and non custom (Mint is not a deviation from mainstream on the vast majority of how it does things, I don't really know if ever deviates, but "mainstream" can be and is very broad/fuzzy term)
It is unlikely that something based on Mint would (or even could) deviate to this degree, but could be wrong. (anything is possible...)

Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: Bogen85 on February 03, 2023, 04:29:47 pm
So I need to create the folder in HOME/username/.fonts/

How I can do that with FPC?

Edit: Say I have the fonts in my program directory I access with this function:
Code: Pascal  [Select][+][-]
  1. DirectorioAplicacion + 'myfont.ttf'
I want to copy to HOME/username/.fonts/myfont.ttf

I'm thinking about how you can do this...
Not sure if would be considered the normal way.
Usually fonts would be in standard location, and you would just indicate in your application the details of the font you are using.

EDIT: Unless you mean when your program runs, it needs to update a standard location? (copy a font that is next to application to the standard location?)
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: KodeZwerg on February 03, 2023, 04:33:12 pm
For Windows I've created a simple class to help using embedded Fonts, maybe that help you too?
You can use it like:
Code: Pascal  [Select][+][-]
  1. var
  2.   FFont: TkzFonts;
  3. begin
  4.   FFont := TkzFonts.Create;
  5.   if FFont.LoadResourceFont(HInstance, 1) then
  6.     AnyControl.Font.Name := string(FFont.Index[0].FontName);
In that case a font with a resource identifier of "1" would be loaded and added to an internal array of fonts what you easy can access like shown.
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: lainz on February 03, 2023, 04:35:30 pm
So I need to create the folder in HOME/username/.fonts/

How I can do that with FPC?

Edit: Say I have the fonts in my program directory I access with this function:
Code: Pascal  [Select][+][-]
  1. DirectorioAplicacion + 'myfont.ttf'
I want to copy to HOME/username/.fonts/myfont.ttf

I'm thinking about how you can do this...
Not sure if would be considered the normal way.
Usually fonts would be in standard location, and you would just indicate in your application the details of the font you are using.

EDIT: Unless you mean when your program runs, it needs to update a standard location? (copy a font that is next to application to the standard location?)

Exactly, I  have say

home/username/myprogram/myfont.ttf

and I want to copy it to

home/username/.fonts/myfont.ttf at runtime, so it's available, only once, if exists don't copy it again... and create the folder if don't exists, all by code
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: Bogen85 on February 03, 2023, 05:02:04 pm
Exactly, I  have say

home/username/myprogram/myfont.ttf

and I want to copy it to

home/username/.fonts/myfont.ttf at runtime, so it's available, only once, if exists don't copy it again... and create the folder if don't exists, all by code

Something like this? (Untested on my part... this is just a possible example of how you could do it...)


Code: Pascal  [Select][+][-]
  1. uses
  2.   SysUtils, FileUtil;
  3.  
  4. function SearchPath(const exe: string): string;
  5.   begin
  6.     result := ExeSearch(exe, GetEnvironmentVariable('PATH'));
  7.   end;
  8.  
  9. function CopyFileToHomeDir(const sourceFile, destFolder: string): Boolean;
  10. var
  11.   ExeName, ExePath, sourcePath, destPath: string;
  12. begin
  13.   // Get directory current application is found in
  14.   ExeName := ParamStr(0);
  15.   //ExePath := ExtractFilePath(ExeName);
  16.   ExePath := SearchPath(ExeName);
  17.  
  18.   // Check if source file exists
  19.   sourcePath := ExePath + PathDelim + sourceFile;
  20.   if not FileExists(sourcePath) then
  21.   begin
  22.     Result := False;
  23.     Exit;
  24.   end;
  25.  
  26.   // Check if destination folder exists
  27.   destPath := GetUserDir + PathDelim + destFolder;
  28.   if not DirectoryExists(destPath) then
  29.     CreateDir(destPath);
  30.  
  31.   // Check if file exists in destination folder
  32.   destPath := destPath + PathDelim + sourceFile;
  33.   if FileExists(destPath) then
  34.   begin
  35.     Result := True;
  36.     Exit;
  37.   end;
  38.  
  39.   // Copy file to destination folder
  40.   Result := CopyFile(sourcePath, destPath);
  41. end;
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: lainz on February 03, 2023, 09:51:39 pm
Thanks it works, it copies the fonts!

Code: Pascal  [Select][+][-]
  1.   {$IFDEF LINUX}
  2.   CopyFileToHomeDir('VictorMono-Bold.otf', '.fonts');
  3.   CopyFileToHomeDir('VictorMono-Regular.otf', '.fonts');
  4.   {$ENDIF}

Thanks =)

Now is missing macOS, and all 3 major OS are covered.
Title: Re: Help Needed: Cross Platform Third Party Fonts
Post by: Bogen85 on February 03, 2023, 11:12:55 pm
Thanks it works, it copies the fonts!

Code: Pascal  [Select][+][-]
  1.   {$IFDEF LINUX}
  2.   CopyFileToHomeDir('VictorMono-Bold.otf', '.fonts');
  3.   CopyFileToHomeDir('VictorMono-Regular.otf', '.fonts');
  4.   {$ENDIF}

Thanks =)

Good! However, I just asked Open AI...
Quote
I need a function for free pascal.
It receives 2 strings. 1) a file 2) a folder

For the file, it chesks for it's existence in the folder the application is being run from. This file with the application directory path is prepended to the file as the source file.

For the folder, it checks for it's existence in the user's home directory (home directory must be prepended).
If the folder does exist, it creates it.
This folder in the user's home directory is the destination folder.

If the source file exists, the the existence of the same file is checked or in the destination folder. If it does not exist in the destionation folder it is copied there.

It did not calculate a source path correctly so I added SearchPath on my own, and the call to it.

Apart from that, the rest is verbatim from OpenAI's reply...

Hmmm... I see it auto corrected a typo of mine (if the directory exists, create it... it knew to add not exists...)
TinyPortal © 2005-2018