dlib C++ Library - fonts.cpp

    // Copyright (C) 2005  Davis E. King (davisking@users.sourceforge.net), and Nils Labugt, Keita Mochizuki
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_FONTs_CPP_
    #define DLIB_FONTs_CPP_

    #include "fonts.h"

    #include "../serialize.h"
    #include <sstream>
    #include "../base64.h"
    #include "../compress_stream.h"
    #include <fstream>
    #include "../tokenizer.h"
    #include "nativefont.h"

    namespace dlib
    {

    // ----------------------------------------------------------------------------------------

        const std::string get_decoded_string_with_default_font_data()
        {
            dlib::base64::kernel_1a base64_coder;
            dlib::compress_stream::kernel_1ea compressor;
            std::ostringstream sout;
            std::istringstream sin;

            /*
                SOURCE BDF FILE (helvR12.bdf) COMMENTS
                COMMENT $XConsortium: helvR12.bdf,v 1.15 95/01/26 18:02:58 gildea Exp $
                COMMENT $Id: helvR12.bdf,v 1.26 2004-11-28 20:08:46+00 mgk25 Rel $
                COMMENT
                COMMENT +
                COMMENT  Copyright 1984-1989, 1994 Adobe Systems Incorporated.
                COMMENT  Copyright 1988, 1994 Digital Equipment Corporation.
                COMMENT
                COMMENT  Adobe is a trademark of Adobe Systems Incorporated which may be
                COMMENT  registered in certain jurisdictions.
                COMMENT  Permission to use these trademarks is hereby granted only in
                COMMENT  association with the images described in this file.
                COMMENT
                COMMENT  Permission to use, copy, modify, distribute and sell this software
                COMMENT  and its documentation for any purpose and without fee is hereby
                COMMENT  granted, provided that the above copyright notices appear in all
                COMMENT  copies and that both those copyright notices and this permission
                COMMENT  notice appear in supporting documentation, and that the names of
                COMMENT  Adobe Systems and Digital Equipment Corporation not be used in
                COMMENT  advertising or publicity pertaining to distribution of the software
                COMMENT  without specific, written prior permission.  Adobe Systems and
                COMMENT  Digital Equipment Corporation make no representations about the
                COMMENT  suitability of this software for any purpose.  It is provided "as
                COMMENT  is" without express or implied warranty.
                COMMENT -
            */

            // The base64 encoded data we want to decode and return.
            sout << "AXF+zOQzCgGitrKiOCGEL4hlIv1ZenWJyjMQ4rJ6f/oPMeHqsZn+8XnpehwFQTz3dtUGlZRAUoOa";
            sout << "uVo8UiplcFxuK69A+94rpMCMAyEeeOwZ/tRzkX4eKuU3L4xtsJDknMiYUNKaMrYimb1QJ0E+SRqQ";
            sout << "wATrMTecYNZvJJm02WibiwE4cJ5scvkHNl4KJT5QfdwRdGopTyUVdZvRvtbTLLjsJP0fQEQLqemf";
            sout << "qPE4kDD79ehrBIwLO1Y6TzxtrrIoQR57zlwTUyLenqRtSN3VLtjWYd82cehRIlTLtuxBg2s+zZVq";
            sout << "jNlNnYTSM+Swy06qnQgg+Dt0lhtlB9shR1OAlcfCtTW6HKoBk/FGeDmjTGW4bNCGv7RjgM6TlLDg";
            sout << "ZYSSA6ZCCAKBgE++U32gLHCCiVkPTkkp9P6ioR+e3SSKRNm9p5MHf+ZQ3LJkW8KFJ/K9gKT1yvyv";
            sout << "F99pAvOOq16tHRFvzBs+xZj/mUpH0lGIS7kLWr9oP2KuccVrz25aJn3kDruwTYoD+CYlOqtPO0Mv";
            sout << "dEI0LUR0Ykp1M2rWo76fJ/fpzHjV7737hjkNPJ13nO72RMDr4R5V3uG7Dw7Ng+vGX3WgJZ4wh1JX";
            sout << "pl2VMqC5JXccctzvnQvnuvBvRm7THgwQUgMKKT3WK6afUUVlJy8DHKuU4k1ibfVMxAmrwKdTUX2w";
            sout << "cje3A05Qji3aop65qEdwgI5O17HIVoRQOG/na+XRMowOfUvI4H8Z4+JGACfRrQctgYDAM9eJzm8i";
            sout << "PibyutmJfZBGg0a3oC75S5R9lTxEjPocnEyJRYNnmVnVAmKKbTbTsznuaD+D1XhPdr2t3A4bRTsp";
            sout << "toKKtlFnd9YGwLWwONDwLnoQ/IXwyF7txrRHNSVToh772U0Aih/yn5vnmcMF750eiMzRAgXu5sbR";
            sout << "VXEOVCiLgVevN5umkvjZt1eGTSSzDMrIvnv4nyOfaFsD+I76wQfgLqd71rheozGtjNc0AOTx4Ggc";
            sout << "eUSFHTDAVfTExBzckurtyuIAqF986a0JLHCtsDpBa2wWNuiQYOH3/LX1zkdU2hdamhBW774bpEwr";
            sout << "dguMxxOeDGOBgIlM5gxXGYXSf5IN3fUAEPfOPRxB7T+tpjFnWd7cg+JMabci3zhJ9ANaYT7HGeTX";
            sout << "bulKnGHjYrR1BxdK3YeliogQRU4ytmxlyL5zlNFU/759mA8XSfIPMEZn9Vxkb00q1htF7REiDcr3";
            sout << "kW1rtPAc7VQNEhT54vK/YF6rMvjO7kBZ/vLYo7E8e8hDKEnY8ucrC3KGmeo31Gei74BBcEbvJBd3";
            sout << "/YAaIKgXWwU2wSUw9wLq2RwGwyguvKBx0J/gn27tjcVAHorRBwxzPpk8r+YPyN+SifSzEL7LEy1G";
            sout << "lPHxmXTrcqnH9qraeAqXJUJvU8SJJpf/tmsAE+XSKD/kpVBnT5qXsJ1SRFS7MtfPjE1j/NYbaQBI";
            sout << "bOrh81zaYCEJR0IKHWCIsu/MC3zKXfkxFgQ9XpYAuWjSSK64YpgkxSMe8VG8yYvigOw2ODg/z4FU";
            sout << "+HpnEKF/M/mKfLKK1i/8BV7xcYVHrhEww1QznoFklJs/pEg3Kd5PE1lRii6hvTn6McVAkw+YbH9q";
            sout << "/sg4gFIAvai64hMcZ1oIZYppj3ZN6KMdyhK5s4++ZS/YOV2nNhW73ovivyi2Tjg7lxjJJtsYrLKb";
            sout << "zIN1slOICKYwBq42TFBcFXaZ6rf0Czd09tL+q6A1Ztgr3BNuhCenjhWN5ji0LccGYZo6bLTggRG/";
            sout << "Uz6K3CBBU/byLs79c5qCohrr7rlpDSdbuR+aJgNiWoU6T0i2Tvua6h51LcWEHy5P2n146/Ae2di4";
            sout << "eh20WQvclrsgm1oFTGD0Oe85GKOTA7vvwKmLBc1wwA0foTuxzVgj0TMTFBiYLTLG4ujUyBYy1N6e";
            sout << "H8EKi8H+ZAlqezrjABO3BQr33ewdZL5IeJ4w7gdGUDA6+P+7cODcBW50X9++6YTnKctuEw6aXBpy";
            sout << "GgcMfPE61G8YKBbFGFic3TVvGCLvre1iURv+F+hU4/ee6ILuPnpYnSXX2iCIK/kmkBse8805d4Qe";
            sout << "DG/8rBW9ojvAgc0jX7CatPEMHGkcz+KIZoKMI7XXK4PJpGQUdq6EdIhJC4koXEynjwwXMeC+jJqH";
            sout << "agwrlDNssq/8AA==";

            // Put the data into the istream sin
            sin.str(sout.str());
            sout.str("");

            // Decode the base64 text into its compressed binary form
            base64_coder.decode(sin,sout);
            sin.clear();
            sin.str(sout.str());
            sout.str("");

            // Decompress the data into its original form
            compressor.decompress(sin,sout);

            // Return the decoded and decompressed data
            return sout.str();
        }

        default_font::
        default_font (
        )
        {
            using namespace std;
            l = new letter[256];

            try
            {
                istringstream sin(get_decoded_string_with_default_font_data());

                for (int i = 0; i < 256; ++i)
                {
                    deserialize(l[i],sin);
                }

            }
            catch (...)
            {
                delete [] l;
                throw;
            }
        }

    // ----------------------------------------------------------------------------------------

        void serialize (
            const letter& item,
            std::ostream& out
        )
        {
            try
            {
                serialize(item.w,out);
                serialize(item.count,out);

                for (unsigned long i = 0; i < item.count; ++i)
                {
                    serialize(item.points[i].x,out);
                    serialize(item.points[i].y,out);
                }
            }
            catch (serialization_error e)
            {
                throw serialization_error(e.info + "\n   while serializing object of type letter");
            }
        }

        void deserialize (
            letter& item,
            std::istream& in
        )
        {
            try
            {
                if (item.points)
                    delete [] item.points;

                deserialize(item.w,in);
                deserialize(item.count,in);

                if (item.count > 0)
                    item.points = new letter::point[item.count];
                else
                    item.points = 0;

                for (unsigned long i = 0; i < item.count; ++i)
                {
                    deserialize(item.points[i].x,in);
                    deserialize(item.points[i].y,in);
                }
            }
            catch (serialization_error e)
            {
                item.w = 0;
                item.count = 0;
                item.points = 0;
                throw serialization_error(e.info + "\n   while deserializing object of type letter");
            }
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        namespace bdf_font_helpers
        {
            class bdf_parser
            {
            public:
                bdf_parser( std::istream& in ) : in_( in )
                {
                    std::string str_tmp;
                    int int_tmp;

                    str_tmp = "STARTFONT";      int_tmp = STARTFONT;        keyword_map.add( str_tmp, int_tmp );
                    str_tmp = "FONTBOUNDINGBOX";int_tmp = FONTBOUNDINGBOX;  keyword_map.add( str_tmp, int_tmp );
                    str_tmp = "DWIDTH";         int_tmp = DWIDTH;           keyword_map.add( str_tmp, int_tmp );
                    str_tmp = "CHARS";          int_tmp = CHARS;            keyword_map.add( str_tmp, int_tmp );
                    str_tmp = "STARTCHAR";      int_tmp = STARTCHAR;        keyword_map.add( str_tmp, int_tmp );
                    str_tmp = "ENCODING";       int_tmp = ENCODING;         keyword_map.add( str_tmp, int_tmp );
                    str_tmp = "BBX";            int_tmp = BBX;              keyword_map.add( str_tmp, int_tmp );
                    str_tmp = "BITMAP";         int_tmp = BITMAP;           keyword_map.add( str_tmp, int_tmp );
                    str_tmp = "ENDCHAR";        int_tmp = ENDCHAR;          keyword_map.add( str_tmp, int_tmp );
                    str_tmp = "ENDFONT";        int_tmp = ENDFONT;          keyword_map.add( str_tmp, int_tmp );
                    str_tmp = "DEFAULT_CHAR";   int_tmp = DEFAULT_CHAR;     keyword_map.add( str_tmp, int_tmp );

                    tokzr.set_identifier_token( tokzr.uppercase_letters(), tokzr.uppercase_letters() + "_" );
                    tokzr.set_stream( in );

                }

                enum bdf_enums
                {
                    NO_KEYWORD = 0,
                    STARTFONT = 1,
                    FONTBOUNDINGBOX = 2,
                    DWIDTH = 4,
                    DEFAULT_CHAR = 8,
                    CHARS = 16,
                    STARTCHAR = 32,
                    ENCODING = 64,
                    BBX = 128,
                    BITMAP = 256,
                    ENDCHAR = 512,
                    ENDFONT = 1024

                };
                struct header_info
                {
                    int FBBx, FBBy, Xoff, Yoff;
                    int dwx0, dwy0;
                    bool has_global_dw;
                    long default_char;
                };
                struct char_info
                {
                    int dwx0, dwy0;
                    int BBw, BBh, BBxoff0x, BByoff0y;
                    array2d<char>::kernel_1a bitmap;
                    bool has_dw;
                };
                bool parse_header( header_info& info )
                {
                    if ( required_keyword( STARTFONT ) == false )
                        return false;    // parse_error: required keyword missing
                    info.has_global_dw = false;
                    int find = FONTBOUNDINGBOX | DWIDTH | DEFAULT_CHAR;
                    int stop = CHARS | STARTCHAR | ENCODING | BBX | BITMAP | ENDCHAR | ENDFONT;
                    int res;
                    while ( 1 )
                    {
                        res = find_keywords( find | stop );
                        if ( res & FONTBOUNDINGBOX )
                        {
                            in_ >> info.FBBx >> info.FBBy >> info.Xoff >> info.Yoff;
                            if ( in_.fail() )
                                return false;    // parse_error
                            find &= ~FONTBOUNDINGBOX;
                            continue;
                        }
                        if ( res & DWIDTH )
                        {
                            in_ >> info.dwx0 >> info.dwy0;
                            if ( in_.fail() )
                                return false;    // parse_error
                            find &= ~DWIDTH;
                            info.has_global_dw = true;
                            continue;
                        }
                        if ( res & DEFAULT_CHAR )
                        {
                            in_ >> info.default_char;
                            if ( in_.fail() )
                                return false;    // parse_error
                            find &= ~DEFAULT_CHAR;
                            continue;
                        }
                        if ( res & NO_KEYWORD )
                            return false;    // parse_error: unexpected EOF
                        break;
                    }
                    if ( res != CHARS || ( find & FONTBOUNDINGBOX ) )
                        return false;    // parse_error: required keyword missing or unexpeced keyword
                    return true;
                }
                int parse_glyph( char_info& info, unichar& enc )
                {
                    info.has_dw = false;
                    int e;
                    int res;
                    while ( 1 )
                    {
                        res = find_keywords( ENCODING );
                        if ( res != ENCODING )
                            return 0; // no more glyphs
                        in_ >> e;
                        if ( in_.fail() )
                            return -1;    // parse_error
                        if ( e >= static_cast<int>(enc) )
                            break;
                    }
                    int find = BBX | DWIDTH;
                    int stop = STARTCHAR | ENCODING | BITMAP | ENDCHAR | ENDFONT;
                    while ( 1 )
                    {
                        res = find_keywords( find | stop );
                        if ( res & BBX )
                        {
                            in_ >> info.BBw >> info.BBh >> info.BBxoff0x >> info.BByoff0y;
                            if ( in_.fail() )
                                return -1;    // parse_error
                            find &= ~BBX;
                            continue;
                        }
                        if ( res & DWIDTH )
                        {
                            in_ >> info.dwx0 >> info.dwy0;
                            if ( in_.fail() )
                                return -1;    // parse_error
                            find &= ~DWIDTH;
                            info.has_dw = true;
                            continue;
                        }
                        if ( res & NO_KEYWORD )
                            return -1;    // parse_error: unexpected EOF
                        break;
                    }
                    if ( res != BITMAP || ( find != NO_KEYWORD ) )
                        return -1;     // parse_error: required keyword missing or unexpeced keyword
                    unsigned h = info.BBh;
                    unsigned w = ( info.BBw + 7 ) / 8 * 2;
                    info.bitmap.set_size( h, w );
                    for ( unsigned r = 0;r < h;r++ )
                    {
                        trim();
                        std::string str = "";
                        extract_hex(str);
                        if(str.size() < w)
                            return -1;    // parse_error
                        for ( unsigned c = 0;c < w;c++ )
                            info.bitmap[r][c] = str[c];
                    }
                    if ( in_.fail() )
                        return -1;      // parse_error
                    if ( required_keyword( ENDCHAR ) == false )
                        return -1;      // parse_error: required keyword missing
                    enc = e;
                    return 1;
                }
            private:
                map<std::string, int>::kernel_1a_c keyword_map;
                tokenizer::kernel_1a_c tokzr;
                std::istream& in_;
                void extract_hex(std::string& str)
                {
                    int type;
                    std::string token;
                    while ( 1 )
                    {
                        type = tokzr.peek_type();
                        if ( type == tokenizer::kernel_1a_c::IDENTIFIER || type == tokenizer::kernel_1a_c::NUMBER )
                        {
                            tokzr.get_token( type, token );
                            str += token;
                            continue;
                        }
                        break;
                    }
                }
                void trim()
                {
                    int type;
                    std::string token;
                    while ( 1 )
                    {
                        type = tokzr.peek_type();
                        if ( type == tokenizer::kernel_1a_c::WHITE_SPACE || type == tokenizer::kernel_1a_c::END_OF_LINE )
                        {
                            tokzr.get_token( type, token );
                            continue;
                        }
                        break;
                    }
                }
                bool required_keyword( int kw )
                {
                    int type;
                    std::string token;
                    while ( 1 )
                    {
                        tokzr.get_token( type, token );
                        if ( type == tokenizer::kernel_1a_c::WHITE_SPACE || type == tokenizer::kernel_1a_c::END_OF_LINE )
                            continue;
                        if ( type != tokenizer::kernel_1a_c::IDENTIFIER || keyword_map.is_in_domain( token ) == false || ( keyword_map[token] & kw ) == 0 )
                            return false;
                        break;
                    }
                    return true;
                }
                int find_keywords( int find )
                {
                    int type;
                    std::string token;
                    while ( 1 )
                    {
                        tokzr.get_token( type, token );
                        if ( type == tokenizer::kernel_1a_c::END_OF_FILE )
                            return NO_KEYWORD;
                        if ( type == tokenizer::kernel_1a_c::IDENTIFIER && keyword_map.is_in_domain( token ) == true )
                        {
                            int kw = keyword_map[token];
                            if ( kw & find )
                                return kw;
                        }
                    }
                    return true;
                }

            };

        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
    //                    bdf_font functions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        bdf_font::bdf_font(
            long default_char_
        ) :
            default_char(0),
            is_initialized( false ),
            right_overflow_( 0 ),
            has_global_width( false ),
            specified_default_char( default_char_ )
        {
            // make sure gl contains at least one letter
            gl.expand(1);
        }

    // ----------------------------------------------------------------------------------------

        void bdf_font::adjust_metrics(
        )
        {
            if ( is_initialized == false )
                return;
            // set starting values for fbb
            if ( gl[default_char].num_of_points() > 0 )
            {
                letter& g =  gl[default_char];
                fbb.set_top( g[0].y );
                fbb.set_bottom( g[0].y );
                fbb.set_left( g[0].x );
                fbb.set_right( g[0].x );
            }
            else
            {
                // ok, the default char was a space
                // let's just choose some safe arbitrary values then...
                fbb.set_top( 10000 );
                fbb.set_bottom( -10000 );
                fbb.set_left( 10000 );
                fbb.set_right( -10000 );
            }
            right_overflow_ = 0;
            for ( unichar n = 0; n < gl.size(); n++ )
            {
                letter& g = gl[n];
                unsigned short nr_pts = g.num_of_points();
                for ( unsigned short k = 0;k < nr_pts;k++ )
                {
                    fbb.set_top( std::min( fbb.top(), (long)g[k].y ) );
                    fbb.set_left( std::min( fbb.left(), (long)g[k].x ) );
                    fbb.set_bottom( std::max( fbb.bottom(), (long)g[k].y ) );
                    fbb.set_right( std::max( fbb.right(), (long)g[k].x ) );
                    right_overflow_ = std::max( right_overflow_, (unsigned long)(g[k].x - g.width()) );  // superfluous?
                }
            }
        }

    // ----------------------------------------------------------------------------------------

        long bdf_font::
        read_bdf_file(
            std::istream& in,
            unichar max_enc,
            unichar min_enc
        )
        {
            using namespace bdf_font_helpers;

            bdf_parser parser( in );
            bdf_parser::header_info hinfo;
            bdf_parser::char_info cinfo;

            gl.expand(max_enc+1);
            hinfo.default_char =  - 1;
            if ( is_initialized == false || static_cast<std::streamoff>(in.tellg()) == std::ios::beg )
            {
                if ( parser.parse_header( hinfo ) == false )
                    return 0;   // parse_error: invalid or missing header
            }
            else
            {
                // not start of file, so use values from previous read.
                hinfo.has_global_dw = has_global_width;
                hinfo.dwx0 = global_width;
            }
            int res;
            unichar nr_letters_added = 0;
            unsigned width;
            for ( unichar n = min_enc; n <= max_enc; n++ )
            {
                if ( in.eof() )
                    break;
                long pos = in.tellg();
                res = parser.parse_glyph( cinfo, n );
                if ( res < 0 )
                    return 0;  // parse_error
                if ( res == 0 )
                    continue;
                if ( n > max_enc )
                {
                    in.seekg( pos );
                    break;
                }

                if ( cinfo.has_dw == false )
                {
                    if ( hinfo.has_global_dw == false )
                        return 0;    // neither width info for the glyph, nor for the font as a whole (monospace).
                    width = hinfo.dwx0;
                }
                else
                    width = cinfo.dwx0;

                if ( bitmap_to_letter( cinfo.bitmap, n, width, cinfo.BBxoff0x, cinfo.BByoff0y ) == false )
                    return 0;
                nr_letters_added++;

                if ( is_initialized == false )
                {
                    // Bonding rectangle for the font.
                    fbb.set_top( -( hinfo.Yoff + hinfo.FBBy - 1 ) );
                    fbb.set_bottom( -hinfo.Yoff );
                    fbb.set_left( hinfo.Xoff );
                    fbb.set_right( hinfo.Xoff + hinfo.FBBx - 1 );
                    // We need to compute this after all the glyphs are loaded.
                    right_overflow_ = 0;
                    // set this to something valid now, just in case.
                    default_char = n;
                    // Save any global width in case we later read from the same file.
                    has_global_width = hinfo.has_global_dw;
                    if ( has_global_width )
                        global_width = hinfo.dwx0;
                    // dont override value specified in the constructor with value specified in the file
                    if ( specified_default_char < 0 && hinfo.default_char >= 0 )
                        specified_default_char = hinfo.default_char;

                    is_initialized = true;
                }
            }
            if ( is_initialized == false )
                return 0;   // Not a single glyph was found within the specified range.

            if ( specified_default_char >= 0 )
                default_char = specified_default_char;
            // no default char specified, try find something sane.
            else
                default_char = 0;

            return nr_letters_added;
        }

    // ----------------------------------------------------------------------------------------

        bool bdf_font::
        bitmap_to_letter(
            array2d<char>::kernel_1a& bitmap,
            unichar enc,
            unsigned long width,
            int x_offset,
            int y_offset
        )
        {
            unsigned nr_points = 0;
            bitmap.reset();
            while ( bitmap.move_next() )
            {
                unsigned char ch = bitmap.element();
                if ( ch > '9' )
                    ch -= 'A' - '9' - 1;
                ch -= '0';
                if ( ch > 0xF )
                    return false;   // parse error: invalid hex digit
                bitmap.element() = ch;
                if ( ch & 8 )
                    nr_points++;
                if ( ch & 4 )
                    nr_points++;
                if ( ch & 2 )
                    nr_points++;
                if ( ch & 1 )
                    nr_points++;
            }

            letter( width, nr_points ).swap(gl[enc]);

            unsigned index = 0;
            for ( int r = 0;r < bitmap.nr();r++ )
            {
                for ( int c = 0;c < bitmap.nc();c++ )
                {
                    int x = x_offset + c * 4;
                    int y = -( y_offset + bitmap.nr() - r - 1 );
                    char ch = bitmap[r][c];
                    letter& glyph =  gl[enc];
                    if ( ch & 8 )
                    {
                        glyph[index] = letter::point( x, y );
                        right_overflow_ = std::max( right_overflow_, x - width );
                        index++;
                    }
                    if ( ch & 4 )
                    {
                        glyph[index] = letter::point( x + 1, y );
                        right_overflow_ = std::max( right_overflow_, x + 1 - width );
                        index++;
                    }
                    if ( ch & 2 )
                    {
                        glyph[index] = letter::point( x + 2, y );
                        right_overflow_ = std::max( right_overflow_, x + 2 - width );
                        index++;
                    }
                    if ( ch & 1 )
                    {
                        glyph[index] = letter::point( x + 3, y );
                        right_overflow_ = std::max( right_overflow_, x + 3 - width );
                        index++;
                    }
                }
            }
            return true;
        }

    // ----------------------------------------------------------------------------------------

        const shared_ptr_thread_safe<font> get_native_font (
        )
        {
            return nativefont::native_font::get_font();
        }

    // ----------------------------------------------------------------------------------------

    }

    #endif // DLIB_FONTs_CPP_