c23ac8a388ebcd424262cc99fb44aa54ecabb1a5
[vlc.git] / modules / text_renderer / freetype / fonts / darwin.c
1 /*****************************************************************************
2  * darwin.c : Put text on the video, using freetype2
3  *****************************************************************************
4  * Copyright (C) 2015 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Felix Paul K├╝hne <fkuehne@videolan.org>
8  *          Jean-Baptiste Kempf <jb@videolan.org>
9  *          Salah-Eddin Shaban <salshaaban@gmail.com>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation, Inc.,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc_common.h>
35 #include <vlc_filter.h>                                      /* filter_sys_t */
36
37 #include <CoreFoundation/CoreFoundation.h>
38 #include <CoreText/CoreText.h>
39
40 #include "../platform_fonts.h"
41
42 char* getPathForFontDescription(CTFontDescriptorRef fontDescriptor);
43 void addNewFontToFamily(filter_t *p_filter, CTFontDescriptorRef iter, char *path, vlc_family_t *family);
44
45 static char* getCStringCopyForCFStringRef(CFStringRef cfstring, CFStringEncoding encoding)
46 {
47     // Try to get pointer directly
48     const char *cptr = CFStringGetCStringPtr(cfstring, encoding);
49     if (cptr) {
50         return strdup(cptr);
51     }
52
53     // If it fails, use CFStringGetCString
54     CFIndex len = CFStringGetLength(cfstring);
55     CFIndex size = CFStringGetMaximumSizeForEncoding(len, encoding);
56     char *buffer = calloc(len + 1, sizeof(char));
57
58     if (CFStringGetCString(cfstring, buffer, size, encoding)) {
59         return buffer;
60     } else {
61         free(buffer);
62         return NULL;
63     }
64 }
65
66 char* getPathForFontDescription(CTFontDescriptorRef fontDescriptor)
67 {
68     CFURLRef url = CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontURLAttribute);
69     CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
70     char *retPath = getCStringCopyForCFStringRef(path, kCFStringEncodingUTF8);
71     CFRelease(path);
72     CFRelease(url);
73     return retPath;
74 }
75
76 void addNewFontToFamily(filter_t *p_filter, CTFontDescriptorRef iter, char *path, vlc_family_t *p_family)
77 {
78     bool b_bold = false;
79     bool b_italic = false;
80     CFDictionaryRef fontTraits = CTFontDescriptorCopyAttribute(iter, kCTFontTraitsAttribute);
81     CFNumberRef trait = CFDictionaryGetValue(fontTraits, kCTFontWeightTrait);
82     float traitValue = 0.;
83     CFNumberGetValue(trait, kCFNumberFloatType, &traitValue);
84     b_bold = traitValue > 0.23;
85     trait = CFDictionaryGetValue(fontTraits, kCTFontSlantTrait);
86     traitValue = 0.;
87     CFNumberGetValue(trait, kCFNumberFloatType, &traitValue);
88     b_italic = traitValue > 0.03;
89
90 #ifndef NDEBUG
91     msg_Dbg(p_filter, "New font: bold %i italic %i path '%s'", b_bold, b_italic, path);
92 #else
93     VLC_UNUSED(p_filter);
94 #endif
95     NewFont(path, 0, b_bold, b_italic, p_family);
96
97     CFRelease(fontTraits);
98 }
99
100 const vlc_family_t *CoreText_GetFamily(filter_t *p_filter, const char *psz_family)
101 {
102     filter_sys_t *p_sys = p_filter->p_sys;
103
104     if (unlikely(psz_family == NULL)) {
105         return NULL;
106     }
107
108     char *psz_lc = ToLower(psz_family);
109     if (unlikely(!psz_lc)) {
110         return NULL;
111     }
112
113     /* let's double check if we have parsed this family already */
114     vlc_family_t *p_family = vlc_dictionary_value_for_key(&p_sys->family_map, psz_lc);
115     if (p_family) {
116         free(psz_lc);
117         return p_family;
118     }
119
120     CTFontCollectionRef coreTextFontCollection = NULL;
121     CFArrayRef matchedFontDescriptions = NULL;
122
123     /* we search for family name, display name and name to find them all */
124     const size_t numberOfAttributes = 3;
125     CTFontDescriptorRef coreTextFontDescriptors[numberOfAttributes];
126     CFMutableDictionaryRef coreTextAttributes[numberOfAttributes];
127     CFStringRef attributeNames[numberOfAttributes] = {
128         kCTFontFamilyNameAttribute,
129         kCTFontDisplayNameAttribute,
130         kCTFontNameAttribute,
131     };
132
133 #ifndef NDEBUG
134     msg_Dbg(p_filter, "Creating new family for '%s'", psz_family);
135 #endif
136
137     CFStringRef familyName = CFStringCreateWithCString(kCFAllocatorDefault,
138                                                        psz_family,
139                                                        kCFStringEncodingUTF8);
140     for (size_t x = 0; x < numberOfAttributes; x++) {
141         coreTextAttributes[x] = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL);
142         CFDictionaryAddValue(coreTextAttributes[x], attributeNames[x], familyName);
143         coreTextFontDescriptors[x] = CTFontDescriptorCreateWithAttributes(coreTextAttributes[x]);
144     }
145
146     CFArrayRef coreTextFontDescriptorsArray = CFArrayCreate(kCFAllocatorDefault,
147                                                             (const void **)&coreTextFontDescriptors,
148                                                             numberOfAttributes, NULL);
149
150     coreTextFontCollection = CTFontCollectionCreateWithFontDescriptors(coreTextFontDescriptorsArray, 0);
151     if (coreTextFontCollection == NULL) {
152         goto end;
153     }
154
155     matchedFontDescriptions = CTFontCollectionCreateMatchingFontDescriptors(coreTextFontCollection);
156     if (matchedFontDescriptions == NULL) {
157         goto end;
158     }
159
160     CFIndex numberOfFoundFontDescriptions = CFArrayGetCount(matchedFontDescriptions);
161
162     char *path = NULL;
163
164     /* create a new family object */
165     p_family = NewFamily(p_filter, psz_lc, &p_sys->p_families, &p_sys->family_map, psz_lc);
166     if (unlikely(!p_family)) {
167         goto end;
168     }
169
170     for (CFIndex i = 0; i < numberOfFoundFontDescriptions; i++) {
171         CTFontDescriptorRef iter = CFArrayGetValueAtIndex(matchedFontDescriptions, i);
172         path = getPathForFontDescription(iter);
173
174         /* check if the path is empty, which can happen in rare circumstances */
175         if (path != NULL) {
176             if (strcmp("", path) == 0) {
177                 FREENULL(path);
178                 continue;
179             }
180         }
181
182         addNewFontToFamily(p_filter, iter, path, p_family);
183     }
184
185 end:
186     if (matchedFontDescriptions != NULL) {
187         CFRelease(matchedFontDescriptions);
188     }
189     if (coreTextFontCollection != NULL) {
190         CFRelease(coreTextFontCollection);
191     }
192
193     for (size_t x = 0; x < numberOfAttributes; x++) {
194         CFRelease(coreTextAttributes[x]);
195         CFRelease(coreTextFontDescriptors[x]);
196     }
197
198     CFRelease(coreTextFontDescriptorsArray);
199     CFRelease(familyName);
200
201     return p_family;
202 }
203
204 vlc_family_t *CoreText_GetFallbacks(filter_t *p_filter, const char *psz_family, uni_char_t codepoint)
205 {
206     filter_sys_t *p_sys = p_filter->p_sys;
207     if (unlikely(psz_family == NULL)) {
208         return NULL;
209     }
210
211     vlc_family_t *p_family = NULL;
212     CFStringRef postScriptFallbackFontname = NULL;
213     CTFontDescriptorRef fallbackFontDescriptor = NULL;
214     char *psz_lc_fallback = NULL;
215     char *psz_fontPath = NULL;
216
217     CFStringRef familyName = CFStringCreateWithCString(kCFAllocatorDefault,
218                                                        psz_family,
219                                                        kCFStringEncodingUTF8);
220     CTFontRef font = CTFontCreateWithName(familyName, 0, NULL);
221     uint32_t littleEndianCodePoint = OSSwapHostToLittleInt32(codepoint);
222     CFStringRef codepointString = CFStringCreateWithBytes(kCFAllocatorDefault,
223                                                           (const UInt8 *)&littleEndianCodePoint,
224                                                           sizeof(littleEndianCodePoint),
225                                                           kCFStringEncodingUTF32LE,
226                                                           false);
227     CTFontRef fallbackFont = CTFontCreateForString(font, codepointString, CFRangeMake(0,1));
228     CFStringRef fallbackFontFamilyName = CTFontCopyFamilyName(fallbackFont);
229
230     /* create a new family object */
231     const char *psz_fallbackFamilyName = CFStringGetCStringPtr(fallbackFontFamilyName, kCFStringEncodingUTF8);
232     if (psz_fallbackFamilyName == NULL) {
233         goto done;
234     }
235 #ifndef NDEBUG
236     msg_Dbg(p_filter, "Will deploy fallback font '%s'", psz_fallbackFamilyName);
237 #endif
238
239     psz_lc_fallback = ToLower(strdup(psz_fallbackFamilyName));
240
241     p_family = vlc_dictionary_value_for_key(&p_sys->family_map, psz_lc_fallback);
242     if (p_family) {
243         goto done;
244     }
245
246     p_family = NewFamily(p_filter, psz_lc_fallback, &p_sys->p_families, &p_sys->family_map, psz_lc_fallback);
247     if (unlikely(!p_family)) {
248         goto done;
249     }
250
251     postScriptFallbackFontname = CTFontCopyPostScriptName(fallbackFont);
252     fallbackFontDescriptor = CTFontDescriptorCreateWithNameAndSize(postScriptFallbackFontname, 0.);
253     psz_fontPath = getPathForFontDescription(fallbackFontDescriptor);
254
255     /* check if the path is empty, which can happen in rare circumstances */
256     if (psz_fontPath != NULL) {
257         if (strcmp("", psz_fontPath) == 0) {
258             goto done;
259         }
260     } else {
261         goto done;
262     }
263
264     addNewFontToFamily(p_filter, fallbackFontDescriptor, strdup(psz_fontPath), p_family);
265
266 done:
267     CFRelease(familyName);
268     CFRelease(font);
269     CFRelease(codepointString);
270     CFRelease(fallbackFont);
271     CFRelease(fallbackFontFamilyName);
272     free(psz_lc_fallback);
273     free(psz_fontPath);
274     if (postScriptFallbackFontname != NULL)
275         CFRelease(postScriptFallbackFontname);
276     if (fallbackFontDescriptor != NULL)
277         CFRelease(fallbackFontDescriptor);
278     return p_family;
279 }