iOS CoreText通过CTFontManagerRegisterGraphicsFont获取已注册字体的列表 [英] iOS CoreText get list of registered fonts via CTFontManagerRegisterGraphicsFont

查看:332
本文介绍了iOS CoreText通过CTFontManagerRegisterGraphicsFont获取已注册字体的列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在通过以下方式动态注册字体:

- (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;
    }
}

第一次正常.看来,如果我关闭应用程序并尝试再次注册相同的字体,那么它会给我(预期的)错误"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>}"

这似乎是因为字体已被注册. CTFontManagerRegisterGraphicsFont的文档指出:

在字体管理器中注册指定的图形字体.可通过字体描述符匹配发现已注册的字体.尝试注册已经注册或包含与已注册字体相同的PostScript名称的字体将失败." >

如何精确地通过字体描述符匹配"?

如何获取通过CTFontManagerRegisterGraphicsFont方法注册的所有字体的列表,以便在再次注册之前可以注销它们?

我尝试使用CTFontManagerCopyAvailablePostScriptNamesCTFontManagerCopyAvailableFontFamilyNames方法,但是两者都只打印出iOS上已经可用的字体名称.不是我通过CTFontManagerRegisterGraphicsFont

注册的

注意:我不是在问iOS上已经可用的字体,可以通过遍历[UIFont familyNames]列出这些字体.

解决方案

我用Apple DTS(开发人员技术支持)记录了一张票,他们说:

"您需要自己跟踪使用CTFontManagerRegisterGraphicsFont注册的字体.CTFontManagerRegisterGraphicsFont返回的错误代码kCTFontManagerErrorAlreadyRegistered会告诉您是否已经注册了字体.使用字体描述符匹配来发现是否已安装字体这不是一个好方法,因为系统可能会选择对丢失的字体执行字体替换.使用CTFontManagerRegisterGraphicsFont安装字体只会使其可供您的应用使用.它不是用于发现已安装字体的查询服务.根据您的喜好,那么我认为考虑提出功能请求以询问您希望接收的功能对您来说是个好主意."

因此,基本上,我们需要跟踪自己注册的字体.

我最终使用的解决方案/解决方法:

当前,我的应用程序允许用户使用字体文件上的Action Sheet's复制到MYAPP"按钮添加字体.同样的解决方案也适用于我从服务器下载的字体文件.

为了在.ttf.otf文件的操作表中列出我的应用程序,在我的应用程序的info.plist中,我添加了一个新的文档类型:

    <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>

这允许我的应用在任何字体文件的action sheet中显示.因此,用户可以将字体文件放在保管箱,Google驱动器或任何其他文件共享应用程序上.然后他们可以从那里将字体导入到我的应用程序中.导入后,我需要将字体文件从临时tmp inbox文件夹移动到应用程序的Documents fonts目录中. fonts目录保留所有自定义字体.之后,我注册该自定义字体并将名称添加到self.arrayOfFonts数组中.这是包含我所有字体列表的数组.

此外,看来CTFontManagerRegisterGraphicsFont仅用于会话.因此,当从应用程序切换器关闭应用程序并重新启动时,该字体不再注册.因此,每次启动后,我都要遍历documents/fonts文件夹并重新注册所有字体,并将其名称添加到self.arrayOfFonts数组中.

我的其余应用代码都可以正常运行:

- (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;
}

注意::您会注意到,我已经注释了CTFontManagerUnregisterGraphicsFont.用于注销字体的CTFontManagerUnregisterGraphicsFont对我来说似乎不起作用,因为它给出了一个错误,指出正在使用字体,因此无法注销.因此,当我需要删除字体时,只需将其从self.arrayOfFonts数组和documents/fonts文件夹中删除.

I am registering fonts dynamically via 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 fine first time. It appears that if I close the app and try to register the same font again, then 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>}"

This seems to be because the font is already registered. The documentation for CTFontManagerRegisterGraphicsFont states that it:

"Registers the specified graphics font with the font manager. Registered fonts are discoverable through font descriptor matching. Attempts to register a font that is either already registered or contains the same PostScript name of an already registered font will fail."

How exactly to do "through font descriptor matching"??

How can I get a list of all fonts which have been registered via the CTFontManagerRegisterGraphicsFont method so I can unregister them before registering again?

EDIT:

I have tried using the CTFontManagerCopyAvailablePostScriptNames and CTFontManagerCopyAvailableFontFamilyNames methods but both only print out the names of the fonts already available on iOS. Not the ones I registered via CTFontManagerRegisterGraphicsFont

NOTE: I am NOT asking about the fonts already available on iOS which can be listed by iterating over [UIFont familyNames].

解决方案

I logged 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 error code kCTFontManagerErrorAlreadyRegistered returned by CTFontManagerRegisterGraphicsFont will tell you if you have already registered a font. Using font descriptor matching to discover if your fonts are installed probably isn’t a good way to go as the system may choose to perform font substitution for missing fonts. Installing a font using CTFontManagerRegisterGraphicsFont simply makes it available for your app’s use. It is not a query service for discovering installed fonts. If that is not sufficient for your preferences, then I think it would be a good idea for you to consider filing a feature request asking for the functionality you would like to receive."

So basically, we need to keep track of the fonts we register ourselves.

Solution/Work-around I ended up using:

Currently my app allows users to add fonts using the Action Sheet's "Copy to MYAPP" button on a font file. This same solution also works for font files which I download from my server.

In order for my app to be listed in the Action Sheet for .ttf and .otf files, in my app's info.plist, 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 app to show in the action sheet on any font file. So user can put the font file on dropbox, google drive or any other file sharing app. Then they can import the font from there into my app. After importing, I need to move the font file from the temporary tmp inbox folder to my app's Documents fonts directory. The fonts directory keeps all the custom fonts. After that, I register this custom font and add the name to the self.arrayOfFonts array. This is the array which contains the list of all my fonts.

Also it appears that CTFontManagerRegisterGraphicsFont is only for the session. So when the app is closed from the App Switcher and relaunched, that font is no longer registered. So after every startup, I go through my documents/fonts folder and re-register all the fonts and add their names to the self.arrayOfFonts array.

Rest of my app code to get this working:

- (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 will noticed, I have commented out the CTFontManagerUnregisterGraphicsFont. The CTFontManagerUnregisterGraphicsFont used to unregister fonts doesn't seem to work for me as it gives an error saying the Font is in use and hence can't be un-registered. So when I need to remove a font, I simply remove it from the self.arrayOfFonts array and my documents/fonts folder.

这篇关于iOS CoreText通过CTFontManagerRegisterGraphicsFont获取已注册字体的列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆