IOS CoreText gets the list of registered fonts via CTFontManagerRegisterGraphicsFont

I am dynamically registering fonts with the following:

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{

    if ([url isFileURL])
    {
        // Handle file being passed in
        NSLog(@"handleOpenURL: %@",url.absoluteString);


        NSData *inData = [NSData dataWithContentsOfURL:url];
        CFErrorRef error;
        CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)inData);
        CGFontRef fontRef = CGFontCreateWithDataProvider(provider);

        UIFont *font;
        if (!CTFontManagerRegisterGraphicsFont(fontRef, &error)) {
            CFStringRef errorDescription = CFErrorCopyDescription(error);
            NSLog(@"Failed to load font: %@", error);
            CFRelease(errorDescription);
        } else {
            CFStringRef fontNameRef = CGFontCopyPostScriptName(fontRef);
            NSLog(@"fontNameRef: %@",fontNameRef);
            font = [UIFont fontWithName:(__bridge NSString *)fontNameRef size:80];
            [self.arrayOfFonts addObject:(__bridge NSString *)fontNameRef];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"refreshFont" object:nil];
            CFRelease(fontNameRef);
        }
        CFRelease(fontRef);
        CFRelease(provider);
        return YES;
    }
    else
    {
        return NO;
    }
}

      

It works great the first time. It looks like if I close the application and try to register the same font again, it gives me the (expected) error"Failed to load font: Error Domain=com.apple.CoreText.CTFontManagerErrorDomain Code=105 "Could not register the CGFont '<CGFont (0x1c00f5980): NeuropolXRg-Regular>'" UserInfo={NSDescription=Could not register the CGFont '<CGFont (0x1c00f5980): NeuropolXRg-Regular>', CTFailedCGFont=<CGFont (0x1c00f5980): NeuropolXRg-Regular>}"

It looks like the font is already registered. The documentation for it CTFontManagerRegisterGraphicsFont

states that she:

"Registers the specified graphic font with the font manager. Registered fonts can be detected using font matching. Attempts to register a font that is already registered or contains the same PostScript font name of an already registered font will fail."

How to do exactly "through font descriptor matching"

How do I get a list of all the fonts registered with a method CTFontManagerRegisterGraphicsFont

so that I can cancel them before registering?

EDIT:

I tried using the methods CTFontManagerCopyAvailablePostScriptNames

and CTFontManagerCopyAvailableFontFamilyNames

, but both only print out the names of the fonts already available on iOS. Not the ones that I registered throughCTFontManagerRegisterGraphicsFont

NOTE. ... I am NOT asking about fonts already available in iOS, which can be listed by repeating through [UIFont usernames].

+3


source to share


2 answers


I registered a ticket with Apple DTS (Developer Technical Support) and they said:

"You need to keep track of the fonts you have registered using CTFontManagerRegisterGraphicsFont yourself. The kCTFontManagerErrorAlreadyRegistered error code returned by CTFontManagerRegisterGraphicsFont will tell you that you have already registered the font. Using font mapper to detect if your fonts are installed is probably not a good way to go the system can choose to perform font replacements for missing fonts. Installing a font using CTFontManagerRegisterGraphicsFont just makes it available to your applications. It is not a query service to detect installed fonts. If this is not enough for your preference then I think it would be a good idea to consider to apply for a feature by requesting the functionality you would like to receive. "

So, basically, we need to keep track of the fonts we are registering.

Solution / Work -around I ended up using

Currently, my application allows users to add fonts using the Action Sheet's

Copy to MYAPP button in the font file. This same solution also works for font files that I download from my server.

For my application to be listed in the File Action Sheet .ttf

and .otf

, in my info.plist application, I added a new document type:



    <key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeIconFiles</key>
            <array/>
            <key>CFBundleTypeName</key>
            <string>Font</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>public.opentype-font</string>
                <string>public.truetype-ttf-font</string>
            </array>
        </dict>
    </array>

      

This allows my application to appear action sheet

in any font file. Thus, the user can put the font file in Dropbox, Google Drive or any other file sharing application. They can then import the font from there into my application. After importing, I need to move the font file from the temp folder tmp

inbox

to my application Documents

fonts

. fonts

All custom fonts are stored in the directory . After that, I register this custom font and add it to the array self.arrayOfFonts

. This is an array containing a list of all my fonts.

It also appears CTFontManagerRegisterGraphicsFont

to be session-only. Therefore, when the app is closed from the App Switcher and reloaded, this font is no longer registered. So after every run I go through the folder documents/fonts

and re-register all the fonts and add their names to the array self.arrayOfFonts

.

The rest of my application code for this:

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{

    if ([url isFileURL])
    {
        // Handle file being passed in
        NSLog(@"handleOpenURL: %@, extension: %@",url.absoluteString,url.pathExtension);
        [self moveFontFrom:url];
        return YES;
    }
    else
    {
        return NO;
    }
}

-(void)moveFontFrom:(NSURL*)fromurl{
    NSString *stringPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0] stringByAppendingPathComponent:@"fonts"];
    // New Folder is your folder name
    NSError *error1 = nil;
    if (![[NSFileManager defaultManager] fileExistsAtPath:stringPath]){
        [[NSFileManager defaultManager] createDirectoryAtPath:stringPath withIntermediateDirectories:NO attributes:nil error:&error1];
    }
    NSLog(@"error1: %@", error1.debugDescription);
    NSURL *tourl = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@",stringPath,[[fromurl absoluteString] lastPathComponent]] isDirectory:NO];

    NSLog(@"Trying to move from:\n\n%@\n\nto:\n\n%@\n\n", fromurl.absoluteString,tourl.absoluteString);

    NSError* error2;
    if ([[NSFileManager defaultManager] fileExistsAtPath:tourl.path]){
        [[NSFileManager defaultManager] removeItemAtPath:tourl.path error:&error2];
        NSLog(@"Deleting old existing file at %@ error2: %@", tourl.path,error2.debugDescription);
    }


    NSError* error3;
    [[NSFileManager defaultManager] moveItemAtURL:fromurl toURL:tourl error:&error3];
    NSLog(@"error3: %@", error3.debugDescription);

    if (!error3) {
        NSString *fontName = [self registerFont:tourl checkIfNotify:YES];
        if (fontName) {
            if (![self.arrayOfFonts containsObject:fontName]) {
                [self.arrayOfFonts addObject:fontName];
                [self.arrayOfFonts sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
                [[NSNotificationCenter defaultCenter] postNotificationName:@"refreshFont" object:nil userInfo:@{@"font":fontName}];
            }
        }
    }
}

-(void)startupLoadFontsInDocuments{

    self.arrayOfFonts = [NSMutableArray new];
    NSString *location = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0] stringByAppendingPathComponent:@"fonts"];
    NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:location error:NULL];
    for (NSInteger count = 0; count < [directoryContent count]; count++)
    {
        NSLog(@"File %ld: %@/%@", (count + 1), location,[directoryContent objectAtIndex:count]);
        NSString *fontName = [self registerFont:[NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@",location,[directoryContent objectAtIndex:count]] isDirectory:NO] checkIfNotify:NO];
        if (fontName) {
            if (![self.arrayOfFonts containsObject:fontName]) {
                [self.arrayOfFonts addObject:fontName];
            }
        }
    }
    [self.arrayOfFonts sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
}

-(NSString*)registerFont:(NSURL *)url checkIfNotify:(BOOL)checkIfNotify{
    NSData *inData = [NSData dataWithContentsOfURL:url];
    CFErrorRef registererror;
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)inData);
    CGFontRef fontRef = CGFontCreateWithDataProvider(provider);
    NSString *fontName = (__bridge NSString *)CGFontCopyPostScriptName(fontRef);
    BOOL registerFontStatus = CTFontManagerRegisterGraphicsFont(fontRef, &registererror);
    if (!registerFontStatus) {
        CFStringRef errorDescription = CFErrorCopyDescription(registererror);
        NSError *registererr = (__bridge NSError*)registererror;
        if ([registererr code]==kCTFontManagerErrorAlreadyRegistered) {
            NSLog(@"Font is already registered!");
        }
        NSLog(@"Failed to load font: %@", registererror);
        CFRelease(errorDescription);

        /*CFErrorRef unregistererror;
    BOOL unregisterFont = CTFontManagerUnregisterGraphicsFont(fontRef, &unregistererror);
    NSLog(@"Font unregister status: %d",unregisterFont);
    CFStringRef unregistererrorDescription = CFErrorCopyDescription(unregistererror);
    NSError *unregistererr = (__bridge NSError*)unregistererror;
    NSInteger code = [unregistererr code];

    NSLog(@"Failed to unregister font: %@", unregistererr);
    CFRelease(unregistererrorDescription);*/

        if (checkIfNotify) {
            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Already added" message:@"That font is already added to the app. Please select it from the fonts list." preferredStyle:UIAlertControllerStyleAlert];
            [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {

            }]];
            [[self.window rootViewController] presentViewController:alert animated:YES completion:nil];
        }
    } else {
        CFStringRef fontNameRef = CGFontCopyPostScriptName(fontRef);
        fontName = (__bridge NSString*)fontNameRef;
        CFRelease(fontNameRef);

        NSLog(@"fontName: %@",fontName);
    }
    CFRelease(fontRef);
    CFRelease(provider);
    return fontName;
}

      

NOTE. As you noticed, I commented CTFontManagerUnregisterGraphicsFont

. CTFontManagerUnregisterGraphicsFont

used to unregister fonts doesn't seem to work for me as it gives an error saying that the Font is in use and therefore cannot be unregistered. So when I need to remove a font, I just remove it from the array self.arrayOfFonts

and mine documents/fonts

.

+3


source


What you should be using is something like this:

- (void)getInstalledFonts {
    NSDictionary *descriptorOptions = @{(id)kCTFontDownloadableAttribute : @YES};
    CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes((CFDictionaryRef)descriptorOptions);
    CFArrayRef fontDescriptors = CTFontDescriptorCreateMatchingFontDescriptors(descriptor, NULL);
    [self showExistingFonts:(NSArray *)CFBridgingRelease(fontDescriptors)];
    CFRelease(descriptor);
}

- (void)showExistingFonts:(NSArray *)fontList {
    NSMutableDictionary *fontFamilies = [NSMutableDictionary new];
    for(UIFontDescriptor *descriptor in fontList) {
        NSString *fontFamilyName = [descriptor objectForKey:UIFontDescriptorFamilyAttribute];
        NSMutableArray *fontDescriptors = [fontFamilies objectForKey:fontFamilyName];
        if(!fontDescriptors) {
            fontDescriptors = [NSMutableArray new];
            [fontFamilies setObject:fontDescriptors forKey:fontFamilyName];
        }

        [fontDescriptors addObject:descriptor];
    }

}

      



Taken from: https://www.shinobicontrols.com/blog/ios7-day-by-day-day-22-downloadable-fonts

+1


source







All Articles