<commit_before>//******************************************************************************
//
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//******************************************************************************

#include "Starboard.h"
#include "CGFontInternal.h"
#include "Foundation/NSString.h"
#include "Foundation/NSMutableDictionary.h"
#include "Foundation/NSData.h"
#include "Foundation/NSArray.h"
#include "Foundation/NSFileManager.h"
#include "UIKit/UIFont.h"
#include "UIKit/UIFontDescriptor.h"
#include "UIFontDescriptorInternal.h"

extern "C" {
#include <ft2build.h>
#include FT_FREETYPE_H
#include <ftglyph.h>
#include <tttables.h>
#include <ftadvanc.h>
#include <ftsizes.h>
}

FT_Library _fontLib;
NSMutableDictionary* _fontList;
CFMutableDictionaryRef _fontInstance, _fontSizingInstance;
NSMutableDictionary* g_fontCache;
NSMutableDictionary* _fontDataCache;

@implementation UIFont {
@public
    void *_font, *_sizingFont;
    idretaintype(NSString) _name;
    UIFontDescriptor* _descriptor;
    idretain _fileName;
    float _size;
    float _horizontalScale;

    bool _cachedCapHeight, _cachedXHeight;
    float _capHeight, _xHeight;
}
static FT_Face getFace(id faceName, bool sizing, UIFont* fontInfo = nil) {
    FT_Error err;

    //  Look it up in our cache
    FT_Face val;
    if (!sizing) {
        val = (FT_Face)CFDictionaryGetValue(_fontInstance, (void*)faceName);
        if (val != 0) {
            return val;
        }
    } else {
        val = (FT_Face)CFDictionaryGetValue(_fontSizingInstance, (void*)faceName);
        if (val != 0) {
            return val;
        }
    }

    //  Lookup the filename
    char* _faceName = (char*)[faceName UTF8String];
    id filename = [_fontList objectForKey:faceName];
    if (filename == nil)
        filename = @"C:/Windows/Fonts/SegoeUI.ttf";

    if (filename == nil) {
        if (strcmp(_faceName, "Helvetica")) {
            EbrDebugLog("*** FONT NOT FOUND: %s -- falling back to Helvetica. ***\n", _faceName);
            filename = [_fontList objectForKey:@"Helvetica"];
        }

        if (filename == nil)
            return nullptr;
    }

#if defined(USE_ROBOTO_FONT)
    if ((strstr(_faceName, "Bold") != NULL || strstr(_faceName, "Heavy") != NULL) &&
        (strstr(_faceName, "Italic") != NULL || strstr(_faceName, "Oblique") != NULL)) {
        filename = @"/fonts/Roboto-BoldItalic.ttf";
    } else if (strstr(_faceName, "Bold") != NULL || strstr(_faceName, "Heavy") != NULL) {
        filename = @"/fonts/Roboto-Bold.ttf";
    } else if ((strstr(_faceName, "Italic") != NULL || strstr(_faceName, "Oblique") != NULL)) {
        filename = @"/fonts/Roboto-Italic.ttf";
    } else {
        filename = @"/fonts/Roboto-Regular.ttf";
    }
    if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) {
        filename = @"/fonts/Helvetica.ttf";
    }
    if (strstr(_faceName, "Condensed") != NULL) {
        if (fontInfo != nil)
            fontInfo->_horizontalScale = 0.85f;
    }
#endif

    id cachedData = [_fontDataCache objectForKey:filename];
    if (!cachedData) {
        EbrDebugLog("Loading font %s\n", [filename UTF8String]);

        cachedData = [[NSData alloc] initWithContentsOfFile:filename];
        [_fontDataCache setObject:cachedData forKey:filename];
    }

    FT_Face ret;
    char* pFont = (char*)[cachedData bytes];
    DWORD fontLen = [cachedData length];

    _CGFontLock();
    err = FT_New_Memory_Face(_fontLib, (const FT_Byte*)pFont, fontLen, 0, &ret);
    _CGFontUnlock();
    assert(err == 0);

//  Adjust the line height up by 20% for system fonts to be consistent
#if 0 // defined( USE_ROBOTO_FONT )
int ascenderDelta = (int) (ret->ascender * 0.2);
ret->ascender += ascenderDelta;
ret->height += ascenderDelta;
#endif

    if (!sizing) {
        CFDictionarySetValue(_fontInstance, (const void*)faceName, (void*)ret);
    } else {
        CFDictionarySetValue(_fontSizingInstance, (const void*)faceName, (void*)ret);
    }

    return ret;
}

/**
 @Status Stub
*/
+ (NSArray*)familyNames {
    UNIMPLEMENTED();
    return [_fontList allKeys];
}

+ (UIFont*)fontWithData:(NSData*)data {
    //  Load the font
    char* pFont = (char*)[data bytes];
    DWORD fontLen = [data length];

    UIFont* ret = [self alloc];

    ret->_name = @"Custom font";
    ret->_size = 0.0f;
    ret->_horizontalScale = 1.0f;

    FT_Error err;

    char* pCopy = (char*)EbrMalloc(fontLen);
    memcpy(pCopy, pFont, fontLen);

    _CGFontLock();
    err = FT_New_Memory_Face(_fontLib, (const FT_Byte*)pCopy, fontLen, 0, (FT_Face*)&ret->_font);
    err = FT_New_Memory_Face(_fontLib, (const FT_Byte*)pCopy, fontLen, 0, (FT_Face*)&ret->_sizingFont);
    _CGFontUnlock();

    if (err != 0) {
        EbrDebugLog("Error loading font\n");
        ret->_font = getFace(@"Helvetica", false);
        ret->_sizingFont = getFace(@"Helvetica", true);
    }
    // assert(err == 0);

    if ([data respondsToSelector:@selector(_fileName)]) {
        ret->_fileName = [[data _fileName] lastPathComponent];
        if (ret->_fileName) {
            CFDictionarySetValue(_fontInstance, (const void*)(id)ret->_fileName, (void*)ret->_font);
            CFDictionarySetValue(_fontSizingInstance, (const void*)(id)ret->_fileName, (void*)ret->_sizingFont);
        }
    }

    return [ret autorelease];
}

/**
 @Status Interoperable
*/
+ (UIFont*)fontWithName:(NSString*)name size:(float)size {
    UIFont* ret = [self alloc];

    ret->_name = name;
    ret->_size = size;
    ret->_horizontalScale = 1.0f;

    id cached = [g_fontCache objectForKey:(id)ret];
    if (cached != nil) {
        [ret release];
        return cached;
    }

    if (name == nil) {
        EbrDebugLog("Warning: Font name is nil!\n");
        ret->_font = getFace(@"Helvetica", false, ret);
        ret->_sizingFont = getFace(@"Helvetica", true, ret);
    } else {
        ret->_font = getFace(name, false, ret);
        ret->_sizingFont = getFace(name, true, ret);
    }

    if (ret->_font == nil) {
        return nil;
    }<commit_msg>UIFont: default to the system font size if the user requests size 0.<commit_after>//******************************************************************************
//
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//******************************************************************************

#include "Starboard.h"
#include "CGFontInternal.h"
#include "Foundation/NSString.h"
#include "Foundation/NSMutableDictionary.h"
#include "Foundation/NSData.h"
#include "Foundation/NSArray.h"
#include "Foundation/NSFileManager.h"
#include "UIKit/UIFont.h"
#include "UIKit/UIFontDescriptor.h"
#include "UIFontDescriptorInternal.h"

extern "C" {
#include <ft2build.h>
#include FT_FREETYPE_H
#include <ftglyph.h>
#include <tttables.h>
#include <ftadvanc.h>
#include <ftsizes.h>
}

FT_Library _fontLib;
NSMutableDictionary* _fontList;
CFMutableDictionaryRef _fontInstance, _fontSizingInstance;
NSMutableDictionary* g_fontCache;
NSMutableDictionary* _fontDataCache;

@implementation UIFont {
@public
    void *_font, *_sizingFont;
    idretaintype(NSString) _name;
    UIFontDescriptor* _descriptor;
    idretain _fileName;
    float _size;
    float _horizontalScale;

    bool _cachedCapHeight, _cachedXHeight;
    float _capHeight, _xHeight;
}
static FT_Face getFace(id faceName, bool sizing, UIFont* fontInfo = nil) {
    FT_Error err;

    //  Look it up in our cache
    FT_Face val;
    if (!sizing) {
        val = (FT_Face)CFDictionaryGetValue(_fontInstance, (void*)faceName);
        if (val != 0) {
            return val;
        }
    } else {
        val = (FT_Face)CFDictionaryGetValue(_fontSizingInstance, (void*)faceName);
        if (val != 0) {
            return val;
        }
    }

    //  Lookup the filename
    char* _faceName = (char*)[faceName UTF8String];
    id filename = [_fontList objectForKey:faceName];
    if (filename == nil)
        filename = @"C:/Windows/Fonts/SegoeUI.ttf";

    if (filename == nil) {
        if (strcmp(_faceName, "Helvetica")) {
            EbrDebugLog("*** FONT NOT FOUND: %s -- falling back to Helvetica. ***\n", _faceName);
            filename = [_fontList objectForKey:@"Helvetica"];
        }

        if (filename == nil)
            return nullptr;
    }

#if defined(USE_ROBOTO_FONT)
    if ((strstr(_faceName, "Bold") != NULL || strstr(_faceName, "Heavy") != NULL) &&
        (strstr(_faceName, "Italic") != NULL || strstr(_faceName, "Oblique") != NULL)) {
        filename = @"/fonts/Roboto-BoldItalic.ttf";
    } else if (strstr(_faceName, "Bold") != NULL || strstr(_faceName, "Heavy") != NULL) {
        filename = @"/fonts/Roboto-Bold.ttf";
    } else if ((strstr(_faceName, "Italic") != NULL || strstr(_faceName, "Oblique") != NULL)) {
        filename = @"/fonts/Roboto-Italic.ttf";
    } else {
        filename = @"/fonts/Roboto-Regular.ttf";
    }
    if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) {
        filename = @"/fonts/Helvetica.ttf";
    }
    if (strstr(_faceName, "Condensed") != NULL) {
        if (fontInfo != nil)
            fontInfo->_horizontalScale = 0.85f;
    }
#endif

    id cachedData = [_fontDataCache objectForKey:filename];
    if (!cachedData) {
        EbrDebugLog("Loading font %s\n", [filename UTF8String]);

        cachedData = [[NSData alloc] initWithContentsOfFile:filename];
        [_fontDataCache setObject:cachedData forKey:filename];
    }

    FT_Face ret;
    char* pFont = (char*)[cachedData bytes];
    DWORD fontLen = [cachedData length];

    _CGFontLock();
    err = FT_New_Memory_Face(_fontLib, (const FT_Byte*)pFont, fontLen, 0, &ret);
    _CGFontUnlock();
    assert(err == 0);

//  Adjust the line height up by 20% for system fonts to be consistent
#if 0 // defined( USE_ROBOTO_FONT )
int ascenderDelta = (int) (ret->ascender * 0.2);
ret->ascender += ascenderDelta;
ret->height += ascenderDelta;
#endif

    if (!sizing) {
        CFDictionarySetValue(_fontInstance, (const void*)faceName, (void*)ret);
    } else {
        CFDictionarySetValue(_fontSizingInstance, (const void*)faceName, (void*)ret);
    }

    return ret;
}

/**
 @Status Stub
*/
+ (NSArray*)familyNames {
    UNIMPLEMENTED();
    return [_fontList allKeys];
}

+ (UIFont*)fontWithData:(NSData*)data {
    //  Load the font
    char* pFont = (char*)[data bytes];
    DWORD fontLen = [data length];

    UIFont* ret = [self alloc];

    ret->_name = @"Custom font";
    ret->_size = 0.0f;
    ret->_horizontalScale = 1.0f;

    FT_Error err;

    char* pCopy = (char*)EbrMalloc(fontLen);
    memcpy(pCopy, pFont, fontLen);

    _CGFontLock();
    err = FT_New_Memory_Face(_fontLib, (const FT_Byte*)pCopy, fontLen, 0, (FT_Face*)&ret->_font);
    err = FT_New_Memory_Face(_fontLib, (const FT_Byte*)pCopy, fontLen, 0, (FT_Face*)&ret->_sizingFont);
    _CGFontUnlock();

    if (err != 0) {
        EbrDebugLog("Error loading font\n");
        ret->_font = getFace(@"Helvetica", false);
        ret->_sizingFont = getFace(@"Helvetica", true);
    }
    // assert(err == 0);

    if ([data respondsToSelector:@selector(_fileName)]) {
        ret->_fileName = [[data _fileName] lastPathComponent];
        if (ret->_fileName) {
            CFDictionarySetValue(_fontInstance, (const void*)(id)ret->_fileName, (void*)ret->_font);
            CFDictionarySetValue(_fontSizingInstance, (const void*)(id)ret->_fileName, (void*)ret->_sizingFont);
        }
    }

    return [ret autorelease];
}

/**
 @Status Interoperable
*/
+ (UIFont*)fontWithName:(NSString*)name size:(float)size {
    UIFont* ret = [self alloc];

    if (size == 0.0f) {
        size = [self systemFontSize];
    }

    ret->_name = name;
    ret->_size = size;
    ret->_horizontalScale = 1.0f;

    id cached = [g_fontCache objectForKey:(id)ret];
    if (cached != nil) {
        [ret release];
        return cached;
    }

    if (name == nil) {
        EbrDebugLog("Warning: Font name is nil!\n");
        ret->_font = getFace(@"Helvetica", false, ret);
        ret->_sizingFont = getFace(@"Helvetica", true, ret);
    } else {
        ret->_font = getFace(name, false, ret);
        ret->_sizingFont = getFace(name, true, ret);
    }

    if (ret->_font == nil) {
        return nil;
    }
{'fraction_non_alphanumeric': 0.1604891956782713, 'fraction_numerical': 0.004576830732292917, 'mean_word_length': 3.2886100386100385, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5738762', 'n_tokens_mistral': 4452, 'n_tokens_neox': 4235, 'n_words': 1054}