<?php /*

 Composr
 Copyright (c) Christopher Graham, 2004-2024

 See docs/LICENSE.md for full licensing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/

/**
 * @license    http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright  Christopher Graham
 * @package    core_graphic_text
 */

// Server-side font rendering code

/**
 * Find whether the server supports TTF.
 *
 * @return boolean Whether TTF is supported
 */
function has_ttf() : bool
{
    static $result = null;
    if ($result !== null) {
        return $result;
    }

    if (!function_exists('imagettftext')) {
        $result = false;
        return $result;
    }

    if (!array_key_exists('FreeType Support', gd_info())) {
        $result = false;
        return $result;
    }

    // Actually this is a problematic check because it fails on the quick installer (fonts not extracted) and we're testing for server ttf support, not if this file exists / can be processed
    /*
    if (@imagettfbbox(26.0, 0.0, get_file_base() . '/data/fonts/NotoSans-Regular.ttf', 'test') === false) {
        $result = false;
        return $result;
    }
    */

    $result = true;
    return $result;
}

/**
 * Enforce TTF support.
 *
 * @return ?Tempcode Error message (null: none)
 */
function check_ttf() : ?object
{
    if (!has_ttf()) {
        return do_lang_tempcode('REQUIRES_TTF');
    }
    return null;
}

/**
 * Find the default font to use.
 *
 * @param  boolean $mono Whether we want a monospaced font
 * @return string Default font file name (without .ttf)
 */
function find_default_font(bool $mono = false) : string
{
    // We have some knowledge of fonts that are fairly comprehensive, widely available, and normal looking, even though we don't bundle them all (for file-size and licensing reasons)
    if ($mono) {
        $precedence = [
            'Courier New Bold', // Microsoft (preferred, de facto standard)
            'Courier New', // Microsoft (preferred, de facto standard)
            'NotoSans-Regular', // Google (bundled)
            'FreeMonoBold', // GNU FreeFont
            'FreeMono', // GNU FreeFont
            'LiberationMono-Bold', // Liberation fonts
            'LiberationMono', // Liberation fonts
            'UbuntuMono-Bold', // Ubuntu
            'UbuntuMono', // Ubuntu
            'FiraMono-Bold', // Firefox OS
            'FiraMono', // Firefox OS
            'RobotoMono-Bold', // Google
            'RobotoMono', // Google
        ];
    } else {
        $precedence = [
            'Segoe UI', // Microsoft (preferred, de facto standard)
            'Tahoma', // Microsoft (preferred, de facto standard)
            'Verdana', // Microsoft (preferred, de facto standard)
            'NotoSans-Regular', // Google (bundled)
            'FreeSans', // GNU FreeFont
            'FreeSerif', // GNU FreeFont
            'Calibri', // Microsoft
            'Arial', // Microsoft
            'Microsoft Sans Serif', // Microsoft
            'Trebuchet MS', // Microsoft
            'Georgia', // Microsoft
            'Times New Roman', // Microsoft(ish)
            'LiberationSans', // Liberation fonts
            'LiberationSerif', // Liberation fonts
            'DejaVuSans', // DejaVu fonts (fork of Bitstream Vera)
            'DejaVuSerif', // DejaVu fonts (fork of Bitstream Vera)
            'Ubuntu', // Ubuntu
            'FiraSans', // Firefox OS
            'Roboto', // Google
            'OpenSans', // Google
            'Cantarell', // GNOME
            'Quivira',
            'Libertine',
            'Lato',
            'Gentium',
        ];
    }

    $all_fonts = find_all_fonts(true);
    foreach ($precedence as $font) {
        if (array_key_exists($font, $all_fonts)) {
            return $font;
        }
    }

    // We could not find a font with the right character support, so we'll compromise
    $all_fonts = find_all_fonts();
    foreach ($precedence as $font) {
        if (array_key_exists($font, $all_fonts)) {
            return $font;
        }
    }

    fatal_exit(do_lang_tempcode('INTERNAL_ERROR'));
    return '';
}

/**
 * Find all available fonts.
 *
 * @param  boolean $test_character_support Test the font supports the characters in the site title (a rough way to check it is a reasonable font to use on this site)
 * @param  boolean $include_paths Whether to include local paths to the font files
 * @return array A map between font name and label to use for the font
 */
function find_all_fonts(bool $test_character_support = false, bool $include_paths = false) : array
{
    if (($test_character_support) && (has_ttf())) {
        $test_text = get_site_name();
        require_code('character_sets');
    }

    $fonts = [];
    foreach (['data_custom/fonts', 'data/fonts'] as $path) {
        $dh = @opendir(get_file_base() . '/' . $path);
        if ($dh !== false) {
            while (($f = readdir($dh)) !== false) {
                if (substr($f, -4) == '.ttf') {
                    if (($test_character_support) && (has_ttf())) {
                        $_test_text = cms_preg_replace_safe('#\s#', '', $test_text);
                        $_chars = [];
                        $len = cms_mb_strlen($_test_text);
                        for ($i = 0; $i < $len; $i++) {
                            $char = cms_mb_substr($_test_text, $i, 1);
                            if (trim($char) != '') {
                                $bounds = @imagettfbbox(26.0, 0.0, $path . '/' . $f, convert_to_html_encoding($char));
                                $_chars[$char] = serialize($bounds);
                            }
                        }
                        if ((count(array_unique($_chars)) == 1) && (count($_chars) > 1)) {
                            continue; // It's all box outlines due to unsupported characters
                        }
                    }

                    $font = basename($f, '.ttf');

                    $font_label = $font;
                    do {
                        $_font_label = $font_label;
                        $font_label = preg_replace('#\s*(italic|Italic|it|It|Oblique)($| )#i', ' Italic$2', $font_label); // We double define due to Turkish issue
                        $font_label = preg_replace('#\s*(Extra Bold)($| )#i', ' Extra Bold$2', $font_label);
                        $font_label = preg_replace('#\s*(Bold|Bd)($| )#i', ' Bold$2', $font_label);
                        $font_label = preg_replace('#\s*(Semi Bold)($| )#i', ' Semi Bold$2', $font_label);
                        $font_label = preg_replace('#\s*(Black)($| )#i', ' Black$2', $font_label);
                        $font_label = preg_replace('#\s*(Medium)($| )#i', ' Medium$2', $font_label);
                        $font_label = preg_replace('#\s*(Medium|Regular)($| )#i', ' Regular$2', $font_label);
                        $font_label = preg_replace('#\s*(Light)($| )#i', ' Light$2', $font_label);
                        $font_label = preg_replace('#\s*(Extra Light)($| )#i', ' Extra Light$2', $font_label);
                        $font_label = preg_replace('#\s*(Thin)($| )#i', ' Thin$2', $font_label);
                    } while ($font_label != $_font_label);
                    $font_label = str_replace('Italic', ' Italic', $font_label);
                    $font_label = trim(preg_replace('#\s+#', ' ', $font_label));

                    if ($include_paths) {
                        $fonts[$path . '/' . $font] = $font_label;
                    } else {
                        $fonts[$font] = $font_label;
                    }
                }
            }
            closedir($dh);
        }
    }

    ksort($fonts);

    return $fonts;
}

/**
 * Find the path to a font.
 *
 * @param  string $font Font file name (without .ttf)
 * @return PATH The file path
 */
function find_font_path(string $font) : string
{
    $file_base = get_custom_file_base() . '/data_custom/fonts/';
    if (!file_exists($file_base . '/' . $font . '.ttf')) {
        $file_base = get_file_base() . '/data/fonts/';
    }
    if (!file_exists($file_base . '/' . $font . '.ttf')) {
        return $file_base . 'NotoSans-Bold.ttf'; // Fallback
    }
    return $file_base . $font . '.ttf';
}
