Mac OS custom fonts

In a good way or a bad way?

Good way, very good :smiley:

Glad it helps.

Sam, I am so impressed. I shall work from your brilliant example, now.

Thank you so much :slight_smile:

I was just starting to investigate the function from Apple reference, and you took the time on your busy day to write all the code. This is exceptional. I appreciate so much your insight and tutorial. I had seen what the call needed, but was unable to grasp the CFURL concept. Now I understand better.

I just tested your function, and it works superbly.

Now while it is quite more involved than ATSApplicationFontsPath, thanks to you, we have an updated method for using fonts without installing them in the system. And it will hopefully not be deprecated soon.

Custom fonts in iOS are still managed through plist (Fonts used by application). I did not suspect Mac OS X had actually two ways to get to the same result.

I see, and it makes sense. OS X still has some backwards compatibility (Carbon) hanging around, while iOS never had to have such a layer to begin with.

Pleased it helps, and hopefully it will help you branch out into using more Apple APIs.

Thanks to you, it will be a lot easier to explore :slight_smile:

The last app I am creating to go into the Mac app store will use the new method :slight_smile:

[quote=37958:@Sam Rowlands]
(…)
So here is the translated declare:

declare function CTFontManagerRegisterFontsForURL lib "Cocoa" ( fontURL as Ptr, scope as Uint32, error as Ptr ) as boolean

Here is a complete function for you, hopefully my explanations above will help you to understand the code. I put this in the action event of a push button, and while I confess that I didn’t actually verify if the font was available to the application, I didn’t get any errors.

[code] Dim nso as new OpenDialog
Dim filter as new fileType
filter.name = “True Type Fonts”
filter.extensions = “ttf”
nso.filter = filter
nso.initialDirectory = specialFolder.userLibrary.child( “Fonts” )
Dim f as folderitem = nso.showModalWithin( self )

if f <> nil then
// - We have a True Type Font file, first step to make a CFURL.
// CFURLRef CFURLCreateFromFSRef (
// CFAllocatorRef allocator,
// const struct FSRef *fsRef
// );

declare function CFURLCreateFromFSRef lib "Cocoa" ( allocator as Ptr, fsRef as Ptr ) as Ptr
Dim CFURLRef as Ptr = CFUrlCreateFromFSRef( nil, f.MacFSRef )

if CFUrlRef = nil then
  Msgbox "Failed to get the CFURLRef of folder """+ f.nativePath + """"
else
  
  // - Now that we have a CFURLRef, we can register it.
  
  // bool CTFontManagerRegisterFontsForURL(
  // CFURLRef fontURL,
  // CTFontManagerScope scope,
  // CFErrorRef * error
  // );
  
  // - Here is our declare.
  declare function CTFontManagerRegisterFontsForURL lib "Cocoa" ( fontURL as Ptr, scope as Uint32, error as Ptr ) as boolean
  Dim CFErrorRef as Ptr
  
  if CTFontManagerRegisterFontsForURL( CFURLRef, 1, CFErrorRef ) then
    // Font is registered
    MsgBox "Success Boyo"
  else
    MsgBox "Font registration failed, so we should investigate the error to figure out why!"
  end if
  
  
  // - As we had a valid CFURLRef from a 'Create' or 'Copy' API, we must release it.
  declare sub CFRelease lib "Cocoa" ( ref as ptr )
  CFRelease CFUrlRef
end if

end if[/code]

One last thing to pay attention to when working with declares, availability! In Apple’s documentation, it lists when the API was introduced, pay close attention to this. For the CTFontManagerRegister function, it was introduced in 10.6, so if you’re targeting 10.5, it’s NOT going to work and may crash. Also check to see if a API function is labelled as Deprecated, and what function you should use instead. Sometimes, you may have to write functions, one for an older system and one for the newer system.

I hope that this helps.[/quote]

This code served me well for many years, now I need to replace or correct it for compatibility with a forthcoming version of Xojo.

This line of code throws this error:

Declare Function CFURLCreateFromFSRef Lib "Cocoa" ( allocator As Ptr, fsRef As Ptr ) As Ptr Dim CFURLRef As Ptr = CFUrlCreateFromFSRef( Nil, f.MacFSRef )

And the error:

[quote]Type “FolderItem” has no member named “MacFSRef”
Dim CFURLRef As Ptr = CFUrlCreateFromFSRef( Nil, f.MacFSRef )[/quote]

What would be the replacement of this code, provided by Sam some years back?

@Oliver Osswald — FSRef is deprecated and I think it is not compatible with APFS volumes. In order to create your CFURLRef, you should use the following (which takes the POSIX path instead of an FSRef):

[code]declare function CFURLCreateWithFileSystemPath lib “Cocoa” (allocator as Ptr, filePath as CFStringRef, pathStyle as integer, isDirectory as boolean) as Ptr

dim CFURLRef as Ptr = CFURLCreateWithFileSystemPath( nil, f.NativePath, 0, true )
[/code]

1 Like

I’ve been using CTFontManagerRegisterFontsForURL and CTFontManagerUnregisterFontsForURL for quite a while to install some fonts that need to be used in another application (MS Word). A few weeks ago I found out that CFURLCreateFromFSRef isn’t working any more and I switched to CFURLCreateWithFileSystemPath.

The behaviour seems to have changed. When I used scope 2 before, the fonts became available for all apps that the current user runs. Now they are available for this one app only. Depending on the scope (1, 2 or 3) the fonts are persistent for the current application only, but additionally for the current process only (1), just the current user (2) or for the current session and user (3).

I’d be happy if someone had an idea how to make my fonts available for other apps, too. This is my testing code:

Dim nso as new OpenDialog
Dim filter as new fileType
filter.name = "True Type Fonts"
filter.extensions = "ttf"
nso.filter = filter
Dim f as folderitem = nso.showModalWithin( self )

if f <> nil then
  // - We have a True Type Font file, first step to make a CFURL.
  declare function CFURLCreateWithFileSystemPath lib "Cocoa" (allocator as Ptr, filePath as CFStringRef, pathStyle as integer, isDirectory as boolean) as Ptr
  dim CFURLRef as Ptr = CFURLCreateWithFileSystemPath( nil, f.NativePath, 0, false )
  
  if CFUrlRef = nil then
    Msgbox "Failed to get the CFURLRef of folder """+ f.nativePath + """"
  else
    // - Now that we have a CFURLRef, we can register it.
  
    // - Here are our declares.
    Declare Function CTFontManagerRegisterFontsForURL Lib "Cocoa" ( fontURL As Ptr, scope As UInt32, error As Ptr ) As Boolean
    Declare Function CTFontManagerUnregisterFontsForURL Lib "Cocoa" ( fontURL As Ptr, scope As UInt32, error As Ptr ) As Boolean
    Dim CFErrorRef as Ptr
    
    Dim iYesNo as integer
    If CTFontManagerUnregisterFontsForURL( CFURLRef, 2, CFErrorRef ) Then
      // Font is unregistered
      iYesNo = MsgBox("Font unregistration succeeded. Continue?", 64+4)
    else
      iYesNo = MsgBox("Font unregistration failed, eventually it was not registered! Continue?", 64+4)
    end if
    
    if iYesNo = 6 then
      if CTFontManagerRegisterFontsForURL( CFURLRef, 2, CFErrorRef ) then
        // Font is registered
        MsgBox "Font registration succeeded."
      else
        MsgBox "Font registration failed, so we should investigate the error to figure out why!"
      end if
    end if
    
    // - As we had a valid CFURLRef from a 'Create' or 'Copy' API, we must release it.
    declare sub CFRelease lib "Cocoa" ( ref as ptr )
    CFRelease CFUrlRef
  end if
end if

I gave up on declares a while ago, in favor of MBS plugins. I don’t have time to deal with changes in systems.

It is Christian’s business to ensure things work as expected, so I can concentrate on making my apps appealing to the public. He never let me down.

Hello,
I’ve been using Sam’s code to temporarily install fonts.

At present, using 19r2, the code below stumbles at the second line: (f.MacFSRef)

declare function CFURLCreateFromFSRef lib “Cocoa” ( allocator as Ptr, fsRef as Ptr ) as Ptr
Dim CFURLRef as Ptr = CFUrlCreateFromFSRef( nil, f.MacFSRef )

A few months ago Stphane suggested:
@Stphane Mons FSRef is deprecated and I think it is not compatible with APFS volumes. In order to create your CFURLRef, you should use the following (which takes the POSIX path instead of an FSRef):

Accordingly, for my situation, I tried:
declare function CFURLCreateFromFSRef lib “Cocoa” ( allocator as Ptr, fileType as CFStringRef ) as Ptr
Dim CFURLRef as Ptr = CFUrlCreateFromFSRef( nil, f.nativePath )

the app compiles, but the font does not get loaded/installed.

Some ideas how to get it work? Thanks.

@Carlo:
As I mentioned in my last post the meaning of the scope of the calls to register fonts has changed. It now works for the current application only . I too wish I knew how to solve this. Meanwhile I helped myself by copying the fonts to the font directory. If interested I can give you a sample. But you’d have to wait till Thursday because I’m out of office for a while.

@Thomas Osthege It now works for the current application only .

As you say, just now I’ve been able to have it work.
I wrongly though that using f.nativePath instead of f.MacFSRef in the old declare would have been enough.
Now, after replacing the original declare with the one provided by @Stphane Mons, my app shows texts in the temporarily installed font.

I tried also loading the same font from outside, as in your post, and it works:

dim f as folderitem = specialfolder.desktop.child(“myFont.ttc”)
call loadIndicFonts(f)//see below

But I guess this does not solve your problem, since --if I understand you all right-- you intend to have that font to be available also for other apps, while for me it is enough to use the font in my own app.
Therefore, Thomas, I’m sorry I don’t have the answer. Hopefully somebody with an experience better than mine will pop up with a solution for your issue.
Meanwhile, thank you for answering.

Public Function loadIndicFonts(f as FolderItem) as Boolean
//f is the full path of the font-location, ex:SpecialFolder.getresource(“myFont.ttc”)

[code] #if TargetMacOS
if f <> nil then

  declare function CFURLCreateWithFileSystemPath lib "Cocoa" (allocator as Ptr, filePath as CFStringRef, pathStyle as integer, isDirectory as boolean) as Ptr
  dim CFURLRef as Ptr = CFURLCreateWithFileSystemPath( nil, f.NativePath, 0, false )
  
  dim fontRegistered as Boolean
  if CFUrlRef <> nil then
    
    declare function CTFontManagerRegisterFontsForURL lib "Cocoa" ( fontURL as Ptr, scope as Uint32, error as Ptr ) as boolean
    Dim CFErrorRef as Ptr
    
    fontRegistered = CTFontManagerRegisterFontsForURL( CFURLRef, 1, CFErrorRef )
    
    declare sub CFRelease lib "Cocoa" ( ref as ptr )
    CFRelease CFUrlRef
  end if
  Return fontRegistered
end if

#else
#Pragma Unused f
#endif
End Function[/code]

That is where using plugins make it a lot easier…