Branch data Line data Source code
1 : : /** @file rendering/fonts/read_fonts.cc
2 : : * @brief Loads the list of available fonts and the list of characters in each. Used by FontPlan.
3 : : */
4 : : #include <cstdio>
5 : :
6 : : #include <future>
7 : : #include <charconv>
8 : : #include <fstream>
9 : : #include <sstream>
10 : : #include <regex>
11 : :
12 : : #include "read_fonts.hh"
13 : : #include "read_font.hh"
14 : : #include "endian.hh"
15 : : #include "share.hh"
16 : :
17 : : using namespace std::literals;
18 : :
19 : : static constexpr auto Ropt = std::regex_constants::ECMAScript | std::regex_constants::optimize;
20 : :
21 : 189 : static std::string GuessEncoding(std::string_view name)
22 : : {
23 : 189 : static const unsigned char latin[] = {0, 1, 2, 3, 4, 9, 10, 13, 14, 15, 16 };
24 : 189 : unsigned iso = 0;
25 [ + - + + ]: 189 : if(name.size() >= 4 && name.compare(name.size()-4,4, ".inc"sv) == 0)
26 : : {
27 [ + + ]: 3 : if(name == "8x12.inc"sv) return "ibm-cp850";
28 : 2 : return "ibm-cp437";
29 : : }
30 [ + + ]: 186 : if(name == "4x5.bdf"sv)
31 : 1 : return "xorg-4x5";
32 [ + + ]: 185 : if(name.compare(0,3, "lat"sv) == 0)
33 : : {
34 : 21 : unsigned value = 0;
35 : 21 : std::from_chars(name.data()+3, name.data()+name.size(), value);
36 [ + - ]: 21 : if(value > 0 && value < std::size(latin)) iso = latin[value];
37 : : }
38 [ + + ]: 164 : else if(name.compare(0,3, "iso"sv) == 0)
39 : : {
40 : 18 : std::from_chars(name.data()+3, name.data()+name.size(), iso);
41 : : }
42 [ + + ]: 185 : if(iso)
43 [ + - ]: 78 : return "iso-8859-" + std::to_string(iso);
44 : 189 : return {};
45 : : }
46 : :
47 : 2 : static auto FontPath()
48 : : {
49 [ + - + - : 8 : return FindShareFile(std::filesystem::path("fonts") / std::filesystem::path("files"));
+ - ]
50 : : }
51 : :
52 : 1 : void ReadFonts(std::ostream& out)
53 : : {
54 [ + - ]: 1 : std::vector<std::future<std::string>> tasks;
55 : :
56 [ + - ]: 1 : auto [fn, status] = FontPath();
57 [ + - + - : 104 : for(auto p: std::filesystem::directory_iterator(fn))
+ - + - +
- + + -
+ ]
58 [ + - + - ]: 100 : if(!p.is_directory())
59 : : {
60 [ + - + - ]: 300 : std::string guess_enc = GuessEncoding(std::string(p.path().filename()));
61 [ + - ]: 100 : std::fprintf(stderr, "Reading %s ...\n", p.path().c_str());
62 : :
63 [ + - + - : 200 : tasks.emplace_back(std::async(std::launch::async, [=]
+ - + - ]
64 : : {
65 [ + - ]: 100 : auto data = Read_Font(p.path(), 0,0, false, guess_enc);
66 : :
67 [ + - ]: 100 : std::ostringstream temp;
68 [ + - + - : 100 : temp << "FONT "sv << p.path().filename() << '\n';
+ - + - ]
69 [ + - + + ]: 200 : for(auto w: std::vector<unsigned>(data.widths)) // make copy
70 : : {
71 [ + - + - : 100 : temp << "SIZE "sv << w << ' ' << data.height << '\n';
+ - + - +
- ]
72 [ + + ]: 100 : if(!data.unicode)
73 : : {
74 [ + - + - : 11 : temp << "GUESSED_ENCODING \""sv << guess_enc << "\"\n"sv;
+ - ]
75 : : }
76 : :
77 [ + - + - ]: 100 : data = Read_Font(p.path(), w, data.height, false, guess_enc);
78 : :
79 [ + - ]: 100 : std::vector<bool> bitmap(65536);
80 [ + + ]: 286553 : for(auto [ch, ofs]: data.glyphs)
81 : : {
82 [ + - - + ]: 286453 : while(bitmap.size() <= ch) bitmap.resize(bitmap.size() + 65536);
83 : 287491 : bitmap[ch] = true;
84 : : }
85 : :
86 : 5941 : auto PrintRange = [&](char32_t begin, char32_t length)
87 : : {
88 [ + + ]: 5841 : if(length == 1)
89 : : {
90 : 1770 : char buf[64] = " CHAR ";
91 : 1770 : auto [p, erc] = std::to_chars(buf+7, buf+sizeof(buf)-1, begin, 16);
92 : 1770 : *p++ = '\n';
93 : 1770 : temp << std::string_view(buf, p-buf);
94 : : }
95 : : else
96 : : {
97 : 4071 : char buf[80] = " CHARS ";
98 : 4071 : auto [p, erc] = std::to_chars(buf+8, buf+sizeof(buf)/2, begin, 16);
99 : 4071 : p[0] = ' '; p[1] = '-'; p[2] = ' ';
100 : 4071 : auto [p2, erc2] = std::to_chars(p+3, buf+sizeof(buf)-1, begin+length-1, 16);
101 : 4071 : *p2++ = '\n';
102 : 4071 : temp << std::string_view(buf, p2-buf);
103 : : }
104 : 5941 : };
105 : :
106 : : /* Express the coverage bitmap in a compact form. */
107 [ + + ]: 3214964 : for(std::size_t begin=0; begin<bitmap.size(); ++begin)
108 : : {
109 [ + + ]: 3214864 : if(!bitmap[begin]) continue;
110 : : std::size_t length = 1;
111 [ + - + + ]: 195612 : while(begin+length < bitmap.size() && bitmap[begin+length]) ++length;
112 : : #if 1
113 : 659315 : std::uint_fast64_t bits=0, run=1, popcount=0, toggles=0, wbits = 64;
114 [ + + ]: 659315 : for(std::size_t bit=0; bit<64; ++bit)
115 [ + + + + ]: 649120 : if(begin+bit < bitmap.size() && bitmap[begin+bit])
116 [ + + ]: 362832 : { if(!run) {++toggles;} bits |= 1ull << bit; ++run; ++popcount; }
117 : : else
118 [ + + ]: 286290 : { if(run) {++toggles;} run = 0; }
119 [ + + ]: 10195 : if(run >= 8)
120 : : {
121 : 1898 : bits = (bits << run) >> run;
122 : 1898 : wbits -= run;
123 : : }
124 : :
125 [ + + ]: 10195 : if(toggles >= 3 && !run)
126 : : {
127 : 4354 : char buf[128] = " MAP ";
128 : : /* Put first character index in hex */
129 : 4354 : auto [p, erc] = std::to_chars(buf+6, buf+sizeof(buf)/2, begin, 16);
130 : 4354 : p[0] = ' ';
131 : : /* Put coverage bitmap (64-bit integer) in binary */
132 : 4354 : auto [p2, erc2] = std::to_chars(p+1, buf+sizeof(buf)-1, bits, 2);
133 : 4354 : *p2++ = '\n';
134 [ + - ]: 4354 : temp << std::string_view(buf, p2-buf);
135 : 4354 : begin += wbits-1;
136 : : }
137 : : else
138 : : #endif
139 : : {
140 [ + - ]: 5841 : PrintRange(begin, length);
141 : 5841 : begin += length-1;
142 : : }
143 : : }
144 : 100 : }
145 : :
146 [ + - ]: 100 : std::fprintf(stderr, ".");
147 : :
148 [ + - ]: 100 : temp << '\n';
149 [ + - ]: 200 : return temp.str();
150 : 100 : }));
151 [ + - - + : 201 : }
+ - - - ]
152 [ + - + + ]: 101 : for(auto& t: tasks) out << t.get();
153 [ + - ]: 1 : std::fprintf(stderr, "Fonts loaded.");
154 : 1 : }
155 : :
156 : : #pragma GCC push_options
157 : : #pragma GCC optimize ("Ofast")
158 : : template<unsigned l>
159 : 54581 : static bool rmatch(std::string_view str, std::smatch& mat, const std::string& line)
160 : : {
161 [ + + + - : 54581 : static std::regex r(std::string(str), Ropt);
+ - + - ]
162 : 54581 : return std::regex_match(line, mat, r);
163 : : }
164 : : #pragma GCC pop_options
165 : :
166 : 1 : FontsInfo ParseFontsList(std::istream& f)
167 : : {
168 : 1 : FontsInfo result;
169 : :
170 : : #define r(str) rmatch<__LINE__>(str##sv, mat, line)
171 : :
172 : 1 : std::string filename;
173 : 1 : unsigned x=0, y=0;
174 : 1 : std::smatch mat;
175 : :
176 : 1 : std::vector<bool> bitmap;
177 : 1 : std::string guessed_encoding;
178 : 202 : auto Flush = [&]()
179 : : {
180 [ + + ]: 201 : if(!bitmap.empty())
181 : : {
182 [ + + + - ]: 200 : if(guessed_encoding.empty()) { guessed_encoding = GuessEncoding(filename); }
183 [ + - + - ]: 100 : result[ std::pair(filename, std::pair(x,y)) ] = std::pair(std::move(guessed_encoding), std::move(bitmap));
184 : 100 : bitmap.clear();
185 : : }
186 : 201 : };
187 : 287596 : auto Set = [&](std::size_t n)
188 : : {
189 [ + + + + ]: 290813 : while(bitmap.size() <= n) bitmap.resize(bitmap.size() + (n-bitmap.size() < 16384 ? 512 : 32768));
190 : 287595 : bitmap[n] = true;
191 : 287596 : };
192 : :
193 [ + - + + ]: 10507 : for(std::string line; std::getline(f,line), f; )
194 : : {
195 [ + - + + ]: 10506 : if(r("FONT[ \t]+\"([^\"]+)\""))
196 : : {
197 [ + - ]: 100 : Flush();
198 [ + - ]: 100 : filename = mat[1];
199 : : }
200 [ + - + + ]: 10406 : else if(r("GUESSED_ENCODING[ \t]+\"([^\"]+)\""))
201 : : {
202 [ + - ]: 11 : guessed_encoding = mat[1];
203 : : }
204 [ + - + + ]: 10395 : else if(r("[ \t]*SIZE[ \t]+([0-9]+)[ \t]+([0-9]+)"))
205 : : {
206 [ + - ]: 100 : Flush();
207 [ + - + - ]: 100 : x = std::stoi(mat[1]);
208 [ + - + - ]: 100 : y = std::stoi(mat[2]);
209 : : }
210 [ + - + + ]: 10295 : else if(r("[ \t]*CHAR[ \t]+([0-9a-fA-F]+)"))
211 : : {
212 [ + - + - ]: 1770 : unsigned a = std::stoi(mat[1], nullptr, 16);
213 [ + - ]: 1770 : Set(a);
214 : : }
215 [ + - + + ]: 8525 : else if(r("[ \t]*CHARS[ \t]+([0-9a-fA-F]+)[ \t]*-[ \t]*([0-9a-fA-F]+)"))
216 : : {
217 [ + - + - : 4071 : unsigned a = std::stoi(mat[1], nullptr, 16), b = std::stoi(mat[2], nullptr, 16);
+ - + - ]
218 [ + - + + ]: 182471 : while(a <= b) Set(a++);
219 : : }
220 [ + - + + ]: 4454 : else if(r("[ \t]*MAP[ \t]+([0-9a-fA-F]+)[ \t]+([01]+)"))
221 : : {
222 [ + - + - ]: 4354 : unsigned a = std::stoi(mat[1], nullptr, 16);
223 [ + + ]: 241343 : for(long n=0; n<mat[2].length(); ++n)
224 [ + + ]: 236989 : if(mat[2].first[n] == '1')
225 [ + - ]: 107425 : Set(a + mat[2].length() - (n+1));
226 : : }
227 : 0 : }
228 : :
229 : : #undef r
230 [ + - ]: 1 : Flush();
231 : 1 : return result;
232 : 1 : }
233 : :
234 : 1 : FontsInfo ReadFontsList()
235 : : {
236 [ + - + - ]: 2 : auto [path, status] = FindCacheFile("fonts-list.dat", true);
237 [ + - ]: 1 : auto [fn, status2] = FontPath();
238 [ - + ]: 2 : if(!std::filesystem::exists(status)
239 [ # # # # ]: 0 : || !std::filesystem::file_size(path)
240 [ # # # # ]: 0 : || (std::filesystem::exists(fn)
241 [ # # # # : 0 : && std::filesystem::last_write_time(fn) > std::filesystem::last_write_time(path)))
# # ]
242 : : {
243 [ + - ]: 1 : std::fprintf(stderr, "Building fonts cache...\n");
244 [ + - ]: 1 : std::ofstream file(path);
245 [ + - ]: 1 : ReadFonts(file);
246 : 1 : }
247 [ + - ]: 1 : std::ifstream f(path);
248 [ + - ]: 1 : return ParseFontsList(f);
249 : 3 : }
|