iOS CoreText通过CTFontManagerRegisterGraphicsFont获取已注册字体的列表 [英] iOS CoreText get list of registered fonts via 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
方法注册的所有字体的列表,以便在再次注册之前可以注销它们?
我尝试使用CTFontManagerCopyAvailablePostScriptNames
和CTFontManagerCopyAvailableFontFamilyNames
方法,但是两者都只打印出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, ®istererror);
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, ®istererror);
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屋!