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.