LCOV - code coverage report
Current view: top level - src/rendering/fonts - read_font.cc (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 361 382 94.5 %
Date: 2022-06-15 20:16:21 Functions: 56 71 78.9 %
Branches: 398 600 66.3 %

           Branch data     Line data    Source code
       1                 :            : #ifdef RUN_TESTS
       2                 :            : # include <gtest/gtest.h>
       3                 :            : #endif
       4                 :            : /** @file rendering/fonts/read_font.cc
       5                 :            :  * @brief Contains low-level functions for parsing font files. Used by ReadFontsList and FontPlan.
       6                 :            :  */
       7                 :            : 
       8                 :            : #include <regex>
       9                 :            : #include <fstream>
      10                 :            : #include <unordered_map>
      11                 :            : #include <mutex>
      12                 :            : #include <set>
      13                 :            : 
      14                 :            : #include "read_font.hh"
      15                 :            : #include "gunzip.hh"
      16                 :            : #include "endian.hh"
      17                 :            : #include "share.hh"
      18                 :            : 
      19                 :            : using namespace std::literals;
      20                 :            : 
      21                 :            : static constexpr auto Ropt = std::regex_constants::ECMAScript | std::regex_constants::optimize;
      22                 :            : 
      23                 :            : // Syntactic shorthand for creating regular expressions.
      24                 :         83 : static std::regex operator ""_r(const char* pattern, std::size_t length)
      25                 :            : {
      26                 :         83 :     return std::regex(pattern,length, Ropt);
      27                 :            : }
      28                 :            : 
      29                 :            : #pragma GCC push_options
      30                 :            : #pragma GCC optimize ("Ofast")
      31                 :            : template<unsigned l>
      32                 :  163797143 : static bool rmatch(std::string_view str, std::smatch& mat, const std::string& line)
      33                 :            : {
      34   [ +  +  +  +  :  163797143 :     static std::regex r(std::string(str), Ropt);
             +  -  +  - ]
      35                 :  163797145 :     return std::regex_match(line, mat, r);
      36                 :            : }
      37                 :            : #pragma GCC pop_options
      38                 :            : 
      39                 :            : /* Read a file in share/encodings/ that specifies character mapping for this encoding */
      40                 :         11 : static std::unordered_map<char32_t,char32_t> ReadEncoding(std::string_view filename)
      41                 :            : {
      42         [ +  - ]:         11 :     std::unordered_map<char32_t, char32_t> result;
      43   [ +  -  +  -  :         44 :     auto [fn, status] = FindShareFile(std::filesystem::path("encodings") / std::filesystem::path(filename));
          +  -  +  -  -  
                      + ]
      44         [ -  + ]:         12 :     if(std::filesystem::exists(status))
      45                 :            :     {
      46         [ +  - ]:         10 :         std::ifstream f(fn);
      47                 :         10 :         auto delim = " \t"sv, hex = "0123456789ABCDEFabcdef"sv;
      48   [ +  -  +  + ]:      16119 :         for(std::string line; std::getline(f,line), f; )
      49   [ +  -  +  - ]:      16109 :             if(!line.empty() && line[0] != '#')
      50                 :            :             {
      51                 :      16109 :                 std::size_t space    = line.find_first_of(delim);
      52         [ -  + ]:      16109 :                 if(space == line.npos) continue;
      53                 :      16109 :                 std::size_t notspace = line.find_first_not_of(delim, space);
      54         [ -  + ]:      16109 :                 if(notspace == line.npos) continue;
      55                 :      16109 :                 std::size_t notdigit = line.find_first_not_of(hex, notspace);
      56         [ +  + ]:      16109 :                 if(notdigit == line.npos) notdigit = line.size();
      57                 :            : 
      58         [ +  - ]:      16109 :                 result.emplace(std::stoi(std::string(line, 0, space), nullptr, 16),
      59   [ +  -  +  - ]:      48327 :                                std::stoi(std::string(line, notspace, notdigit-notspace), nullptr, 16));
      60                 :          0 :             }
      61                 :            :         //std::sort(result.begin(), result.end());
      62                 :            :         //for(auto p: result)
      63                 :            :         //    std::printf("Enc %s 0x%X = 0x%X\n", filename.data(), (int)p.first,(int)p.second);
      64                 :         10 :     }
      65                 :            :     //else
      66                 :            :     //{
      67                 :            :     //    std::printf("%s does not exist\n", filename.data());
      68                 :            :     //}
      69                 :         11 :     return result;
      70                 :         11 : }
      71                 :            : 
      72                 :     795013 : std::vector<char32_t> BDFtranslateToUnicode(int index, std::string_view reg, std::string_view enc)
      73                 :            : {
      74         [ -  + ]:     795013 :     if(index == -1) return {65533}; // notdef in ibmfonts
      75                 :            : 
      76                 :     795013 :     std::string regenc(reg);
      77   [ +  +  +  +  :     795561 :     if(!reg.empty() && !enc.empty()) regenc += '-';
                   +  - ]
      78         [ +  - ]:     795542 :     regenc += enc;
      79   [ +  +  +  + ]:    8995328 :     for(char& c: regenc) if(c >= 'A' && c <= 'Z') c += 32; // tolower
      80                 :            : 
      81                 :     796512 :     char32_t result = index;
      82         [ +  + ]:     796512 :     if(regenc == "iso10646-1"sv
      83   [ +  +  +  + ]:     796512 :     || regenc == "iso-8859-1"sv)
      84                 :            :     {
      85                 :            :         result = index;
      86                 :            :     }
      87         [ +  + ]:      66406 :     else if(regenc == "jisx0201.1976-0"sv)
      88                 :            :     {
      89         [ +  + ]:        636 :         if(index == 0x5C) result =  0xA5;
      90         [ +  + ]:        632 :         else if(index == 0x7E) result =  0x203E;
      91         [ +  + ]:        628 :         else if(index >= 0xA1) result =  index - 0xA1 + 0xFF61;
      92                 :            :         else result = index;
      93                 :            :     }
      94                 :            :     else
      95                 :            :     {
      96                 :      65770 :         static std::mutex lock;
      97   [ +  +  +  - ]:      65770 :         static std::unordered_map<std::string, std::unordered_map<char32_t,char32_t>> data;
      98                 :      65770 :         auto i = data.find(regenc);
      99         [ +  + ]:      65777 :         if(i == data.end())
     100                 :            :         {
     101         [ +  - ]:         19 :             std::lock_guard lk(lock);
     102                 :         19 :             i = data.find(regenc);
     103         [ +  + ]:         19 :             if(i == data.end())
     104   [ +  -  +  -  :         22 :                 i = data.emplace(regenc, ReadEncoding(regenc + ".txt")).first;
                   -  - ]
     105                 :         19 :         }
     106                 :      65777 :         auto& translation = i->second;
     107                 :            :         /*
     108                 :            :         auto j = std::lower_bound(translation.begin(), translation.end(), index,
     109                 :            :                                   [](const auto& pair, char32_t value) { return pair.first < value; });
     110                 :            :         if(j != translation.end() && j->first == (char32_t)index)
     111                 :            :         {
     112                 :            :             result = j->second;
     113                 :            :             //std::printf("%s: 0x%X = 0x%X\n", regenc.c_str(), index, result);
     114                 :            :         }
     115                 :            :         */
     116                 :      65777 :         auto j = translation.find(index);
     117         [ +  + ]:      65767 :         if(j != translation.end())
     118                 :      63483 :             result = j->second;
     119                 :            :     }
     120         [ +  - ]:     796509 :     std::vector<char32_t> chars{ result };
     121                 :     796085 :     static const unsigned short trans_00_to_1F[] = 
     122                 :            :     {
     123                 :            :         0x0000,0x263A,0x263B,0x2665,
     124                 :            :         0x2666,0x2663,0x2660,0x2022,
     125                 :            :         0x25D8,0x25CB,0x25D9,0x2642,
     126                 :            :         0x2640,0x266A,0x266B,0x263C,
     127                 :            :         0x25BA,0x25C4,0x2195,0x203C,
     128                 :            :         0x00B6,0x00A7,0x25AC,0x21A8,
     129                 :            :         0x2191,0x2193,0x2192,0x2190,
     130                 :            :         0x221F,0x2194,0x25B2,0x25BC,
     131                 :            :     };
     132                 :     796085 :     static const unsigned short trans_7F_to_9F[] =
     133                 :            :     {
     134                 :            :         0x2302, // 7F house
     135                 :            :         // 81..8F follows CP/M plus character set (Amstrad CPC e.g.) but shifted 91..9F
     136                 :            :         // ⎺ ╵ ╶ └ ╷ │ ┌ ├ ╴ ┘ ─ ┴ ┐ ┤ ┬ ┼
     137                 :            :         0x23BA,0x2575,0x2576,0x2514,0x2577,0x2502,0x250C,0x251C,
     138                 :            :         0x2574,0x2518,0x2500,0x2534,0x2510,0x2524,0x252C,0x253C,
     139                 :            :         // 95..9F follows CP/M plus character set (Amstrad CPC e.g.) but shifted 85..8F
     140                 :            :         // ⎽ ╧ ╟ ╚ ╤ ║ ╔ ╠ ╢ ╝ ═ ╩ ╗ ╣ ╦ ╬
     141                 :            :         // Note that 91,92,94,98 are truncated *double* lines, which are not supported by unicode
     142                 :            :         0x25BD,0x2567,0x255F,0x255A,0x2564,0x2551,0x2554,0x2560,
     143                 :            :         0x2562,0x255D,0x2550,0x2569,0x2557,0x2563,0x2566,0x256C
     144                 :            :     };
     145   [ +  +  +  - ]:     796085 :     if(/* result >= 0x00 &&*/ result <= 0x1F) chars.push_back(trans_00_to_1F[result]);
     146   [ +  +  +  - ]:     795047 :     else if(result >= 0x7F && result <= 0x9F) chars.push_back(trans_7F_to_9F[result- 0x7F]);
     147                 :            : 
     148   [ +  +  +  - ]:     796087 :     if(index < 32 && result >= 32) chars.push_back(index);
     149                 :            : 
     150                 :     796087 :     return chars;
     151                 :    1592050 : }
     152                 :            : 
     153                 :            : 
     154                 :        158 : std::string ReadGZ(std::string_view filename)
     155                 :            : {
     156         [ +  - ]:        158 :     std::string result;
     157                 :        158 :     char buf[8192];
     158                 :        158 :     std::size_t pos=0, cap=0;
     159   [ +  -  +  - ]:        158 :     std::ifstream f{ std::string(filename) };
     160         [ +  - ]:        158 :     Deflate([&]() // in char
     161                 :            :             {
     162         [ +  + ]:     388857 :                 if(pos >= cap)
     163                 :            :                 {
     164         [ +  - ]:        171 :                     if(!f) return -1;
     165                 :        171 :                     f.read(buf, sizeof(buf));
     166                 :        172 :                     pos = 0;
     167                 :        172 :                     cap = f.gcount();
     168                 :            :                 }
     169                 :     388858 :                 return (int) (unsigned char) buf[pos++];
     170                 :            :             },
     171                 :    1036795 :             [&](unsigned char c) // out char
     172                 :            :             {
     173                 :    1036795 :                 result += char(c);
     174                 :            :             }/*,
     175                 :            :             [&](std::size_t length, std::size_t offset) // window
     176                 :            :             {
     177                 :            :                 result += std::string( result.end()-(offset+1), result.end()-(offset+1) + length );
     178                 :            :             }*/);
     179                 :        158 :     return result;
     180                 :        158 : }
     181                 :            : 
     182                 :            : /** Data that is read from the PSF file header. */
     183                 :            : struct PSFheader
     184                 :            : {
     185                 :            :     unsigned fontlen;  ///< 256 or 512
     186                 :            :     bool     hastable; ///< If the font contains encoding table
     187                 :            :     unsigned offset;   ///< Starting offset of data
     188                 :            :     bool     utf8;     ///< If the codepoints are represented in UTF-8
     189                 :            :     unsigned charsize; ///< Size in bytes of each glyph
     190                 :            :     unsigned width;    ///< Width
     191                 :            :     unsigned height;   ///< Height
     192                 :            : };
     193                 :        158 : PSFheader ReadPSFheader(const char* data)
     194                 :            : {
     195         [ +  + ]:        158 :     unsigned magic1 = R16r(&data[0]);
     196         [ +  + ]:        158 :     unsigned magic2 = R32r(&data[0]);
     197         [ +  + ]:        158 :     if(magic1 == 0x3604)
     198                 :            :     {
     199                 :         99 :         unsigned char mode  = data[2];
     200                 :         99 :         unsigned char csize = data[3];
     201                 :            :         /* mode & 1: MODE512
     202                 :            :          * mode & 2: MODEHASTAB
     203                 :            :          * mode & 4: MODEHASSEQ
     204                 :            :          */
     205         [ +  + ]:         99 :         return { (mode&1) ? 512u : 256u, // fontlen
     206                 :         99 :                  (mode&6) != 0,          // hastable
     207                 :            :                  4,       // offset
     208                 :            :                  false,   // utf8
     209                 :         99 :                  csize,   // charsize
     210                 :            :                  8,       // width
     211                 :         99 :                  csize }; // height
     212                 :            :     }
     213         [ +  - ]:         59 :     else if(magic2 == 0x72B54A86)
     214                 :            :     {
     215                 :            :         //unsigned      vers  = R32(&data[4]);
     216                 :         59 :         unsigned    hdrsize = R32(&data[8]);
     217                 :         59 :         unsigned      flags = R32(&data[12]);
     218                 :         59 :         unsigned     length = R32(&data[16]);
     219                 :         59 :         unsigned   charsize = R32(&data[20]);
     220                 :         59 :         unsigned     height = R32(&data[24]);
     221                 :         59 :         unsigned      width = R32(&data[28]);
     222                 :         59 :         return { length,          // fontlen
     223                 :         59 :                  (flags&1) != 0,  // hastable
     224                 :            :                  hdrsize,         // offset
     225                 :            :                  true,            // utf8
     226                 :            :                  charsize,        // charsize
     227                 :            :                  width,           // width
     228                 :         59 :                  height };        // height
     229                 :            :     }
     230                 :          0 :     return {};
     231                 :            : }
     232                 :            : 
     233                 :            : std::multimap<std::size_t, char32_t>
     234                 :        158 :     Read_PSFgzEncoding(std::string_view data, const PSFheader& h)
     235                 :            : {
     236                 :        158 :     static constexpr char32_t SS   = 0xFFEFFAEE; // magic
     237                 :        158 :     static constexpr char32_t TERM = 0xFFEFFAEF; // magic
     238                 :            : 
     239         [ +  + ]:        158 :     std::multimap<std::size_t, char32_t> table;
     240         [ +  + ]:        158 :     if(h.hastable)
     241                 :            :     {
     242                 :        140 :         std::size_t pos = h.offset + h.fontlen * h.charsize;
     243                 :     324074 :         auto ReadUC = [&h,&pos,&data]() -> char32_t
     244                 :            :         {
     245         [ +  + ]:     127652 :             if(h.utf8)
     246                 :            :             {
     247         [ +  - ]:      62191 :                 char32_t unichar = (unsigned char) data[pos++];
     248         [ +  - ]:      62191 :                 if(unichar == 0xFE) return SS;
     249         [ +  + ]:      62191 :                 if(unichar == 0xFF) return TERM;
     250         [ +  + ]:      33787 :                 if(unichar >= 0x80)
     251                 :            :                 {
     252                 :            :                     /*
     253                 :            :                      UTF-8 encodings:
     254                 :            :                          7 bits:  0xxxxxxx                              (7)
     255                 :            :                          11 bits: 110xxxxx 10xxxxxx                     (5 + 6)
     256                 :            :                          16 bits: 1110xxxx 10xxxxxx 10xxxxxx            (4 + 6 + 6)
     257                 :            :                          21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx   (3 + 6 + 6 + 6)
     258                 :            :                     */
     259         [ -  + ]:      28153 :                     if((unichar & 0xF8) == 0xF0) unichar &= 0x07;      // 3 bits
     260         [ +  + ]:      28153 :                     else if((unichar & 0xF0) == 0xE0) unichar &= 0x0F; // 4 bits
     261         [ +  - ]:      15825 :                     else if((unichar & 0xE0) == 0xC0) unichar &= 0x1F; // 5 bits
     262                 :          0 :                     else unichar &= 0x7F;                              // 7 bits
     263   [ +  +  +  + ]:      68630 :                     while(pos < data.size() && ((data[pos] & 0xC0) == 0x80))
     264                 :      40477 :                         unichar = (unichar << 6) | (data[pos++] & 0x3F);
     265                 :            :                 }
     266                 :      33787 :                 return unichar;
     267                 :            :             }
     268                 :            :             else
     269                 :            :             {
     270      [ +  +  - ]:      65461 :                 char32_t unichar = (unsigned char) data[pos++];
     271                 :      65461 :                 unichar |= (unsigned char)data[pos++] << 8;
     272      [ +  +  - ]:      65461 :                 switch(unichar)
     273                 :            :                 {
     274                 :            :                     case 0xFFFE: return SS;
     275                 :      30698 :                     case 0xFFFF: return TERM;
     276                 :      34763 :                     default: return unichar;
     277                 :            :                 }
     278                 :            :             }
     279                 :        140 :         };
     280         [ +  + ]:      59271 :         for(unsigned n=0; n<h.fontlen; ++n)
     281                 :            :         {
     282                 :            :             // Grammar:
     283                 :            :             //   UC* (SS UC+)* TERM
     284                 :      59131 :             std::set<char32_t> list;
     285                 :            :             char32_t code;
     286                 :            :             // Single-codepoint alternatives for this glyph
     287                 :            :             //if(pos >= data.size()) std::cout << "EOF " << n << '\n';
     288                 :            :             //else                   std::cout << "Pos " << n << ' ' << std::hex << pos << std::dec << '\t';
     289         [ +  # ]:     127524 :             while(pos < data.size())
     290                 :            :             {
     291                 :     127534 :                 code = ReadUC();
     292         [ +  + ]:     127602 :                 if(code == TERM || code == SS) break;
     293                 :            :                 //std::cout << "Code " << n << ": " << std::hex << code << std::dec << '\n';
     294         [ +  - ]:      68529 :                 list.insert(code);
     295                 :            :             }
     296                 :            :             // Multi-codepoint alternatives for this glyph (for e.g. combining diacritics)
     297   [ +  +  #  + ]:      59063 :             while(pos < data.size() && code == SS)
     298                 :            :             {
     299                 :          0 :                 std::vector<char32_t> seq;
     300                 :          0 :                 for(;;)
     301                 :            :                 {
     302                 :          0 :                     code = ReadUC();
     303         [ #  # ]:          0 :                     if(code == SS || code == TERM) break;
     304         [ #  # ]:          0 :                     seq.push_back(code);
     305                 :            :                 }
     306                 :            :                 //std::cout << "Seq " << n << ":"; for(auto c: seq) { std::cout << ' ' << std::hex << c; } std::cout << std::dec << '\n';
     307                 :            :                 // We ignore all of them if they have more than 1 codepoint.
     308   [ #  #  #  # ]:          0 :                 if(seq.size() == 1) list.insert(seq[0]); //else { print 'seq'; print_r($seq); }
     309                 :          0 :             }
     310                 :            :             /*
     311                 :            :             Line-drawing characters:
     312                 :            :             If this symbol claims simultaneously to accomplish
     313                 :            :             multiple line-styles, call out the bluff.
     314                 :            :             */
     315                 :      59070 :             static const std::u32string_view bluff_list[] =
     316                 :            :             {
     317                 :            :                 // Single-lines
     318                 :            :                 U"─━┄┅┈┉╌╍╴╶╸╺╼╾"sv,
     319                 :            :                 U"│┃┆┇┊┋╎╏╵╷╹╻╽╿"sv,
     320                 :            :                 // Single-line corners
     321                 :            :                 U"┌┍┎┏╭"sv,
     322                 :            :                 U"┐┑┒┓╮"sv,
     323                 :            :                 U"└┕┖┗╰"sv,
     324                 :            :                 U"┘┙┚┛╯"sv,
     325                 :            :                 // Double-line corners
     326                 :            :                 U"╔╔╒┏"sv,
     327                 :            :                 U"╗╕╖┓"sv,
     328                 :            :                 U"╚╘╙┗"sv,
     329                 :            :                 U"╝╛╜┛"sv,
     330                 :            :                 // Single-line junctions
     331                 :            :                 U"┬┭┮┯┰┱┲┳"sv,
     332                 :            :                 U"┴┵┶┷┸┹┺┻"sv,
     333                 :            :                 U"├┝┞┟┠┡┢┣"sv,
     334                 :            :                 U"┤┥┦┧┨┩┪┫"sv,
     335                 :            :                 U"┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋"sv,
     336                 :            :                 // Double-line junctions
     337                 :            :                 U"╦╤╥┳"sv,
     338                 :            :                 U"╩╧╨┻"sv,
     339                 :            :                 U"╠╞╟┣"sv,
     340                 :            :                 U"╣╡╢┫"sv,
     341                 :            :                 U"╬╪╫╋"sv,
     342                 :            :             };
     343         [ +  + ]:      59070 :             if(list.size() > 1)
     344         [ +  + ]:     143646 :                 for(auto s: bluff_list)
     345         [ +  + ]:     136792 :                     if(list.find(s[0]) != list.end())
     346         [ +  + ]:       6271 :                         for(auto i = s.begin()+1; i != s.end(); ++i)
     347                 :       5469 :                             list.erase(*i);
     348         [ +  + ]:     123272 :             for(auto u: list)
     349         [ +  - ]:      64181 :                 table.emplace(n, u);
     350                 :      59131 :         }
     351                 :            :     }
     352                 :        158 :     return table;
     353                 :          0 : }
     354                 :            : 
     355                 :            : std::multimap<std::size_t, char32_t>
     356                 :          1 :     Read_PSFgzEncoding(std::string_view filename)
     357                 :            : {
     358                 :          1 :     std::string data = ReadGZ(filename);
     359         [ +  - ]:          1 :     const PSFheader h = ReadPSFheader(data.data());
     360         [ +  - ]:          1 :     return Read_PSFgzEncoding(data, h);
     361                 :          1 : }
     362                 :            : 
     363                 :        157 : GlyphList Read_PSFgz(std::string_view filename, unsigned /*width*/, unsigned /*height*/, std::string_view assume_encoding)
     364                 :            : {
     365                 :        157 :     std::string data = ReadGZ(filename);
     366         [ +  - ]:        157 :     const PSFheader h = ReadPSFheader(data.data());
     367         [ +  - ]:        157 :     auto table = Read_PSFgzEncoding(data, h);
     368                 :            :     //print_r($table);
     369                 :        157 :     GlyphList result;
     370                 :        157 :     result.height = h.height;
     371         [ +  - ]:        157 :     result.widths.push_back(h.width);
     372                 :        157 :     result.unicode = !table.empty();
     373                 :            : 
     374                 :            :     //std::cout << "assuming " << assume_encoding << ", table is " << (table.empty() ? "empty" : "not empty") << '\n';
     375                 :            : 
     376         [ +  + ]:      61463 :     for(std::size_t n=0; n<h.fontlen; ++n)
     377                 :            :     {
     378                 :            :         // List of unicode characters fulfilled by this glyph
     379         [ +  + ]:      61306 :         std::vector<char32_t> index;
     380         [ +  + ]:      61306 :         if(!table.empty())
     381                 :            :         {
     382                 :      57589 :             auto [begin, end] = table.equal_range(n);
     383                 :            :             // if(begin == end) { std::cout << "Glyph " << n << " empty range?\n"; }
     384   [ +  -  +  + ]:     120570 :             for(; begin != end; ++begin) index.push_back(begin->second);
     385                 :            :         }
     386                 :            : 
     387   [ +  +  +  + ]:      61802 :         if(index.empty() && !assume_encoding.empty())
     388                 :            :         {
     389   [ +  -  +  + ]:      13413 :             for(auto code: BDFtranslateToUnicode(n, ""sv, assume_encoding))
     390         [ +  - ]:      13423 :                 index.push_back(code);
     391                 :            :         }
     392                 :            : 
     393         [ +  + ]:      61813 :         if(index.empty())
     394                 :            :         {
     395                 :            :             //std::cout << "Index on " << n << " is empty?\n";
     396         [ +  - ]:       4391 :             index.push_back( n + 0xF300 );
     397                 :            :         }
     398                 :            : 
     399         [ +  - ]:      61813 :         std::size_t start_offset = result.bitmap.size();
     400         [ +  - ]:      61813 :         result.bitmap.reserve(start_offset + h.charsize);
     401   [ +  -  +  + ]:     137544 :         for(auto c: index) result.glyphs.emplace(c, start_offset);
     402                 :            :         // Read the bitmap
     403                 :      62255 :         std::size_t pos          = h.offset + n * h.charsize;
     404                 :      62255 :         std::size_t bytesperchar = h.charsize / h.height;
     405         [ +  + ]:     979690 :         for(std::size_t m=0; m<h.charsize; m += bytesperchar)
     406                 :            :         {
     407                 :            :             std::uint_fast64_t w = 0;
     408                 :            :             // Read big-endian
     409         [ +  + ]:    2375522 :             for(std::size_t a=0; a<bytesperchar; ++a)
     410                 :    1456576 :                 w |= ((std::uint_fast64_t)(unsigned char)data[pos + m + bytesperchar - a - 1]) << (a*8);
     411         [ +  + ]:     918946 :             if(h.width % 8)
     412                 :     344341 :                 w >>= (8 - h.width%8);
     413                 :            :             // Write little-endian
     414         [ +  + ]:    2356570 :             for(std::size_t a=0; a<bytesperchar; ++a)
     415         [ +  - ]:    1439135 :                 result.bitmap.push_back( w >> (a*8) );
     416                 :            :         }
     417                 :      61306 :     }
     418                 :        157 :     return result;
     419                 :        157 : }
     420                 :            : 
     421                 :         83 : GlyphList Read_BDF(std::string_view filename, unsigned width, unsigned /*height*/, std::string_view guess_encoding)
     422                 :            : {
     423                 :         83 :     std::vector<char32_t> chno = {0};
     424         [ +  - ]:         83 :     std::vector<std::size_t> data;
     425                 :         83 :     unsigned mode            = 0;
     426                 :         83 :     unsigned matrix_row_size = (8 + 7) >> 3; // 1 byte
     427                 :         83 :     int ascent = 0, descent = 0, shiftbits = 0, beforebox = 0, afterbox = 0;
     428         [ +  - ]:         83 :     std::string registry, encoding(guess_encoding);
     429                 :         83 :     unsigned fontwidth = width, fontheight = 1, avg_width = 0;
     430                 :         83 :     GlyphList result;
     431                 :         83 :     result.unicode = false;
     432                 :            : 
     433                 :            :     #define r(str) rmatch<__LINE__>(str##sv, mat, line)
     434                 :            : 
     435                 :            :     // Ignore width for variable-width fonts
     436   [ +  -  +  -  :         83 :     bool ignore_width = std::regex_search(std::string(filename), "monak1[246]|mona6x12|mona7x14"_r);
                   +  - ]
     437                 :            : 
     438   [ +  -  +  - ]:         83 :     std::ifstream f{ std::string(filename) };
     439                 :         83 :     auto space = " \t\r"sv;
     440   [ +  -  +  + ]:   17700216 :     for(std::string line; std::getline(f,line), f; )
     441                 :            :     {
     442                 :            :         // Trim
     443                 :   17849587 :         std::size_t begin = line.find_first_not_of(space);
     444         [ +  + ]:   17814920 :         if(begin == line.npos) continue;
     445                 :   17261956 :         std::size_t end   = line.find_last_not_of(space)+1;
     446   [ +  -  +  +  :   17261956 :         if(begin > 0 || end < line.size()) line = line.substr(begin, end-begin);
                   +  - ]
     447                 :            :         // Identify
     448         [ +  - ]:   17261944 :         std::smatch mat;
     449   [ +  -  +  +  :   17261944 :         /**/ if(r("FONT_ASCENT ([0-9]+)")) ascent = std::stoi(mat[1]);
             +  -  +  - ]
     450   [ +  -  +  +  :   16604026 :         else if(r("FONT_DESCENT ([0-9]+)")) descent = std::stoi(mat[1]);
             +  -  +  - ]
     451   [ +  -  +  +  :   16844454 :         else if(r("AVERAGE_WIDTH ([0-9]+)")) avg_width = std::stoi(mat[1]);
             +  -  +  - ]
     452   [ +  -  +  +  :   16749353 :         else if(r("FONT -.*-([^-]*)-([^-]*)")) { registry = mat[1]; encoding = mat[2]; result.unicode = true; }
             +  -  +  - ]
     453   [ +  -  +  +  :   17504483 :         else if(r("ENCODING ([0-9]+)")) { chno = BDFtranslateToUnicode(std::stoi(mat[1]), registry, encoding); }
          +  -  +  -  +  
                      - ]
     454   [ +  -  -  +  :   16140179 :         else if(r("CHARSET_REGISTRY \"?([^\"]*)")) registry = mat[1];
                   -  - ]
     455   [ +  -  -  +  :   15991000 :         else if(r("CHARSET_ENCODING \"?([^\"]*)")) encoding = mat[1];
                   -  - ]
     456   [ +  -  +  + ]:   16037036 :         else if(r("FONTBOUNDINGBOX ([0-9]+) ([0-9]+).*"))
     457                 :            :         {
     458                 :            :             //fontwidth = std::stoi(mat[1]);
     459   [ +  -  +  - ]:         78 :             fontheight = std::stoi(mat[2]);
     460                 :            :             //matrix_row_size = (fontwidth + 7) >> 3; // 1 byte
     461                 :         78 :             result.height = fontheight;
     462                 :            :         }
     463   [ +  -  +  + ]:   15967477 :         else if(r("DWIDTH ([0-9]+) ([0-9]+).*"))
     464                 :            :         {
     465   [ +  -  +  - ]:     787161 :             fontwidth = std::stoi(mat[1]);
     466                 :     788244 :             matrix_row_size = (fontwidth + 7) >> 3; // 1 byte
     467         [ +  + ]:     788244 :             if(std::find(result.widths.begin(), result.widths.end(), fontwidth) == result.widths.end())
     468         [ +  - ]:        164 :                 result.widths.push_back(fontwidth);
     469                 :            :         }
     470   [ +  -  +  + ]:   15396975 :         else if(r("BBX (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+).*"))
     471                 :            :         {
     472   [ +  -  +  - ]:     787222 :             int x = std::stoi(mat[1]);
     473   [ +  -  +  - ]:     788120 :             int y = std::stoi(mat[2]);
     474   [ +  -  +  - ]:     788371 :             int xo = std::stoi(mat[3]);
     475   [ +  -  +  - ]:     788456 :             int yo = std::stoi(mat[4]);
     476                 :     788356 :             shiftbits = (matrix_row_size - ((x + 7) >> 3) ) * 8 - xo;
     477                 :     788356 :             beforebox = (ascent - yo - y) * matrix_row_size;
     478                 :     788356 :             afterbox  = (descent + yo) * matrix_row_size;
     479   [ +  +  +  - ]:     788356 :             if(width == 9 && filename.find("gb16st") != filename.npos)
     480                 :            :             {
     481                 :            :                 // The autodetected value seems bad, manually override
     482                 :            :                 beforebox = 1;
     483                 :            :                 afterbox = 0;
     484                 :            :             }
     485   [ +  +  +  + ]:     788356 :             if(width == 10 && filename.find("gb24st") != filename.npos)
     486                 :            :             {
     487                 :            :                 // The autodetected value seems bad, manually override
     488                 :            :                 beforebox = 4;
     489                 :            :                 afterbox = 0;
     490                 :            :             }
     491                 :            :         }
     492         [ +  + ]:   14367227 :         else if(line == "BITMAP")
     493                 :            :             mode = 1;
     494         [ +  + ]:   13765201 :         else if(line == "ENDCHAR")
     495                 :            :         {
     496                 :     786793 :             mode = 0;
     497   [ +  +  +  + ]:     786793 :             if(fontwidth == width || ignore_width)
     498                 :            :             {
     499         [ -  + ]:     409908 :                 std::vector<std::uint_fast64_t> map;
     500         [ -  + ]:     409908 :                 if(beforebox < 0)
     501                 :            :                 {
     502         [ #  # ]:          0 :                     if(data.size() >= std::size_t(-beforebox))
     503                 :          0 :                         data.erase(data.begin(), data.begin() + (-beforebox));
     504                 :            :                     else
     505         [ #  # ]:          0 :                         data.clear();
     506                 :            :                     beforebox = 0;
     507                 :            :                 }
     508         [ -  + ]:     409908 :                 if(afterbox < 0)
     509                 :            :                 {
     510         [ #  # ]:          0 :                     if(data.size() >= std::size_t(-afterbox))
     511                 :          0 :                         data.erase(data.end() - (-afterbox), data.end());
     512                 :            :                     else
     513         [ #  # ]:          0 :                         data.clear();
     514                 :            :                     afterbox = 0;
     515                 :            :                 }
     516         [ +  + ]:     409908 :                 if(beforebox > 0)
     517         [ +  - ]:        901 :                     map.resize(map.size() + beforebox);
     518         [ +  - ]:     409908 :                 map.insert(map.end(), data.begin(), data.end());
     519         [ +  + ]:     411193 :                 if(afterbox > 0)
     520         [ +  - ]:      36140 :                     map.resize(map.size() + afterbox);
     521         [ -  + ]:     411192 :                 if(map.size() < fontheight)
     522         [ #  # ]:          0 :                     map.resize(fontheight);
     523                 :            : 
     524                 :     411192 :                 unsigned bytes  = (width + 7) >> 3;
     525                 :     411192 :                 unsigned sbytes = (fontwidth + 7) >> 3;
     526                 :            : 
     527         [ +  - ]:     411192 :                 std::size_t start_offset = result.bitmap.size();
     528         [ +  - ]:     411192 :                 result.bitmap.reserve(start_offset + fontheight * bytes);
     529                 :            : 
     530   [ +  -  +  + ]:     823068 :                 for(auto ch: chno) { result.glyphs.emplace(ch, start_offset); }
     531                 :            : 
     532         [ +  + ]:    6608172 :                 for(unsigned y=0; y<fontheight; ++y)
     533                 :            :                 {
     534                 :    6196747 :                     auto m = map[y];
     535         [ +  + ]:   18967608 :                     for(unsigned b=0; b<bytes; ++b)
     536         [ +  - ]:   12770818 :                         result.bitmap.push_back( ((m >> ((sbytes*8)-fontwidth)) >> (b*8)) & 0xFF );
     537                 :            :                 }
     538         [ +  + ]:     823117 :                 for(auto& ch: chno) ++ch;
     539                 :     411412 :             }
     540         [ +  # ]:   17787211 :             data.clear();
     541                 :            :         }
     542         [ +  + ]:   12978408 :         else if(mode)
     543                 :            :         {
     544         [ +  - ]:   11540637 :             std::uint_fast64_t v = std::stoll(line, nullptr, 16);
     545         [ +  + ]:   11818602 :             if(shiftbits > 0)
     546                 :         48 :                 v <<= shiftbits;
     547                 :            :             else
     548                 :   11818554 :                 v >>= -shiftbits;
     549         [ +  - ]:   11818602 :             data.push_back(v);
     550                 :            :         }
     551                 :            :         #undef r
     552                 :   17147169 :     }
     553                 :            : 
     554                 :         83 :     std::sort(result.widths.begin(), result.widths.end());
     555         [ +  + ]:         83 :     if(ignore_width)
     556                 :            :     {
     557         [ +  - ]:          9 :         if(avg_width)
     558         [ +  - ]:          9 :             result.widths.assign(1, (avg_width + 5) / 10);
     559                 :            :         else
     560                 :          0 :             result.widths.erase(result.widths.begin(), std::prev(result.widths.end()));
     561                 :            :     }
     562                 :        166 :     return result;
     563                 :         83 : }
     564                 :            : 
     565                 :          4 : static const std::regex& GetHeightPat()
     566                 :            : {
     567   [ +  +  +  -  :          4 :     static const std::regex heightpat(".*[^0-9]([0-9]+)[^0-9]*", Ropt);
                   +  - ]
     568                 :          4 :     return heightpat;
     569                 :            : }
     570                 :            : 
     571                 :          7 : GlyphList Read_Inc(std::string_view filename, unsigned width, unsigned height, std::string_view guess_encoding)
     572                 :            : {
     573         [ +  + ]:          7 :     std::smatch mat;
     574         [ +  + ]:          7 :     if(!height)
     575                 :            :     {
     576         [ +  - ]:          3 :         std::string tmp(filename);
     577   [ +  -  +  -  :          3 :         if(std::regex_match(tmp, mat, GetHeightPat())) { height = std::stoi(mat[1]); }
          +  -  +  -  +  
                      - ]
     578                 :          3 :     }
     579                 :            : 
     580                 :          7 :     GlyphList result;
     581                 :          7 :     result.height = height;
     582         [ +  - ]:          7 :     result.widths.push_back(8);
     583                 :          7 :     result.unicode = false;
     584                 :            : 
     585         [ +  + ]:          7 :     if(width != 8) return result;
     586   [ +  +  +  +  :          4 :     static const std::regex hexpat("0x[0-9A-Fa-f]+", Ropt);
                   +  - ]
     587                 :            : 
     588   [ +  -  +  - ]:          4 :     std::ifstream f{ std::string(filename) };
     589                 :          4 :     unsigned glyph=0, row=0;
     590   [ +  -  +  + ]:        521 :     for(std::string line; std::getline(f,line), f; )
     591   [ +  -  +  + ]:      12845 :         for(auto b = line.cbegin(), e = line.cend(); std::regex_search(b,e, mat, hexpat); b = mat[0].second)
     592                 :            :         {
     593   [ +  -  +  - ]:      12474 :             int val = std::stoi(mat[0], nullptr, 16); // accepts 0x prefix
     594         [ +  + ]:      12311 :             if(!row)
     595   [ +  -  +  + ]:       2181 :                 for(auto unichar: BDFtranslateToUnicode(glyph, {}, guess_encoding))
     596         [ +  - ]:       2186 :                     result.glyphs.emplace(unichar, result.bitmap.size());
     597         [ +  - ]:      12313 :             result.bitmap.push_back(val);
     598         [ +  + ]:      12325 :             if(++row >= height) { row=0; ++glyph; }
     599                 :          0 :         }
     600                 :          4 :     return result;
     601                 :         11 : }
     602                 :            : 
     603                 :          2 : GlyphList Read_ASM(std::string_view filename, unsigned width, unsigned height, std::string_view guess_encoding)
     604                 :            : {
     605         [ +  + ]:          2 :     std::smatch mat;
     606         [ +  + ]:          2 :     if(!height)
     607                 :            :     {
     608         [ +  - ]:          1 :         std::string tmp(filename);
     609   [ +  -  +  -  :          1 :         if(std::regex_match(tmp, mat, GetHeightPat())) { height = std::stoi(mat[1]); }
          +  -  +  -  +  
                      - ]
     610                 :          1 :     }
     611                 :            : 
     612                 :          2 :     GlyphList result;
     613                 :          2 :     result.height = height;
     614         [ +  - ]:          2 :     result.widths.push_back(8);
     615                 :          2 :     result.unicode = false;
     616                 :            : 
     617         [ +  - ]:          2 :     if(width != 8) return result;
     618   [ +  +  +  -  :          2 :     static const std::regex dbpat("[ \t]+[dD][bB][ \t]+([^;]+).*\r?\n?", Ropt);
                   +  - ]
     619   [ +  +  +  -  :          2 :     static const std::regex hexpat("[0-9A-Fa-f]+", Ropt);
                   +  - ]
     620                 :            : 
     621                 :          2 :     unsigned glyph=0, row=0;
     622   [ +  -  +  - ]:          2 :     std::ifstream f{ std::string(filename) };
     623   [ +  -  +  + ]:        556 :     for(std::string line; std::getline(f,line), f; )
     624   [ +  -  +  + ]:        552 :         if(std::regex_match(line, mat, dbpat))
     625                 :            :         {
     626   [ +  -  +  + ]:       7680 :             for(auto b = mat[1].first, e = mat[1].second; std::regex_search(b,e, mat, hexpat); b = mat[0].second)
     627                 :            :             {
     628   [ +  -  +  - ]:       7168 :                 int val = std::stoi(std::string(mat[0].first, mat[0].second), nullptr, 16);
     629         [ +  + ]:       7168 :                 if(!row)
     630   [ +  -  +  + ]:       1154 :                     for(auto unichar: BDFtranslateToUnicode(glyph, {}, guess_encoding))
     631         [ +  - ]:       1154 :                         result.glyphs.emplace(unichar, result.bitmap.size());
     632         [ +  - ]:       7168 :                 result.bitmap.push_back(val);
     633         [ +  + ]:       7168 :                 if(++row >= height) { row=0; ++glyph; }
     634                 :            :             }
     635                 :          0 :         }
     636                 :          2 :     return result;
     637                 :          4 : }
     638                 :            : 
     639                 :        249 : GlyphList::GlyphList()
     640                 :            : {
     641                 :        249 : }
     642                 :            : 
     643                 :        246 : GlyphList Read_Font(std::filesystem::path filename, unsigned width, unsigned height,
     644                 :            :                     bool find, std::string_view guess_encoding)
     645                 :            : {
     646         [ +  + ]:        246 :     if(find)
     647                 :            :     {
     648   [ +  -  +  -  :        225 :         auto [location, status] = FindShareFile(std::filesystem::path("fonts") / "files" / filename);
          +  -  +  -  +  
                      - ]
     649         [ +  - ]:         45 :         filename = location;
     650                 :         45 :     }
     651                 :            : 
     652                 :        492 :     std::string ext = filename.extension();
     653   [ +  +  +  -  :        253 :     if(ext == ".inc"sv) return Read_Inc(std::string(filename), width, height, guess_encoding);
                   +  - ]
     654   [ +  +  +  -  :        240 :     if(ext == ".asm"sv) return Read_ASM(std::string(filename), width, height, guess_encoding);
                   +  - ]
     655   [ +  +  +  -  :        319 :     if(ext == ".bdf"sv) return Read_BDF(std::string(filename), width, height, guess_encoding);
                   +  - ]
     656   [ +  -  +  - ]:        314 :     return Read_PSFgz(std::string(filename), width, height, guess_encoding);
     657                 :        246 : }
     658                 :            : 
     659                 :            : #ifdef RUN_TESTS
     660                 :            : // Do tests for endian.hh . Use volatile to avoid inline-switch-case thwarting coverage tests.
     661                 :          4 : TEST(endian, bswap16) { EXPECT_EQ(BSwap16(0x1234u), 0x3412u); }
     662                 :          4 : TEST(endian, bswap32) { EXPECT_EQ(BSwap32(0x12345678u), 0x78563412u); }
     663                 :          4 : TEST(endian, bswap64) { EXPECT_EQ(BSwap64(0x12345678ABCDEF01ull), 0x01EFCDAB78563412ull); }
     664                 :          4 : TEST(endian, R8)      { EXPECT_EQ(R8("K"), 0x4Bu); }
     665                 :          4 : TEST(endian, R16)     { EXPECT_EQ(R16(" A"), 0x4120u); }
     666                 :          4 : TEST(endian, R24)     { EXPECT_EQ(R24(" AB"), 0x424120u); }
     667                 :          4 : TEST(endian, R32)     { EXPECT_EQ(R32(" ABC"), 0x43424120u); }
     668                 :          4 : TEST(endian, R64)     { EXPECT_EQ(R64(" ABC0123"), 0x3332313043424120ull); }
     669                 :          4 : TEST(endian, R16r)     { EXPECT_EQ(R16r(" A"), 0x2041u); }
     670                 :          4 : TEST(endian, R24r)     { EXPECT_EQ(R24r(" AB"), 0x204142u); }
     671                 :          4 : TEST(endian, R32r)     { EXPECT_EQ(R32r(" ABC"), 0x20414243u); }
     672                 :          4 : TEST(endian, R64r)     { EXPECT_EQ(R64r(" ABC0123"), 0x2041424330313233ull); }
     673                 :          3 : TEST(endian, Rn)       { char data[] = " ABC0123";
     674                 :          1 :                          volatile unsigned n=0;
     675                 :          1 :                          n=n+1; EXPECT_EQ(Rn(data, n), 0x20u);
     676                 :          1 :                          n=n+1; EXPECT_EQ(Rn(data, n), 0x4120u);
     677                 :          1 :                          n=n+1; EXPECT_EQ(Rn(data, n), 0x424120u);
     678                 :          1 :                          n=n+1; EXPECT_EQ(Rn(data, n), 0x43424120u);
     679                 :          1 :                          n=n+1; EXPECT_EQ(Rn(data, n), 0x3043424120u);
     680                 :          1 :                          n=n+1; EXPECT_EQ(Rn(data, n), 0x313043424120u);
     681                 :          1 :                          n=n+1; EXPECT_EQ(Rn(data, n), 0x32313043424120u);
     682                 :          2 :                          n=n+1; EXPECT_EQ(Rn(data, n), 0x3332313043424120u); }
     683                 :          3 : TEST(endian, Wn)       { char data[] = " ABC0123";
     684                 :          1 :                          volatile unsigned n=0;
     685                 :          2 :                          { char buf[16]{}; n=n+1; Wn(buf, 0x20u, n); EXPECT_EQ(std::string(buf,n), std::string(data,n)); }
     686                 :          2 :                          { char buf[16]{}; n=n+1; Wn(buf, 0x4120u, n); EXPECT_EQ(std::string(buf,n), std::string(data,n)); }
     687                 :          2 :                          { char buf[16]{}; n=n+1; Wn(buf, 0x424120u, n); EXPECT_EQ(std::string(buf,n), std::string(data,n)); }
     688                 :          2 :                          { char buf[16]{}; n=n+1; Wn(buf, 0x43424120u, n); EXPECT_EQ(std::string(buf,n), std::string(data,n)); }
     689                 :          2 :                          { char buf[16]{}; n=n+1; Wn(buf, 0x3043424120u, n); EXPECT_EQ(std::string(buf,n), std::string(data,n)); }
     690                 :          2 :                          { char buf[16]{}; n=n+1; Wn(buf, 0x313043424120u, n); EXPECT_EQ(std::string(buf,n), std::string(data,n)); }
     691                 :          2 :                          { char buf[16]{}; n=n+1; Wn(buf, 0x32313043424120u, n); EXPECT_EQ(std::string(buf,n), std::string(data,n)); }
     692                 :          2 :                          { char buf[16]{}; n=n+1; Wn(buf, 0x3332313043424120u, n); EXPECT_EQ(std::string(buf,n), std::string(data,n)); }
     693                 :          1 :                        }
     694                 :          3 : TEST(read_font, coverage)
     695                 :            : {
     696                 :          1 :     Read_PSFgzEncoding("share/fonts/files/iso01.f08.psf.gz"sv);
     697                 :          1 :     Read_BDF("share/fonts/files/share/fonts/files/4x5.bdf"sv, 0,0, "iso10646-1"sv);
     698                 :          1 :     Read_BDF("share/fonts/files/share/fonts/files/4x5.bdf"sv, 4,0, "ascii"sv);
     699                 :          1 :     Read_ASM("test/860-8x14.asm"sv, 8, 14, "cp860"sv);        // Give height
     700         [ +  - ]:          1 :     Read_Font("test/860-8x14.asm"sv, 8, 0, false, "cp860"sv); // Test height guessing
     701                 :          1 : }
     702                 :            : #endif

Generated by: LCOV version 1.16