Mac OS custom fonts

I want to use fonts without installing them in the system.

I tried modifying an app package as outlined in this forum (appfont folder and in info.plist) it works but I would like to have all in the IDE to be able to generate an installer pcakege with App Wrapper Mini (suberb).

I found this in this forum but cannot really see how to apply it. Where should this code go ? Any help will be strongly appreciated. TIA :


Sam Rowlands Jun 13 Alpha Testers, Beta Testers, Xojo Pro
hmmm… turns out that it requires more code than can be easily pasted into the forum. If you have the MBS plugin, it should have the functions so a little translation from my code to the MBS will be required.

Dim fontManager as new NSFontManager
call fontManager.registerFontsInFolder( app.resourcesFolder.child( “fonts” ), NSFontManager.scope.process )

Public Enum scope
none
process
user
session

Function registerFontsInFolder(inFolder as folderitem, inScope as scope) As boolean
Dim n,l as integer
Dim fontArray as new CFarray
n = inFolder.count
for l=1 to n
select case inFolder.item( l ).nameExtensionMBS
case “ttf”, “otf”
fontArray.addValue CFURL.createWithFSRef( inFolder.item( l ).macFSRef )
end select
next

declare function CTFontManagerRegisterFontsForURLs lib library ( urls as Ptr, inScope as Uint32, errors as Ptr ) as boolean
return CTFontManagerRegisterFontsForURLs( fontArray.handle, Uint32( inScope ), nil )
End Function
You could also translate the CFArray to NSArray and the CFUrl to NSURL.

You mean like folderitem.ATSFontActivateFileMBS in our plugins?

For Sam’s code: You need to call registerFontsInFolder with the folder of the font files and pass the right scope value.

Okay, well you first need to automatically copy your fonts into your app bundle, a “Copy Files” build script will help you there.

Then on application execution, you need to ‘register’ these fonts.

I posted this code as more of a guide to getting there, the actual code I used has quite a few dependancies, but if you’ve got the MBS plugin or probably even the MacOSLib, you should be able to adapt it.

Oh - last but not least - If you want to post your app to the Mac App Store, make sure that you have either permission to distribute these fonts or a license to do so. We obtained permission for the fonts included in Fun Greetings Deluxe, but from a list of about 50 different ‘FREE’ fonts available on the web, only 2 or 3 people actually agreed. Many font creators outrightly said no, other’s simply didn’t respond. Not one font designer want to enter into licensing us fonts for use in our application.

Thank you Sam. I will continue my patient work towards achieving a great font app. I happen to have been developing fonts since 1987 so I have no real worries about rights ;). My concern is more about displaying them in a friendly fashion to users.

When I have finally succeeded as I hope, I will try my best to share sensibly what I found, as sharing is the best way to give back what has been given to one.

Michel Bujardet
fontmenu com

Allright. I have found a way to have custom fonts without extraneous steps, without dependencies everywhere, and sorry without MBS. I post it here in the hope of saving frustration to countless other poor souls who may not like seing that something is ‘very easy’ without any hint of solution.

  • Add a Copy Files to the project build steps (insert) and copy all fonts into the Resources folder in a subfolder called appfonts (in this example, any name will do)
  • Build an app. In the package contents folder, pick the info.plist file, copy it into your development folder, and add to it the following line:
    ATSApplicationFontsPath
    appfonts
  • Add a Copy Files which copies the modified info.plist back into the Contents folder.

Voil. That’s it.

I found that it is easier to install the fonts in the system to have a correct display in the IDE, but at runtime, the app will use the fonts within itself.

If you are looking for original fonts, look for FontMenu.com. They may not be free, but they are extremely affordable. And I can grant usage rights to friendly Xojo developers. If you need fonts created to your specs, I do that too.

Michel Bujardet
Match Software

Ah, the info plist entry. I think we talked about that a couple of times over the years.

My only concern with using ATS, is that Core Text superseded it back in 10.5 and while it seems that most services have been depreciated, this one still works for the time being.

I would certainly test it on a Mavericks installation, as this is coming very soon and the plist key may be depreciated on Mavericks. Otherwise you’re high and dry for at least another year.

We’re very interested in licensing additional fonts to use in our Fun Greetings Deluxe app.

I just tested my app under Mavericks. It works just fine. I would suppose that even if Apple plans on retiring plist eventually, they do have to keep it working until today’s applications are off the track. And there are hundreds of thousands sold. Which will take time. More than one year I bet.

I would love to switch to NSFontManager if only the information was available to apply it without extra aspirin and cryptic instructions.

I hate the esoteric ambiance related to some aspects of programming. It is counter productive, breeds pseudo elitist attitudes, and altogether promotes ill-born snobbism. Exactly the reason why I posted in three points only a method that works, and does not require extraneous humiliation, or bully prone initiation.

Feel free to dash by FontMenu.com and see which fonts may be right for your application.

MB

I would not make any such bet about what Apple does / does not do.
However, a quick read of the docs make no reference that this key is being deprecated.

Thank you Norman for the precision about the key not being deprecated.

Now indeed, it would be nice to use the more recent NSFontManager.

I noticed that NSFontManager is already present in the file called designable.nib, within the Resources folder.

NSObject IBFrameworkSource AppKit.framework/Headers/NSFontManager.h

I do not see at this point what to do with it, but maybe someone will eventually share a simple and working way of replacing ATSApplicationFontsPath with Xojo…

[quote=37298:@Norman Palardy]I would not make any such bet about what Apple does / does not do.
However, a quick read of the docs make no reference that this key is being deprecated.[/quote]
I’m going to seem like the evil one here - but I wouldn’t trust Apple’s doc entirely. If the key works, it’s working.

Apple’s docs can be out of date and incomplete. Only two weeks ago I noticed that some functions I was using on 10.8 were no longer listed on the current ADC docs and the replacement function (which was introduced in 10.8, API differences) wasn’t in the 10.8 doc set.

The thing is, I’m not trying to be pig headed, I’ve used functions in the past that were marked as depreciated, because they still worked and were easier at the time. Photo editing app and using a certain function on 10.8 would result in a solid black image!

ATS was depreciated with 10.5, that was 6 years ago and there are still some APIs left in 10.8, but they might disappear with 10.9.1 or 10.10(?).

If it works for you at the moment, great. Just be conscious of future versions of the Mac OS (hell which you have to be anyway!)

So, where can I find the documentation about NSFontManager ?

TIA

The function I was using is CTFontManagerRegisterFontsForURLs
You can find documentation on Apple’s Developer Site.
https://developer.apple.com/library/mac/documentation/Carbon/Reference/CoreText_FontManager_Ref/Reference/reference.html

It takes a CFArray of CFURLs, but as CFArray and NSArray are interchangeable it can be modified to accept these. Hence the dependancies.

[quote=37565:@Sam Rowlands]The function I was using is CTFontManagerRegisterFontsForURLs
You can find documentation on Apple’s Developer Site.
https://developer.apple.com/library/mac/documentation/Carbon/Reference/CoreText_FontManager_Ref/Reference/reference.html

It takes a CFArray of CFURLs, but as CFArray and NSArray are interchangeable it can be modified to accept these. Hence the dependancies.[/quote]

Thank you Sam, I’ll go there

Just found this Mac Ruby code, extremely simple, to add a custom font. Wonder if this is translatable to Xojo …


font_location = NSBundle.mainBundle.pathForResource(‘MyCustomFont’, ofType: ‘ttf’)
font_url = NSURL.fileURLWithPath(font_location)

in MacRuby, always make sure that cocoa constants start by an uppercase

CTFontManagerRegisterFontsForURL(font_url, KCTFontManagerScopeProcess, nil)

CTFontManagerRegisterFontsForURL is almost like the function CTFontManagerRegisterFontsForURLs, except that it does each font at once, but will be easier to translate as you only need to convert a Folderitem to a NSURL… If you dig out the code on this forum for handling NSDocumentController, there’ll be a conversation method there :slight_smile:

Could not find any sample code there.

Tried to concoct my own from the Ruby example


dim f as folderitem = app.executablefile.parent.parent.child(“Resources”).child(“Calebasse.ttf”)
dim UrlToFont as CString = f.URLPath

dim scopitone as integer = 1

Soft Declare Sub CTFontManagerRegisterFontsForURL Lib “Cocoa” selector “CTFontManagerRegisterFontsForURL” (zurl as CString , zscop as integer, znile as integer)
CTFontManagerRegisterFontsForURL(UrlToFont,scopitone,nil)

I get an error ‘Parameters are not compatible with that function’…

I am not advanced enough in this kind of exercise yet to understand what is going on. Without help, I will give up.

Apple loves HO documentation.

HO stands for High Obfuscation :wink:

Apple’s toolbox is amazingly impressive, I’ve not seen another OS with such a large and powerful toolbox. Coming from Xojo, tapping into it can be daunting and it’s taken myself many years to fully understand how things worked in Carbon, then with the switch to Cocoa I’m still learning a great deal of things.

I still don’t fully understand how we can efficiently do delegated controls in the Xojo system. Although I’ve gotten them to work…

I would certainly recommend learning how to tap directly into Apple’s toolbox, even if it’s just to add little functions like this or gain more control over how a push button looks/works. It not only allows you more control, but allows you to do things with Xojo before Xojo provide such a function. Such as my Retina Kit is using the same Retina functions as a Obj-C application, and that makes life so much easier.

If it all goes well, I’m hoping to do some stuff in the xDev magazine where I’ll be showing people how to understand Apple’s docs and using declares to tap into Apple’s toolbox directly. I know that Charles wrote a book many years ago and has also helped me many times.

I have a lot to do today (not only with work), but I’ll see if I can give you a quick tutorial on this function.

So I looked up CTFontManagerRegisterFontsForURL at the Apple documentation and it showed me the following function.

bool CTFontManagerRegisterFontsForURL( CFURLRef fontURL, CTFontManagerScope scope, CFErrorRef * error );

This is a ‘flat’ API call, not an objective API call. How can I tell? Because the API name is self contained. An objective API call might simply state ‘registerFontsForURL’, where as this one includes ‘CTFontManager’ and APIs with CT in front are Core Text APIs. So this will simplify matters for this short tutorial.

The first part is actually the return datatype, if it said ‘void’ then it doesn’t return anything. In Xojo terms, as this API returns something, it a function (not a sub). It returns a bool, which is just like a Xojo Boolean.

In the brackets are the arguments. However in C, you specify the datatype and then the name, in Xojo you specify the name as datatype. i.e.
CFURLRef fontURL = fontURL as CFURLRef.

Now comes the harder part, there is no CFURLRef object in Xojo, so we need to make one. Thankfully as most Apple objects as just pointers or references, we can use the Ptr object. I know this because of the Obj-C books and work that I have done/read.

Go back to the Apple documents and do a search for CFURL, ignore anything for iPhone, there shouldn’t be any difference, but there are in some cases, so we focus on OS X.
https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFURLRef/Reference/reference.html

Looking at all the CFURLCreate functions, I pick this one, mainly because it uses a FSRef, which while it may be deprecated in the near future, it’s based on an old structure, that in my mind is less likely to have any intermediate steps that may cause an issue, such as string encoding.

CFURLRef CFURLCreateFromFSRef ( CFAllocatorRef allocator, const struct FSRef *fsRef );
Using what we discussed above this function can then be translated to:

declare function CFURLCreateFromFSRef lib "Cocoa" ( allocator as Ptr, fsRef as Ptr ) as Ptr

According to the Apple docs, we can pass NULL as the allocator (which is fine as I’m not interested in managing the memory myself).

What’s important here is the use of Create in the API name, this means that when we’ve finished with the object, we must notify the OS by calling a release on it, otherwise we’ll be leaking memory. We’ll use CFRelease when we’re done.

Next to figure out the CTFontManagerScope: Following Apple’s docs, leads us to see that it’s an enum of type uint32_t. Nice and easy as we can simply pass a Xojo Uint32 to the function! Pass 1 for app only, 2 = user for ever, 3 = user until they logout.

Lastly is the error message, for this example we’ll skip it, as it’s a CFType (the datatype starts with CF), we can just pass a Ptr and then later on we can investigate it, if we wish.

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.

1 Like

Holy blazes.