First Contact! OSX fix #1 of many

I don’t really know what I’m doing when it comes to OSX and Obj-C but I do know enough to learn more and not give up just because ‘it’s too hard’. I also know to learn with something simple, but useful and that would be shoe_font_list() which is called at Shoes init time and populates the Shoes::FONTS array for Ruby. The clang compiler complains about deprecations (from 10.5 or 10.6 time). Time to learn and update something that works.

tiny test script:

Shoes.app do
  para "Fonts:n"
  Shoes::FONTS.each {|font| para font+"n"}
end

I learned OSX folks don’t write as many blog posts describing problems and solutions like the Linux folk do. So, Shoes used the older ATS C level api and the newer better thing is CoreText (CT). Note, I wasted a day digging into a hole with NSFontCollection (but I learned some Obj-C so I can’t complain). I found this bit of code and I modfified it slightly for Shoes. After much testing and horsing around, it works. I don’t about the memory management issues. FIXME: I guess, maybe.

Since shoes_font_list is small, I’ll post it here for others who may google upon it.

VALUE
shoes_font_list()
{
  INIT;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
  VALUE ary = rb_ary_new();
  CFBooleanRef value= kCFBooleanTrue;
  int vtrue[1] = {1};
  CFDictionaryRef dict = CFDictionaryCreate(NULL, 
					    (const void **)kCTFontCollectionRemoveDuplicatesOption, 
					    (const void **)&vtrue, 1, NULL, NULL);
  CTFontCollectionRef fcref = CTFontCollectionCreateFromAvailableFonts(dict);
  CFArrayRef arrayref = CTFontCollectionCreateMatchingFontDescriptors(fcref);
  CFRelease(fcref);
  CFIndex count = CFArrayGetCount(arrayref);
  CFIndex i;
 for (i=0; i<count; i++) {
    CTFontDescriptorRef fdesc =(CTFontDescriptorRef)CFArrayGetValueAtIndex(arrayref, i);
    CTFontRef font = CTFontCreateWithFontDescriptor(fdesc, 0., NULL);
	CFStringRef cfname = CTFontCopyFullName(font);
	static char fname[100];
	CFStringGetCString(cfname, fname, sizeof(fname), kCFStringEncodingUTF8);
    rb_ary_push(ary, rb_str_new2(fname));
  }
#else
  ATSFontIterator fi = NULL;
  ATSFontRef fontRef = 0;
  NSMutableArray *outArray;
  VALUE ary = rb_ary_new(); 
  if (noErr == ATSFontIteratorCreate(kATSFontContextLocal, nil, nil,
         kATSOptionFlagsUnRestrictedScope, &fi))
  {
    while (noErr == ATSFontIteratorNext(fi, &fontRef))
    {
      NSString *fontName;
      ATSFontGetName(fontRef, kATSOptionFlagsDefault, &fontName);
      if (fontName != NULL)
        rb_ary_push(ary, rb_str_new2([fontName UTF8String]));
    }
  }

  ATSFontIteratorRelease(&fi);
#endif
  RELEASE;
  rb_funcall(ary, rb_intern("uniq!"), 0);
  rb_funcall(ary, rb_intern("sort!"), 0);
  return ary;
}

shoe_load_font(filename) will be next. No, I haven’t fixed a bug users would see. I have a lot to learn about Obj-c and Cocoa and AppKit. I’ll start with something bite sized before diving into the the deep end of the pool.

Leave a Reply

Your email address will not be published. Required fields are marked *