2 * Copyright (C) 2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
4 * This file is part of libass.
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <CoreFoundation/CoreFoundation.h>
22 #include <CoreText/CoreText.h>
24 #include "ass_coretext.h"
26 #define CT_FONTS_EAGER_LOAD 0
27 #define CT_FONTS_LAZY_LOAD !CT_FONTS_EAGER_LOAD
29 static char *cfstr2buf(CFStringRef string)
31 const int encoding = kCFStringEncodingUTF8;
32 const char *buf_ptr = CFStringGetCStringPtr(string, encoding);
34 return strdup(buf_ptr);
36 size_t len = CFStringGetLength(string);
37 CFIndex buf_len = CFStringGetMaximumSizeForEncoding(len, encoding);
38 char *buf = malloc(buf_len);
39 CFStringGetCString(string, buf, buf_len, encoding);
44 static void destroy_font(void *priv)
46 CFCharacterSetRef set = priv;
50 static int check_glyph(void *priv, uint32_t code)
52 CFCharacterSetRef set = priv;
60 return CFCharacterSetIsLongCharacterMember(set, code);
63 static char *get_font_file(CTFontDescriptorRef fontd)
65 CFURLRef url = CTFontDescriptorCopyAttribute(fontd, kCTFontURLAttribute);
66 CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
67 char *buffer = cfstr2buf(path);
73 static void get_name(CTFontDescriptorRef fontd, CFStringRef attr,
74 char **array, int *idx)
77 CFStringRef name = CTFontDescriptorCopyAttribute(fontd, attr);
79 array[*idx] = cfstr2buf(name);
85 static void get_trait(CFDictionaryRef traits, CFStringRef attribute,
88 CFNumberRef cftrait = CFDictionaryGetValue(traits, attribute);
89 if (!CFNumberGetValue(cftrait, kCFNumberFloatType, trait))
93 static void get_font_traits(CTFontDescriptorRef fontd,
94 ASS_FontProviderMetaData *meta)
96 float weight, slant, width;
98 CFDictionaryRef traits =
99 CTFontDescriptorCopyAttribute(fontd, kCTFontTraitsAttribute);
101 get_trait(traits, kCTFontWeightTrait, &weight);
102 get_trait(traits, kCTFontSlantTrait, &slant);
103 get_trait(traits, kCTFontWidthTrait, &width);
107 // Printed all of my system fonts (see if'deffed code below). Here is how
108 // CoreText 'normalized' weights maps to CSS/libass:
110 // opentype: 0 100 200 300 400 500 600 700 800 900
111 // css: LIGHT REG MED SBOLD BOLD BLACK EXTRABL
112 // libass: LIGHT MEDIUM BOLD
113 // coretext: -0.4 0.0 0.23 0.3 0.4 0.62
117 else if (weight >= 0.4)
119 else if (weight >= 0.3)
121 else if (weight >= 0.23)
123 else if (weight >= -0.4)
129 meta->slant = FONT_SLANT_ITALIC;
131 meta->slant = FONT_SLANT_NONE;
134 meta->width = FONT_WIDTH_CONDENSED;
135 else if (width >= 0.2)
136 meta->width = FONT_WIDTH_EXPANDED;
138 meta->width = FONT_WIDTH_NORMAL;
143 get_name(fontd, kCTFontDisplayNameAttribute, name, &idx);
144 char *file = get_font_file(fontd);
146 "Font traits for: %-40s [%-50s] "
147 "<slant: %f, %03d>, <weight: (%f, %03d)>, <width: %f, %03d>\n",
149 slant, meta->slant, weight, meta->weight, width, meta->width);
155 static void process_descriptors(ASS_FontProvider *provider, CFArrayRef fontsd)
157 ASS_FontProviderMetaData meta;
159 char *identifiers[1];
165 for (int i = 0; i < CFArrayGetCount(fontsd); i++) {
166 CTFontDescriptorRef fontd = CFArrayGetValueAtIndex(fontsd, i);
169 char *path = get_font_file(fontd);
170 if (strcmp("", path) == 0) {
171 // skip the font if the URL field in the font descriptor is empty
176 memset(&meta, 0, sizeof(meta));
177 get_font_traits(fontd, &meta);
179 get_name(fontd, kCTFontFamilyNameAttribute, families, &meta.n_family);
180 meta.families = families;
183 get_name(fontd, kCTFontNameAttribute, identifiers, &zero);
184 get_name(fontd, kCTFontDisplayNameAttribute, fullnames, &meta.n_fullname);
185 meta.fullnames = fullnames;
187 CFCharacterSetRef chset =
188 CTFontDescriptorCopyAttribute(fontd, kCTFontCharacterSetAttribute);
189 ass_font_provider_add_font(provider, &meta, path, index,
190 identifiers[0], (void*)chset);
192 for (int j = 0; j < meta.n_family; j++)
193 free(meta.families[j]);
195 for (int j = 0; j < meta.n_fullname; j++)
196 free(meta.fullnames[j]);
198 free(identifiers[0]);
204 #if CT_FONTS_EAGER_LOAD
205 static void scan_fonts(ASS_Library *lib, ASS_FontProvider *provider)
208 CTFontCollectionRef coll = CTFontCollectionCreateFromAvailableFonts(NULL);
209 CFArrayRef fontsd = CTFontCollectionCreateMatchingFontDescriptors(coll);
211 process_descriptors(provider, fontsd);
218 #if CT_FONTS_LAZY_LOAD
219 static void match_fonts(ASS_Library *lib, ASS_FontProvider *provider,
222 const size_t attributes_n = 3;
223 CTFontDescriptorRef ctdescrs[attributes_n];
224 CFMutableDictionaryRef cfattrs[attributes_n];
225 CFStringRef attributes[attributes_n] = {
226 kCTFontFamilyNameAttribute,
227 kCTFontDisplayNameAttribute,
228 kCTFontNameAttribute,
232 CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
234 for (int i = 0; i < attributes_n; i++) {
235 cfattrs[i] = CFDictionaryCreateMutable(NULL, 0, 0, 0);
236 CFDictionaryAddValue(cfattrs[i], attributes[i], cfname);
237 ctdescrs[i] = CTFontDescriptorCreateWithAttributes(cfattrs[i]);
240 CFArrayRef descriptors =
241 CFArrayCreate(NULL, (const void **)&ctdescrs, attributes_n, NULL);
243 CTFontCollectionRef ctcoll =
244 CTFontCollectionCreateWithFontDescriptors(descriptors, 0);
247 CTFontCollectionCreateMatchingFontDescriptors(ctcoll);
249 process_descriptors(provider, fontsd);
255 for (int i = 0; i < attributes_n; i++) {
256 CFRelease(cfattrs[i]);
257 CFRelease(ctdescrs[i]);
260 CFRelease(descriptors);
265 static char *get_fallback(void *priv, const char *family, uint32_t codepoint)
267 char *failed = family;
268 CFStringRef name = CFStringCreateWithBytes(
269 0, (UInt8 *)failed, sizeof(failed), kCFStringEncodingUTF8, false);
270 CTFontRef font = CTFontCreateWithName(name, 0, NULL);
271 uint32_t codepointle = OSSwapHostToLittleInt32(codepoint);
272 CFStringRef r = CFStringCreateWithBytes(
273 0, (UInt8*)&codepointle, sizeof(codepointle),
274 kCFStringEncodingUTF32LE, false);
275 CTFontRef fb = CTFontCreateForString(font, r, CFRangeMake(0, 1));
276 CFStringRef cffamily = CTFontCopyFamilyName(fb);
277 char *res_family = cfstr2buf(cffamily);
288 static ASS_FontProviderFuncs coretext_callbacks = {
289 .check_glyph = check_glyph,
290 .destroy_font = destroy_font,
291 #if !CT_FONTS_EAGER_LOAD
292 .match_fonts = match_fonts,
294 .get_fallback = get_fallback,
298 ass_coretext_add_provider(ASS_Library *lib, ASS_FontSelector *selector,
301 ASS_FontProvider *provider =
302 ass_font_provider_new(selector, &coretext_callbacks, NULL);
304 #if CT_FONTS_EAGER_LOAD
305 scan_fonts(lib, provider);