// Copyright (C) 2005 Davis E. King (davis@dlib.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 <fstream> #include <memory> #include <sstream> #include "../serialize.h" #include "../base64.h" #include "../compress_stream.h" #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> 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.resize(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.resize(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>& 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 std::shared_ptr<font> get_native_font ( ) { return nativefont::native_font::get_font(); } // ---------------------------------------------------------------------------------------- } #endif // DLIB_FONTs_CPP_