Branch data Line data Source code
1 : : #include <map>
2 : : #include <regex>
3 : : #include <string>
4 : : #include <ranges>
5 : : #include <fstream>
6 : : #include <ostream>
7 : : #include <functional>
8 : : #include <unordered_set>
9 : : /** @file rendering/fonts/make_similarities.cc
10 : : * @brief Builds a similarity map between glyphs. Used internally by FontPlan.
11 : : */
12 : :
13 : : #include "share.hh"
14 : : #include "ctype.hh"
15 : : #include "make_similarities.hh"
16 : :
17 : : using namespace std::literals;
18 : :
19 : : static constexpr auto Ropt = std::regex_constants::ECMAScript | std::regex_constants::optimize;
20 : :
21 : 2 : static auto UnicodeDataFileName()
22 : : {
23 [ + - ]: 4 : return FindShareFile("unicode/UnicodeData.txt", {"/usr/local/share", "/usr/share"});
24 : : /* Try UnicodeData.txt in the following:
25 : :
26 : : <path>/share/unicode/
27 : : <home>/.local/share/<prog>/unicode/
28 : : /usr/local/share/<prog>/unicode/
29 : : /usr/share/<prog>/unicode/
30 : : /usr/share/unicode
31 : :
32 : : * Where
33 : : <prog> = that_terminal
34 : : <path> = pathname(argv[0])
35 : : */
36 : : }
37 : :
38 : 11136 : static std::string u8(char32_t code)
39 : : {
40 : 11136 : return ToUTF8(std::u32string_view(&code,1));
41 : : }
42 : :
43 : 2524 : static std::vector<std::string_view> Split(std::string_view str, std::string_view delimiter)
44 : : {
45 [ + - ]: 2524 : std::vector<std::string_view> result;
46 [ + - ]: 2524 : if(!str.empty())
47 : 361 : for(std::size_t pos = 0; ; )
48 : : {
49 : 2885 : std::size_t d = str.find(delimiter, pos);
50 [ + + + - ]: 2885 : if(d == str.npos) { result.emplace_back(str.data()+pos, str.size()-pos); break; }
51 [ + - ]: 361 : result.emplace_back(str.data()+pos, d-pos);
52 : 361 : pos = d + delimiter.size();
53 : 361 : }
54 : 2524 : return result;
55 : 0 : }
56 : : template<typename T>
57 : 1 : static std::string Join(const T& elems, std::string_view delim)
58 : : {
59 : 1 : std::string result;
60 [ + + ]: 26 : for(auto i = elems.begin(); i != elems.end(); ++i)
61 : : {
62 [ + + + - ]: 49 : if(i != elems.begin()) result += delim;
63 : 25 : result += *i;
64 : : }
65 : 1 : return result;
66 : 0 : }
67 : : template<typename T, typename U>
68 : 800 : static std::string Join(const std::map<T,U>& elems, std::string_view delim)
69 : : {
70 : 800 : std::string result;
71 [ + + ]: 1717 : for(auto i = elems.begin(); i != elems.end(); ++i)
72 : : {
73 [ + + + - ]: 1034 : if(i != elems.begin()) result += delim;
74 [ + - ]: 917 : result += i->second;
75 : : }
76 : 800 : return result;
77 : 0 : }
78 : :
79 : : /** Contents of UniData.txt in a parsed format */
80 : 1 : struct UniData
81 : : {
82 : : std::map<std::string, char32_t> codes; ///< Translation of names into codepoints
83 : : std::map<char32_t, std::string> names; ///< Translation of codepoints into names
84 : :
85 : : /** Load from @param unipath file */
86 : 1 : void Load(std::filesystem::path unipath)
87 : : {
88 : 1 : std::regex pattern("([0-9A-F]+);([^;]+);.*", Ropt);
89 [ + - ]: 1 : std::regex mathpat("(MATHEMATICAL.*) (CAPITAL|SMALL) ([A-Z])", Ropt);
90 : :
91 [ + - ]: 1 : std::ifstream f(unipath);
92 [ + - + + ]: 33829 : for(std::string line; std::getline(f, line), f; )
93 [ + - + - ]: 33828 : if(std::smatch mat; std::regex_match(line, mat, pattern))
94 : : {
95 [ + - ]: 33828 : std::string name = mat[2];
96 [ + - + - ]: 33828 : char32_t code = std::stoi(mat[1], nullptr, 16);
97 : :
98 : : // Rename math symbols with "CAPITAL L" into with "LATIN CAPITAL LETTER L"
99 : : // to get better matches.
100 [ + - + + ]: 33828 : if(std::regex_match(name, mat, mathpat))
101 : : {
102 [ + - + - : 652 : name = std::string(mat[1]) + " LATIN " + std::string(mat[2]) + " LETTER " + std::string(mat[3]);
+ - + - +
- + - +
- ]
103 : : }
104 : :
105 [ + - ]: 33828 : codes[name] = code;
106 [ + - + - ]: 67656 : names[code] = name;
107 : 67656 : }
108 : 1 : }
109 : : /** Query whether the given name is defined.
110 : : * @param s name to search for
111 : : * @returns codepoint if known, 0 otherwise
112 : : */
113 : 7031 : char32_t Has(const std::string& s) const
114 : : {
115 [ + + ]: 7031 : if(auto i = codes.find(s); i != codes.end()) return i->second;
116 : 1953 : return 0;
117 : : }
118 : : /** Query whether the given codepoint has a name.
119 : : * @param c codepoint to search for
120 : : * @returns True if the codepoint has a known name.
121 : : */
122 : : bool Has(char32_t c) const
123 : : {
124 : : return names.find(c) != names.end();
125 : : }
126 : : };
127 : :
128 : 1 : void MakeSimilarities(std::ostream& out, std::filesystem::path unipath) // Same as old "similarities.dat"
129 : : {
130 [ + - ]: 1 : UniData d;
131 [ + - + - ]: 1 : d.Load(unipath);
132 : :
133 : : // Create special matching recipe for ASCII from FULLWIDTH
134 : : // Do this BEFORE the MATHEMATICAL section
135 : : // to avoid your ASCII letters looking completely silly.
136 [ + - ]: 1 : out << "// If this is fullwidth font, create ASCII characters from FULLWIDTH characters\n";
137 : :
138 [ + - ]: 1 : std::regex fullhalf("(FULLWIDTH|HALFWIDTH) (.*)", Ropt);
139 : 1 : char32_t tmp;
140 [ + - + + ]: 33765 : for(auto&[name,code]: d.codes)
141 [ + - + + : 33992 : if(std::smatch mat; std::regex_match(name, mat, fullhalf) && (tmp = d.Has(mat[2])))
+ - + + +
+ + + ]
142 [ + - + - : 34430 : out << "→ " << u8(tmp) << u8(code) << '\n';
+ - + - ]
143 : :
144 [ + - ]: 1 : out << R"(
145 : : // Insert some manually crafted rules between pre-composed encircled or stylished letters
146 : : // Do this before the MATH section may alias doublestruck R () with regular R
147 : : // when the font may in fact have doublestruck R (ℝ) in the letterlike section.
148 : : = ℂ
149 : : = ℊ
150 : : = ℋ
151 : : = ℌ𝔥
152 : : = ℍ
153 : : = ℎ
154 : : = ℐ
155 : : = ℑ
156 : : = ℒ
157 : : = ℓ𝓁
158 : : = ℕ
159 : : = ℙ
160 : : = ℚ
161 : : = ℛ
162 : : = ℜ
163 : : = ℝ
164 : : = ℤ
165 : : = K𝖪
166 : : = ℬ
167 : : = ℭ
168 : : = ℮𝕖
169 : : = ℯ𝑒
170 : : = ℳ
171 : : = ℴ
172 : : = ℹ𝐢
173 : : = ⅅ𝔻
174 : : = ⅆ𝕕
175 : : = ⅇ𝕖
176 : : = ⅈ𝕚
177 : : = ⅉ𝕛
178 : : = ⅀𝚺
179 : : = ℿ𝚷
180 : : = ℾ𝚪
181 : : = ℽ𝛄
182 : : = ℼ𝛑
183 : : = ℗Ⓟ
184 : : = ©Ⓒ
185 : : = ®Ⓡ
186 : :
187 : : // Insert equality rules between symbols that are visually completely indiscernible
188 : : // First, ASCII-like characters
189 : : )";
190 : 1 : std::vector<std::u32string_view> equal_symbols{
191 : : UR"(!ǃ)",
192 : : UR"(#ⵌꖛ⌗⋕)",
193 : : UR"(÷➗)",
194 : : UR"(+➕ᚐ)",
195 : : UR"(-−–➖)",
196 : : UR"(.ꓸ)",
197 : : UR"(,ꓹ)",
198 : : UR"(ꓽ::׃꞉⁚ː∶։܃𝄈)",
199 : : UR"(;;;ꓼ)",
200 : : UR"(=ꘌ⚌゠᐀꓿)",
201 : : UR"(/⟋╱⁄𝈺)",
202 : : UR"(\⟍╲𝈻⧹⧵)",
203 : : UR"(2շ)",
204 : : UR"(3З𝟹ვ౩𝈆)",
205 : : UR"(ɜзᴈ)",
206 : : UR"(ƷӠ)",
207 : : UR"(ʒӡ)",
208 : : UR"(4Ꮞ)",
209 : : UR"(8𐌚)",
210 : : UR"(➊❶⓵➀①)",
211 : : UR"(➋❷⓶➁②)",
212 : : UR"(➌❸⓷➂③)",
213 : : UR"(➍❹⓸➃④)",
214 : : UR"(➎❺⓹➄⑤)",
215 : : UR"(➏❻⓺➅⑥)",
216 : : UR"(➐❼⓻➆⑦)",
217 : : UR"(➑❽⓼➇⑧)",
218 : : UR"(➒❾⓽➈⑨)",
219 : : UR"(➓❿⓾➉⑩)",
220 : : UR"(AΑАᎪᗅᗋ𐌀ꓐꓮ)",
221 : : UR"(ĂӐᾸ)",
222 : : UR"(ĀᾹ)",
223 : : UR"(ÄӒ)",
224 : : UR"(ÅÅ)",
225 : : UR"(ÆӔ)",
226 : : UR"(ʙвⲃ)",
227 : : UR"(BΒВᏴⲂꕗ𐌁)",
228 : : UR"(ƂБ)",
229 : : UR"(ϭб)",
230 : : UR"(CϹСᏟⲤⅭꓚ)",
231 : : UR"(ϽƆↃꓛ)",
232 : : UR"(DᎠ𐌃ᗞⅮⅮꓓ)",
233 : : UR"(ꓷᗡ⫏)",
234 : : UR"(EΕЕᎬⴹꗋ⋿ꓰ)",
235 : : UR"(ꓱ∃Ǝⴺ)",
236 : : UR"(ÈЀ)",
237 : : UR"(ËЁ)",
238 : : UR"(ĔӖ)",
239 : : UR"(ƐԐ)",
240 : : UR"(FϜ𝈓ߓᖴ𐌅ꓝ)",
241 : : UR"(GᏀႺꓖ)",
242 : : UR"(HΗНᎻⲎᕼꖾꓧ)",
243 : : UR"(ʜнⲏ)",
244 : : UR"(IΙІӀᏆⲒⅠꓲ)",
245 : : UR"(ΪÏЇ)",
246 : : UR"(JЈᒍلﻝᎫꓙ)",
247 : : UR"(KΚКⲔᏦK𐌊ꓗ)",
248 : : UR"(ḰЌ)",
249 : : UR"(κᴋкⲕ)",
250 : : UR"(LᏞᒪ𝈪Ⅼ˪ԼⅬԼⅬԼլꓡ)",
251 : : UR"(MΜМᎷⲘϺ𐌑Ⅿꓟ)",
252 : : UR"(ᴍмⲙ)",
253 : : UR"(NΝⲚꓠ)",
254 : : UR"(ͶИ)",
255 : : UR"(ɴⲛ)",
256 : : UR"(ͷи)",
257 : : UR"(OΟОⲞ◯○⃝❍🌕߀ⵔՕ⚪⭕౦೦ꓳ)",
258 : : UR"(ÖӦ)",
259 : : UR"(ϴθѲӨƟᎾⲐ)",
260 : : UR"(ΦФⲪ)",
261 : : UR"(PΡРᏢⲢ𐌓ᑭ𐌛ꓑ)",
262 : : UR"(ΠПⲠ)",
263 : : UR"(QԚꝖႳႭⵕℚ)",
264 : : UR"(RᎡ𝈖ᖇᏒꓣ)",
265 : : UR"(Яᖆ)",
266 : : UR"(SЅᏚՏႽꚂऽ𐒖ꕶꓢ)",
267 : : UR"(ΣƩ∑⅀Ʃⵉ)",
268 : : UR"(TΤТᎢⲦ⊤⟙ꔋ𝍮🝨⏉ߠꓔ)",
269 : : UR"(U⋃ᑌ∪ՍՍꓴ)",
270 : : UR"(VᏙᐯⴸ⋁𝈍ⅤⅤꓦ)",
271 : : UR"(WԜᎳꓪ)",
272 : : UR"(XΧХⲬ╳ⵝ𐌢Ⅹ𐌗Ⅹꓫ)",
273 : : UR"(YΥҮⲨꓬ)",
274 : : UR"(ΫŸ)",
275 : : UR"(ZΖᏃⲌꓜ)",
276 : : UR"(aа)",
277 : : UR"(äӓ)",
278 : : UR"(ăӑ)",
279 : : UR"(æӕ)",
280 : : UR"(əә)",
281 : : UR"(ЬᏏ)",
282 : : UR"(cϲсⲥⅽ)",
283 : : UR"(ͻɔᴐↄ)",
284 : : UR"(dⅾ)",
285 : : UR"(eе)",
286 : : UR"(ĕӗ)",
287 : : UR"(ϵє)",
288 : : UR"(ɛԑ)",
289 : : UR"(gց)",
290 : : UR"(гᴦⲅ)",
291 : : UR"(iіⅰ)",
292 : : UR"(ïї)",
293 : : UR"(jϳј)",
294 : : UR"(lⅼ)",
295 : : UR"(ιɩ)",
296 : : UR"(mⅿ)",
297 : : UR"(ʌᴧ)",
298 : : UR"(oοоסᴏⲟօ૦௦ഠ๐໐໐)",
299 : : UR"(òὸ)",
300 : : UR"(óό)",
301 : : UR"(öӧ)",
302 : : UR"(ɵөⲑ)",
303 : : UR"(фⲫ)",
304 : : UR"(pρрⲣ)",
305 : : UR"(πпᴨⲡ)",
306 : : UR"(яᴙ)",
307 : : UR"(qԛ)",
308 : : UR"(sѕ)",
309 : : UR"(uս)",
310 : : UR"(vᴠݍⅴ)",
311 : : UR"(xⅹ)",
312 : : UR"(yу)",
313 : : UR"(~῀)",
314 : : UR"(··•∙⋅・)",
315 : : UR"(ᴛтⲧ)",
316 : : UR"(૰。࿁)",
317 : : UR"(ᐃ△🜂∆ΔᐃⵠΔꕔ)",
318 : : UR"(ᐊᐊ◁⊲)",
319 : : UR"(ᐁ▽🜄⛛∇ᐁ𝈔)",
320 : : UR"(ᐅ▷⊳▻)",
321 : : UR"(ᐱΛ𐤂⋀ⴷ𐌡Ʌ)",
322 : : UR"(ᑎႶ⋂Ո∩𝉅ꓵ)",
323 : : UR"(⨆∐ⵡ𝈈)",
324 : : UR"(∏⨅ПΠ⊓)",
325 : : UR"(⊏ⵎ𝈸)",
326 : : UR"(コ⊐ߏ𝈹)",
327 : : UR"(⎕□☐⬜◻▢⃞❑❒❐❏⧠⃢⌷ロ)",
328 : : UR"(⛝⌧🝱)",
329 : : UR"( )"
330 [ + - ]: 1 : };
331 [ + + ]: 140 : for(auto w: equal_symbols)
332 [ + - + - : 278 : out << "= " << ToUTF8(w) << '\n';
+ - ]
333 : :
334 [ + - ]: 1 : out << "\n// Create similarity rules between modified stylished symbols\n";
335 : 1 : if(1)
336 : : {
337 [ + - ]: 1 : std::initializer_list<std::string_view> words
338 : : {
339 : : "BLACK",
340 : : "HEAVY",
341 : : "FULLWIDTH",
342 : : "MATHEMATICAL BOLD", // 1D400,1D41A
343 : : "MATHEMATICAL SANS-SERIF BOLD", // 1D5D4,1D5EE
344 : : "MATHEMATICAL SANS-SERIF BOLD ITALIC",// 1D63C,1D656
345 : : "MATHEMATICAL BOLD ITALIC", // 1D468,1D482
346 : : "MATHEMATICAL ITALIC", // 1D434,1D44E
347 : : "MATHEMATICAL SANS-SERIF ITALIC", // 1D608,1D622
348 : : "", // no modifier.
349 : : "MATHEMATICAL SANS-SERIF", // 1D5A0,1D5BA
350 : : "MATHEMATICAL SCRIPT", // 1D49C,1D4B6
351 : : "MATHEMATICAL BOLD SCRIPT", // 1D4D0,1D4EA
352 : : "MATHEMATICAL FRAKTUR", // 1D504,1D51E
353 : : "MATHEMATICAL BOLD FRAKTUR", // 1D56C,1D586
354 : : "MATHEMATICAL DOUBLE-STRUCK", // 1D538,1D552
355 : : "MATHEMATICAL MONOSPACE", // 1D670,1D68A
356 : : "MATHEMATICAL",
357 : : "WHITE",
358 : : "LIGHT",
359 : : "HALFWIDTH",
360 : : "SMALL",
361 : : "PARENTHESIZED",
362 : : "CIRCLED",
363 : : "TAG"
364 : 1 : };
365 [ + - + - : 2 : std::regex word_style_pat("(" + Join(words, "|") + ") (.*)", Ropt);
+ - ]
366 : 1 : std::map<std::string, std::map<std::string, char32_t>> symbols;
367 [ + - + + ]: 33765 : for(const auto& [name,code]: d.codes)
368 : : {
369 [ + - + - : 33764 : symbols[name][""] = code;
+ - ]
370 [ + - + + ]: 33764 : if(std::smatch mat; std::regex_match(name, mat, word_style_pat))
371 [ + - + - : 36006 : symbols[mat[2]][mat[1]] = code;
+ - + - ]
372 : : }
373 [ + + + + ]: 34510 : for(auto&& [basename,group]: symbols)
374 [ + + ]: 34509 : if(group.size() > 1)
375 : : {
376 [ + - ]: 679 : out << "◆ ";
377 [ + + ]: 17654 : for(auto w: words)
378 [ + - + + ]: 33950 : if(auto i = group.find(std::string(w)); i != group.end())
379 [ + - ]: 4352 : out << u8(i->second);
380 [ + - ]: 679 : out << '\n';
381 : : }
382 : 1 : }
383 : :
384 : : // Convert the equal-symbols list into a searchable one
385 : 1 : std::map<char32_t, std::vector<char32_t>> equal_with;
386 [ + + ]: 140 : for(auto w: equal_symbols)
387 [ + + ]: 749 : for(char32_t code: w)
388 [ + + ]: 4544 : for(char32_t code2: w)
389 [ + + ]: 3934 : if(code != code2)
390 [ + - + - ]: 3288 : equal_with[code].push_back(code2);
391 [ + + ]: 593 : for(auto& w: equal_with)
392 : : {
393 : 592 : std::sort(w.second.begin(), w.second.end());
394 : 592 : w.second.erase(std::unique(w.second.begin(), w.second.end()), w.second.end());
395 : : }
396 : :
397 : 1 : out << "// Then go through all symbols that are “WITH” something.\n"
398 : : "// As a general rule, try to compose things that have more “WITHs”\n"
399 [ + - ]: 1 : "// from things that have less “WITHs”.\n";
400 : :
401 [ + - ]: 1 : std::map<std::string, std::map<std::string, std::size_t>> with_lists;
402 [ + - ]: 1 : std::regex with_match("(.*) WITH (.*)", Ropt);
403 [ + - + + ]: 33765 : for(const auto& [name,code]: d.codes)
404 [ + - + + ]: 33764 : if(std::smatch mat; std::regex_match(name, mat, with_match))
405 : : {
406 [ + - ]: 2524 : std::string base = mat[1];
407 [ + - ]: 2524 : std::string full = mat[2];
408 [ + - ]: 2524 : std::vector<std::string_view> attrs = Split(full, " AND ");
409 [ + - ]: 2524 : std::size_t len = attrs.size();
410 [ + - + - ]: 2524 : auto& wl = with_lists[" WITH " + full];
411 [ + - + - ]: 2524 : wl[""] = 0;
412 [ + + ]: 2885 : for(std::size_t n = len; --n > 0; )
413 : : {
414 [ + - ]: 361 : std::map<std::size_t, std::string_view> pick;
415 : 839 : std::function<void(std::size_t,std::size_t)> Do = [&](std::size_t index, std::size_t start)
416 : : {
417 [ + + ]: 1395 : for(std::size_t a=start; a<len; ++a)
418 : : {
419 : 917 : pick[index] = attrs[a];
420 [ + + ]: 917 : if(index+1 == n)
421 : : {
422 [ + - + - ]: 800 : wl[" WITH " + /*partial*/Join(pick, " AND ")] = pick.size();
423 : : //print "try make $partial from $full for $name for $base\n";
424 : : }
425 : : else
426 : 117 : Do(index+1, a+1);
427 : : }
428 [ + - ]: 839 : };
429 [ + - ]: 361 : Do(0, 0);
430 : 361 : }
431 : 36288 : }
432 [ + + ]: 3 : for(auto operation: std::initializer_list<std::string_view>{"→ ", "← "})
433 [ + - + + ]: 1268 : for(auto& [full_with, p]: with_lists)
434 : : {
435 [ + - ]: 1266 : std::vector<std::pair<std::string,std::size_t>> partial_list(p.begin(), p.end());
436 : : // Sort in descending order according to the score (second item)
437 : 1266 : std::sort(partial_list.begin(), partial_list.end(),
438 [ - - - - : 1328 : [](const auto& a, const auto& b) { return b.second > a.second; });
+ + - - -
+ - - - -
- - - - -
- - - -
- ]
439 : : // Find all symbols that have this "full with" list.
440 [ + - ]: 2532 : std::regex pat("(.*)" + full_with, Ropt);
441 [ + + + + ]: 42746490 : for(auto& [name,code]: d.codes)
442 [ + + + - : 42745224 : if(name.size() >= full_with.size() && name.compare(name.size()-full_with.size(), full_with.size(), full_with) == 0)
+ + ]
443 : : {
444 [ + - ]: 5048 : std::string_view mat1(name.data(), name.size()-full_with.size());
445 [ + - ]: 5048 : std::vector<std::tuple<char32_t, std::string_view, std::string_view>> rep_list, sub_list;
446 [ + - ]: 5048 : rep_list.emplace_back(code, name, mat1);
447 [ + + ]: 5048 : if(auto i = equal_with.find(code); i != equal_with.end())
448 [ + + ]: 234 : for(auto code2: i->second)
449 : : {
450 [ + - ]: 158 : const auto& name2 = d.names[code2];
451 [ + - ]: 158 : std::smatch mat2;
452 [ + - ]: 158 : std::regex_match(name2, mat2, with_match); // (.*) WITH.*
453 [ + - ]: 158 : rep_list.emplace_back(code2, name2, std::string_view(&*mat2[1].first, mat2[1].second-mat2[1].first));
454 : 158 : }
455 [ + + ]: 11696 : for(auto& [partial_with,dummy]: partial_list)
456 [ + + ]: 13454 : for(auto& rep: rep_list)
457 : : {
458 [ + - ]: 6806 : std::string sub_name{std::get<2>(rep)};
459 [ + - ]: 6806 : sub_name += partial_with;
460 : : //print "can we find $sub_name?\n";
461 [ + + ]: 6806 : if(auto code2 = d.Has(sub_name); code2)
462 [ + - ]: 4856 : sub_list.emplace_back(code2, sub_name, sub_name);
463 : 6806 : }
464 [ + - + + ]: 9904 : for(auto&& sub: sub_list) rep_list.push_back(std::move(sub));
465 [ + + ]: 5048 : if(rep_list.size() > 1)
466 : : {
467 [ + - ]: 3502 : out << operation;
468 [ + - + + ]: 12018 : for(auto& rep: rep_list) out << u8(std::get<0>(rep));
469 [ + - ]: 3502 : out << '\n';
470 : : }
471 : 5048 : }
472 : 1266 : }
473 : :
474 [ + - ]: 1 : out << R"(
475 : : // Some symbols that act as last resort…
476 : : = Ⅱ║∥‖ǁ𝄁
477 : : = Ⅲ⫴⦀⫼𝍫ꔖ
478 : : = -‐‑–—−-‒―➖─━一╴╶╸╺╼╾╼╾
479 : : = ┄┅⋯┈┉╌╍
480 : : = ╎╏¦
481 : : = │┃|╿╽
482 : : = ═=꓿
483 : : = ~⁓~
484 : : = <く𐌂ᐸᑉ
485 : : = ┌┍┎┏╭╒╓╔гᴦⲅ
486 : : = ┐┑┒┓╮╕╖╗
487 : : = └┕┖┗╰╘╙╚˪լ
488 : : = ┘┙┚┛╯╛╜╝
489 : : = ┬┭┮┯┰┱┲┳╤╥╦⊤
490 : : = ┴┵┶┷┸┹┺┻╧╨╩
491 : : = ├┝┞┟┠┡┢┣߅╞╟╠
492 : : = ┤┥┦┧┨┩┪┫╡╢╣
493 : : = ┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋╪╫╬
494 : : = ▉⬛██▉▇
495 : : → ガカ
496 : : → グク
497 : : → ギキ
498 : : → ゲケ
499 : : → ゴコ
500 : : → パバハ
501 : : → ピビヒ
502 : : → ペベヘ
503 : : → ポボホ
504 : : → プブフ
505 : : → ピビ
506 : : → ペベ
507 : : → ポボ
508 : : → プブ
509 : : → ザサ
510 : : → ジシ
511 : : → ズス
512 : : → ゼセ
513 : : → ゾソ
514 : : → ダタ
515 : : → ヂチ
516 : : → ヅツ
517 : : → デテ
518 : : → ドト
519 : : → がか
520 : : → ぐく
521 : : → ぎき
522 : : → げけ
523 : : → ごこ
524 : : → ぱばは
525 : : → ぴびひ
526 : : → ぺべへ
527 : : → ぽぼほ
528 : : → ぷぶふ
529 : : → ぱば
530 : : → ぴび
531 : : → ぺべ
532 : : → ぽぼ
533 : : → ぷぶ
534 : : → ざさ
535 : : → じし
536 : : → ずす
537 : : → ぜせ
538 : : → ぞそ
539 : : → だた
540 : : → ぢち
541 : : → づつ
542 : : → でて
543 : : → どと
544 : : )";
545 : 1 : }
546 : :
547 : 2 : std::vector<std::pair<char32_t/*goal*/, char32_t/*recipe*/>> ParseSimilarities(std::istream& f) // Same as old "similarities.inc"
548 : : {
549 : 2 : std::vector<std::pair<char32_t, char32_t>> results;
550 : :
551 [ + - + + ]: 9330 : for(std::string line; std::getline(f, line), f; )
552 [ + + ]: 9326 : if(!line.empty())
553 : : {
554 [ - + - - ]: 9318 : if(line.back() == '\r') { line.erase(line.size()-1); }
555 [ + - + - ]: 9318 : if(auto codes = FromUTF8(line); !codes.empty())
556 : : {
557 : 9318 : std::size_t a=2, b=codes.size();
558 [ + + + + : 9318 : switch(codes[0])
+ ]
559 : : {
560 : : case U'→':
561 : : // Generate symbol on left from symbol on right
562 [ + + ]: 9620 : for(std::size_t n=a, m=n+1; m<b; ++m)
563 [ + - ]: 5576 : results.emplace_back(codes[n], codes[m]);
564 : : break;
565 : : case U'←':
566 : : // Generate symbol on right from symbol on left
567 [ + + ]: 15520 : for(std::size_t n=b; n-- >= a; )
568 [ + + ]: 20344 : for(std::size_t m=n; --m >= a; )
569 [ + - ]: 8326 : results.emplace_back(codes[n], codes[m]);
570 : : break;
571 : 1358 : case U'◆':
572 : 1358 : {
573 : : // Generate any symbol from any other,
574 : : // with preference for close-by one
575 : 1358 : std::map<std::size_t, std::vector<std::pair<std::size_t,std::size_t>>> score;
576 [ + + ]: 5710 : for(std::size_t n=a; n<b; ++n)
577 [ + + ]: 31904 : for(std::size_t m=a; m<b; ++m)
578 [ + + ]: 27552 : if(m != n)
579 [ + - + - ]: 23200 : score[std::abs(int(n-m))].emplace_back(n, m);
580 [ + + ]: 4352 : for(auto& [dummy,pairs]: score)
581 [ + + ]: 26194 : for(auto pair: pairs)
582 [ + - ]: 23200 : results.emplace_back(codes[pair.first], codes[pair.second]);
583 : 1358 : break;
584 : 1358 : }
585 : : case U'=':
586 : : // Generate any symbol from any other symbol
587 [ + + ]: 2098 : for(std::size_t n=a; n<b; ++n)
588 [ + + ]: 13708 : for(std::size_t m=a; m<b; ++m)
589 [ + + ]: 12002 : if(m != n)
590 [ + - ]: 10296 : results.emplace_back(codes[n], codes[m]);
591 : : break;
592 : : }
593 : 9318 : }
594 : 0 : }
595 : :
596 : : // Improve the list using iconv's //TRANSLIT option (pregenerated using make-alias.php)
597 [ + - + - : 8 : auto [location, status] = FindShareFile(std::filesystem::path("fonts") / "alias.txt");
+ - + - -
+ ]
598 [ - + ]: 2 : if(std::filesystem::exists(status))
599 : : {
600 [ + - ]: 2 : std::ifstream f2(location);
601 : 2 : auto delim = " \t"sv, hex = "0123456789ABCDEFabcdef"sv;
602 [ + - + + ]: 9230 : for(std::string line; std::getline(f2, line), f2; )
603 [ + - + - ]: 9228 : if(!line.empty() && line[0] != '#')
604 : : {
605 : 9228 : std::size_t space = line.find_first_of(delim);
606 [ - + ]: 9228 : if(space == line.npos) continue;
607 : 9228 : std::size_t notspace = line.find_first_not_of(delim, space);
608 [ - + ]: 9228 : if(notspace == line.npos) continue;
609 : 9228 : std::size_t notdigit = line.find_first_not_of(hex, notspace);
610 [ - + ]: 9228 : if(notdigit == line.npos) notdigit = line.size();
611 : :
612 [ + - ]: 9228 : results.emplace_back(std::stoi(std::string(line, 0, space), nullptr, 16),
613 [ + - + - ]: 27684 : std::stoi(std::string(line, notspace, notdigit-notspace), nullptr, 16));
614 : 0 : }
615 : 2 : }
616 : :
617 : : // Delete duplicates
618 [ + - ]: 4 : std::unordered_set<std::uint_fast64_t> seen;
619 [ + - ]: 2 : results.erase(std::remove_if(results.begin(), results.end(), [&](auto& pair)
620 : : {
621 : 56626 : uint_fast64_t key = pair.first; key = (key << 32) | pair.second;
622 [ + + ]: 56626 : if(seen.find(key) == seen.end())
623 : : {
624 : 49138 : seen.insert(key);
625 : 49138 : return false;
626 : : }
627 : : return true;
628 [ + - ]: 2 : }), results.end());
629 : :
630 : 2 : std::sort(results.begin(), results.end());
631 : 2 : return results;
632 : 2 : }
633 : :
634 : 2 : std::vector<std::pair<char32_t/*goal*/, char32_t/*recipe*/>> ParseSimilarities() // Same as old "similarities.inc"
635 : : {
636 [ + - + - ]: 4 : auto [path, status] = FindCacheFile("similarities.dat", true);
637 [ + - ]: 2 : auto [unipath, unistatus] = UnicodeDataFileName();
638 : :
639 [ - + ]: 2 : if(!std::filesystem::exists(status)
640 [ + - + + ]: 2 : || !std::filesystem::file_size(path)
641 [ + - + - ]: 1 : || (std::filesystem::exists(unipath)
642 [ + - + - : 1 : && std::filesystem::last_write_time(unipath) > std::filesystem::last_write_time(path)))
- + ]
643 : : {
644 [ + - ]: 1 : std::ofstream file(path);
645 [ + - + - ]: 1 : MakeSimilarities(file, unipath);
646 : 1 : }
647 : :
648 [ + - ]: 2 : std::ifstream f(path);
649 [ + - ]: 2 : return ParseSimilarities(f);
650 : 6 : }
|