darwin.c 9.97 KB
Newer Older
1
/*****************************************************************************
2
 * darwin.c : Put text on the video, using freetype2
3
 *****************************************************************************
4
 * Copyright (C) 2015 VLC authors and VideoLAN
5 6
 * $Id$
 *
7
 * Authors: Felix Paul Kühne <fkuehne@videolan.org>
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
 *          Jean-Baptiste Kempf <jb@videolan.org>
 *          Salah-Eddin Shaban <salshaaban@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_filter.h>                                      /* filter_sys_t */

37 38
#include <CoreFoundation/CoreFoundation.h>
#include <CoreText/CoreText.h>
39 40 41

#include "../platform_fonts.h"

42
char* getPathForFontDescription(CTFontDescriptorRef fontDescriptor);
43
void addNewFontToFamily(filter_t *p_filter, CTFontDescriptorRef iter, char *path, vlc_family_t *family);
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
static char* getCStringCopyForCFStringRef(CFStringRef cfstring, CFStringEncoding encoding)
{
    // Try to get pointer directly
    const char *cptr = CFStringGetCStringPtr(cfstring, encoding);
    if (cptr) {
        return strdup(cptr);
    }

    // If it fails, use CFStringGetCString
    CFIndex len = CFStringGetLength(cfstring);
    CFIndex size = CFStringGetMaximumSizeForEncoding(len, encoding);
    char *buffer = calloc(len + 1, sizeof(char));

    if (CFStringGetCString(cfstring, buffer, size, encoding)) {
        return buffer;
    } else {
        free(buffer);
        return NULL;
    }
}

66 67 68 69
char* getPathForFontDescription(CTFontDescriptorRef fontDescriptor)
{
    CFURLRef url = CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontURLAttribute);
    CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
70
    char *retPath = getCStringCopyForCFStringRef(path, kCFStringEncodingUTF8);
71 72 73 74 75
    CFRelease(path);
    CFRelease(url);
    return retPath;
}

76
void addNewFontToFamily(filter_t *p_filter, CTFontDescriptorRef iter, char *path, vlc_family_t *p_family)
77 78 79 80 81 82 83 84 85 86 87 88 89
{
    bool b_bold = false;
    bool b_italic = false;
    CFDictionaryRef fontTraits = CTFontDescriptorCopyAttribute(iter, kCTFontTraitsAttribute);
    CFNumberRef trait = CFDictionaryGetValue(fontTraits, kCTFontWeightTrait);
    float traitValue = 0.;
    CFNumberGetValue(trait, kCFNumberFloatType, &traitValue);
    b_bold = traitValue > 0.23;
    trait = CFDictionaryGetValue(fontTraits, kCTFontSlantTrait);
    traitValue = 0.;
    CFNumberGetValue(trait, kCFNumberFloatType, &traitValue);
    b_italic = traitValue > 0.03;

90
#ifndef NDEBUG
91
    msg_Dbg(p_filter, "New font: bold %i italic %i path '%s'", b_bold, b_italic, path);
92 93
#else
    VLC_UNUSED(p_filter);
94
#endif
95 96 97 98 99
    NewFont(path, 0, b_bold, b_italic, p_family);

    CFRelease(fontTraits);
}

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 109 110 111 112 113 114 115 116 117 118 119
    }

    char *psz_lc = ToLower(psz_family);
    if (unlikely(!psz_lc)) {
        return NULL;
    }

    /* let's double check if we have parsed this family already */
    vlc_family_t *p_family = vlc_dictionary_value_for_key(&p_sys->family_map, psz_lc);
    if (p_family) {
        free(psz_lc);
        return p_family;
    }

120 121
    CTFontCollectionRef coreTextFontCollection = NULL;
    CFArrayRef matchedFontDescriptions = NULL;
122

123
    /* we search for family name, display name and name to find them all */
124 125 126 127 128 129 130 131 132
    const size_t numberOfAttributes = 3;
    CTFontDescriptorRef coreTextFontDescriptors[numberOfAttributes];
    CFMutableDictionaryRef coreTextAttributes[numberOfAttributes];
    CFStringRef attributeNames[numberOfAttributes] = {
        kCTFontFamilyNameAttribute,
        kCTFontDisplayNameAttribute,
        kCTFontNameAttribute,
    };

133 134 135 136 137 138 139
#ifndef NDEBUG
    msg_Dbg(p_filter, "Creating new family for '%s'", psz_family);
#endif

    CFStringRef familyName = CFStringCreateWithCString(kCFAllocatorDefault,
                                                       psz_family,
                                                       kCFStringEncodingUTF8);
140 141
    for (size_t x = 0; x < numberOfAttributes; x++) {
        coreTextAttributes[x] = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL);
142
        CFDictionaryAddValue(coreTextAttributes[x], attributeNames[x], familyName);
143 144 145
        coreTextFontDescriptors[x] = CTFontDescriptorCreateWithAttributes(coreTextAttributes[x]);
    }

146 147 148
    CFArrayRef coreTextFontDescriptorsArray = CFArrayCreate(kCFAllocatorDefault,
                                                            (const void **)&coreTextFontDescriptors,
                                                            numberOfAttributes, NULL);
149

150 151 152 153
    coreTextFontCollection = CTFontCollectionCreateWithFontDescriptors(coreTextFontDescriptorsArray, 0);
    if (coreTextFontCollection == NULL) {
        goto end;
    }
154

155 156 157 158
    matchedFontDescriptions = CTFontCollectionCreateMatchingFontDescriptors(coreTextFontCollection);
    if (matchedFontDescriptions == NULL) {
        goto end;
    }
159

160
    CFIndex numberOfFoundFontDescriptions = CFArrayGetCount(matchedFontDescriptions);
161

162 163
    char *path = NULL;

164 165 166 167 168 169
    /* create a new family object */
    p_family = NewFamily(p_filter, psz_lc, &p_sys->p_families, &p_sys->family_map, psz_lc);
    if (unlikely(!p_family)) {
        goto end;
    }

170 171 172 173 174
    for (CFIndex i = 0; i < numberOfFoundFontDescriptions; i++) {
        CTFontDescriptorRef iter = CFArrayGetValueAtIndex(matchedFontDescriptions, i);
        path = getPathForFontDescription(iter);

        /* check if the path is empty, which can happen in rare circumstances */
175 176
        if (path == NULL || *path == '\0') {
            FREENULL(path);
177
            continue;
178 179
        }

180
        addNewFontToFamily(p_filter, iter, path, p_family);
181
    }
182

183 184 185 186 187 188 189
end:
    if (matchedFontDescriptions != NULL) {
        CFRelease(matchedFontDescriptions);
    }
    if (coreTextFontCollection != NULL) {
        CFRelease(coreTextFontCollection);
    }
190

191 192 193
    for (size_t x = 0; x < numberOfAttributes; x++) {
        CFRelease(coreTextAttributes[x]);
        CFRelease(coreTextFontDescriptors[x]);
194 195
    }

196
    CFRelease(coreTextFontDescriptorsArray);
197 198 199 200
    CFRelease(familyName);

    return p_family;
}
201

202 203
vlc_family_t *CoreText_GetFallbacks(filter_t *p_filter, const char *psz_family, uni_char_t codepoint)
{
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
    filter_sys_t *p_sys = p_filter->p_sys;
    if (unlikely(psz_family == NULL)) {
        return NULL;
    }

    vlc_family_t *p_family = NULL;
    CFStringRef postScriptFallbackFontname = NULL;
    CTFontDescriptorRef fallbackFontDescriptor = NULL;
    char *psz_lc_fallback = NULL;
    char *psz_fontPath = NULL;

    CFStringRef familyName = CFStringCreateWithCString(kCFAllocatorDefault,
                                                       psz_family,
                                                       kCFStringEncodingUTF8);
    CTFontRef font = CTFontCreateWithName(familyName, 0, NULL);
219 220 221 222 223 224
    uint32_t littleEndianCodePoint = OSSwapHostToLittleInt32(codepoint);
    CFStringRef codepointString = CFStringCreateWithBytes(kCFAllocatorDefault,
                                                          (const UInt8 *)&littleEndianCodePoint,
                                                          sizeof(littleEndianCodePoint),
                                                          kCFStringEncodingUTF32LE,
                                                          false);
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
    CTFontRef fallbackFont = CTFontCreateForString(font, codepointString, CFRangeMake(0,1));
    CFStringRef fallbackFontFamilyName = CTFontCopyFamilyName(fallbackFont);

    /* create a new family object */
    const char *psz_fallbackFamilyName = CFStringGetCStringPtr(fallbackFontFamilyName, kCFStringEncodingUTF8);
    if (psz_fallbackFamilyName == NULL) {
        goto done;
    }
#ifndef NDEBUG
    msg_Dbg(p_filter, "Will deploy fallback font '%s'", psz_fallbackFamilyName);
#endif

    psz_lc_fallback = ToLower(strdup(psz_fallbackFamilyName));

    p_family = vlc_dictionary_value_for_key(&p_sys->family_map, psz_lc_fallback);
    if (p_family) {
        goto done;
    }

    p_family = NewFamily(p_filter, psz_lc_fallback, &p_sys->p_families, &p_sys->family_map, psz_lc_fallback);
    if (unlikely(!p_family)) {
        goto done;
    }

    postScriptFallbackFontname = CTFontCopyPostScriptName(fallbackFont);
    fallbackFontDescriptor = CTFontDescriptorCreateWithNameAndSize(postScriptFallbackFontname, 0.);
    psz_fontPath = getPathForFontDescription(fallbackFontDescriptor);

    /* check if the path is empty, which can happen in rare circumstances */
    if (psz_fontPath != NULL) {
        if (strcmp("", psz_fontPath) == 0) {
            goto done;
        }
    } else {
        goto done;
    }

262
    addNewFontToFamily(p_filter, fallbackFontDescriptor, strdup(psz_fontPath), p_family);
263 264 265 266 267 268 269 270 271 272 273 274 275 276

done:
    CFRelease(familyName);
    CFRelease(font);
    CFRelease(codepointString);
    CFRelease(fallbackFont);
    CFRelease(fallbackFontFamilyName);
    free(psz_lc_fallback);
    free(psz_fontPath);
    if (postScriptFallbackFontname != NULL)
        CFRelease(postScriptFallbackFontname);
    if (fallbackFontDescriptor != NULL)
        CFRelease(fallbackFontDescriptor);
    return p_family;
277
}