max / shop
15 files changed,
+580 insertions,
-168 deletions
| @@ -17,6 +17,21 @@ | |||
| 17 | 17 | "memchr", | |
| 18 | 18 | ] | |
| 19 | 19 | ||
| 20 | + | [[package]] | |
| 21 | + | name = "alloc-no-stdlib" | |
| 22 | + | version = "2.0.4" | |
| 23 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 24 | + | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" | |
| 25 | + | ||
| 26 | + | [[package]] | |
| 27 | + | name = "alloc-stdlib" | |
| 28 | + | version = "0.2.4" | |
| 29 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 30 | + | checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" | |
| 31 | + | dependencies = [ | |
| 32 | + | "alloc-no-stdlib", | |
| 33 | + | ] | |
| 34 | + | ||
| 20 | 35 | [[package]] | |
| 21 | 36 | name = "allocator-api2" | |
| 22 | 37 | version = "0.2.21" | |
| @@ -88,6 +103,15 @@ | |||
| 88 | 103 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 89 | 104 | checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" | |
| 90 | 105 | ||
| 106 | + | [[package]] | |
| 107 | + | name = "arbitrary" | |
| 108 | + | version = "1.4.2" | |
| 109 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 110 | + | checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" | |
| 111 | + | dependencies = [ | |
| 112 | + | "derive_arbitrary", | |
| 113 | + | ] | |
| 114 | + | ||
| 91 | 115 | [[package]] | |
| 92 | 116 | name = "arrayvec" | |
| 93 | 117 | version = "0.7.8" | |
| @@ -136,6 +160,36 @@ | |||
| 136 | 160 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 137 | 161 | checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" | |
| 138 | 162 | ||
| 163 | + | [[package]] | |
| 164 | + | name = "block-buffer" | |
| 165 | + | version = "0.10.4" | |
| 166 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 167 | + | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" | |
| 168 | + | dependencies = [ | |
| 169 | + | "generic-array", | |
| 170 | + | ] | |
| 171 | + | ||
| 172 | + | [[package]] | |
| 173 | + | name = "brotli" | |
| 174 | + | version = "8.0.4" | |
| 175 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 176 | + | checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" | |
| 177 | + | dependencies = [ | |
| 178 | + | "alloc-no-stdlib", | |
| 179 | + | "alloc-stdlib", | |
| 180 | + | "brotli-decompressor", | |
| 181 | + | ] | |
| 182 | + | ||
| 183 | + | [[package]] | |
| 184 | + | name = "brotli-decompressor" | |
| 185 | + | version = "5.0.3" | |
| 186 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 187 | + | checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" | |
| 188 | + | dependencies = [ | |
| 189 | + | "alloc-no-stdlib", | |
| 190 | + | "alloc-stdlib", | |
| 191 | + | ] | |
| 192 | + | ||
| 139 | 193 | [[package]] | |
| 140 | 194 | name = "bumpalo" | |
| 141 | 195 | version = "3.20.3" | |
| @@ -279,6 +333,15 @@ | |||
| 279 | 333 | "crossbeam-utils", | |
| 280 | 334 | ] | |
| 281 | 335 | ||
| 336 | + | [[package]] | |
| 337 | + | name = "cpufeatures" | |
| 338 | + | version = "0.2.17" | |
| 339 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 340 | + | checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" | |
| 341 | + | dependencies = [ | |
| 342 | + | "libc", | |
| 343 | + | ] | |
| 344 | + | ||
| 282 | 345 | [[package]] | |
| 283 | 346 | name = "crc32fast" | |
| 284 | 347 | version = "1.5.0" | |
| @@ -300,12 +363,43 @@ | |||
| 300 | 363 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 301 | 364 | checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" | |
| 302 | 365 | ||
| 366 | + | [[package]] | |
| 367 | + | name = "crypto-common" | |
| 368 | + | version = "0.1.7" | |
| 369 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 370 | + | checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" | |
| 371 | + | dependencies = [ | |
| 372 | + | "generic-array", | |
| 373 | + | "typenum", | |
| 374 | + | ] | |
| 375 | + | ||
| 303 | 376 | [[package]] | |
| 304 | 377 | name = "cursor-icon" | |
| 305 | 378 | version = "1.2.0" | |
| 306 | 379 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 307 | 380 | checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" | |
| 308 | 381 | ||
| 382 | + | [[package]] | |
| 383 | + | name = "derive_arbitrary" | |
| 384 | + | version = "1.4.2" | |
| 385 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 386 | + | checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" | |
| 387 | + | dependencies = [ | |
| 388 | + | "proc-macro2", | |
| 389 | + | "quote", | |
| 390 | + | "syn 2.0.119", | |
| 391 | + | ] | |
| 392 | + | ||
| 393 | + | [[package]] | |
| 394 | + | name = "digest" | |
| 395 | + | version = "0.10.7" | |
| 396 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 397 | + | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" | |
| 398 | + | dependencies = [ | |
| 399 | + | "block-buffer", | |
| 400 | + | "crypto-common", | |
| 401 | + | ] | |
| 402 | + | ||
| 309 | 403 | [[package]] | |
| 310 | 404 | name = "dispatch2" | |
| 311 | 405 | version = "0.3.1" | |
| @@ -394,6 +488,7 @@ | |||
| 394 | 488 | dependencies = [ | |
| 395 | 489 | "crc32fast", | |
| 396 | 490 | "miniz_oxide", | |
| 491 | + | "zlib-rs", | |
| 397 | 492 | ] | |
| 398 | 493 | ||
| 399 | 494 | [[package]] | |
| @@ -404,9 +499,9 @@ | |||
| 404 | 499 | ||
| 405 | 500 | [[package]] | |
| 406 | 501 | name = "font-types" | |
| 407 | - | version = "0.12.2" | |
| 502 | + | version = "0.12.3" | |
| 408 | 503 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 409 | - | checksum = "0a7299a780854a6d391be2ae1c8521c9368471b559dbfd6a8dbd9f407eaff100" | |
| 504 | + | checksum = "75382bc7392ef10aad10935f92fc3db36d2d4dad0e5d96d8d65e04f89a07ec39" | |
| 410 | 505 | dependencies = [ | |
| 411 | 506 | "bytemuck", | |
| 412 | 507 | ] | |
| @@ -444,6 +539,16 @@ | |||
| 444 | 539 | "slab", | |
| 445 | 540 | ] | |
| 446 | 541 | ||
| 542 | + | [[package]] | |
| 543 | + | name = "generic-array" | |
| 544 | + | version = "0.14.7" | |
| 545 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 546 | + | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" | |
| 547 | + | dependencies = [ | |
| 548 | + | "typenum", | |
| 549 | + | "version_check", | |
| 550 | + | ] | |
| 551 | + | ||
| 447 | 552 | [[package]] | |
| 448 | 553 | name = "getrandom" | |
| 449 | 554 | version = "0.4.3" | |
| @@ -671,6 +776,18 @@ | |||
| 671 | 776 | "tracing", | |
| 672 | 777 | ] | |
| 673 | 778 | ||
| 779 | + | [[package]] | |
| 780 | + | name = "kurbo" | |
| 781 | + | version = "0.13.1" | |
| 782 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 783 | + | checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2" | |
| 784 | + | dependencies = [ | |
| 785 | + | "arrayvec", | |
| 786 | + | "euclid", | |
| 787 | + | "polycool", | |
| 788 | + | "smallvec", | |
| 789 | + | ] | |
| 790 | + | ||
| 674 | 791 | [[package]] | |
| 675 | 792 | name = "lazy_static" | |
| 676 | 793 | version = "1.5.0" | |
| @@ -1009,6 +1126,15 @@ | |||
| 1009 | 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1010 | 1127 | checksum = "bc6355899e1c9462875b6757c79f3caa011a1fdae12bbb1a2e72dd1f234f8336" | |
| 1011 | 1128 | ||
| 1129 | + | [[package]] | |
| 1130 | + | name = "polycool" | |
| 1131 | + | version = "0.4.0" | |
| 1132 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1133 | + | checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6" | |
| 1134 | + | dependencies = [ | |
| 1135 | + | "arrayvec", | |
| 1136 | + | ] | |
| 1137 | + | ||
| 1012 | 1138 | [[package]] | |
| 1013 | 1139 | name = "portable-atomic" | |
| 1014 | 1140 | version = "1.14.0" | |
| @@ -1051,6 +1177,22 @@ | |||
| 1051 | 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1052 | 1178 | checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" | |
| 1053 | 1179 | ||
| 1180 | + | [[package]] | |
| 1181 | + | name = "quasi-type" | |
| 1182 | + | version = "0.1.0" | |
| 1183 | + | dependencies = [ | |
| 1184 | + | "brotli", | |
| 1185 | + | "font-types", | |
| 1186 | + | "kurbo", | |
| 1187 | + | "read-fonts 0.43.0", | |
| 1188 | + | "serde", | |
| 1189 | + | "sha2", | |
| 1190 | + | "skrifa 0.46.0", | |
| 1191 | + | "toml", | |
| 1192 | + | "write-fonts", | |
| 1193 | + | "zip", | |
| 1194 | + | ] | |
| 1195 | + | ||
| 1054 | 1196 | [[package]] | |
| 1055 | 1197 | name = "quick-xml" | |
| 1056 | 1198 | version = "0.41.0" | |
| @@ -1104,6 +1246,17 @@ | |||
| 1104 | 1246 | "once_cell", | |
| 1105 | 1247 | ] | |
| 1106 | 1248 | ||
| 1249 | + | [[package]] | |
| 1250 | + | name = "read-fonts" | |
| 1251 | + | version = "0.43.0" | |
| 1252 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1253 | + | checksum = "a6b5cae85882cfb0167038e7726a426f47ebd808e67af78c2d6bcbb02acbf3ce" | |
| 1254 | + | dependencies = [ | |
| 1255 | + | "bytemuck", | |
| 1256 | + | "font-types", | |
| 1257 | + | "once_cell", | |
| 1258 | + | ] | |
| 1259 | + | ||
| 1107 | 1260 | [[package]] | |
| 1108 | 1261 | name = "redox_syscall" | |
| 1109 | 1262 | version = "0.5.18" | |
| @@ -1225,6 +1378,17 @@ | |||
| 1225 | 1378 | "serde_core", | |
| 1226 | 1379 | ] | |
| 1227 | 1380 | ||
| 1381 | + | [[package]] | |
| 1382 | + | name = "sha2" | |
| 1383 | + | version = "0.10.9" | |
| 1384 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1385 | + | checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" | |
| 1386 | + | dependencies = [ | |
| 1387 | + | "cfg-if", | |
| 1388 | + | "cpufeatures", | |
| 1389 | + | "digest", | |
| 1390 | + | ] | |
| 1391 | + | ||
| 1228 | 1392 | [[package]] | |
| 1229 | 1393 | name = "sharded-slab" | |
| 1230 | 1394 | version = "0.1.7" | |
| @@ -1251,6 +1415,7 @@ | |||
| 1251 | 1415 | "kittygfx", | |
| 1252 | 1416 | "makeover", | |
| 1253 | 1417 | "pollster", | |
| 1418 | + | "shop-font", | |
| 1254 | 1419 | "shop-grid", | |
| 1255 | 1420 | "shop-pty", | |
| 1256 | 1421 | "shop-render", | |
| @@ -1276,6 +1441,13 @@ | |||
| 1276 | 1441 | "serde_json", | |
| 1277 | 1442 | ] | |
| 1278 | 1443 | ||
| 1444 | + | [[package]] | |
| 1445 | + | name = "shop-font" | |
| 1446 | + | version = "0.1.0" | |
| 1447 | + | dependencies = [ | |
| 1448 | + | "quasi-type", | |
| 1449 | + | ] | |
| 1450 | + | ||
| 1279 | 1451 | [[package]] | |
| 1280 | 1452 | name = "shop-grid" | |
| 1281 | 1453 | version = "0.1.0" | |
| @@ -1301,6 +1473,7 @@ | |||
| 1301 | 1473 | "bytemuck", | |
| 1302 | 1474 | "fontconfig", | |
| 1303 | 1475 | "guillotiere", | |
| 1476 | + | "shop-font", | |
| 1304 | 1477 | "swash", | |
| 1305 | 1478 | "tracing", | |
| 1306 | 1479 | "wgpu", | |
| @@ -1339,7 +1512,17 @@ | |||
| 1339 | 1512 | checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68" | |
| 1340 | 1513 | dependencies = [ | |
| 1341 | 1514 | "bytemuck", | |
| 1342 | - | "read-fonts", | |
| 1515 | + | "read-fonts 0.41.0", | |
| 1516 | + | ] | |
| 1517 | + | ||
| 1518 | + | [[package]] | |
| 1519 | + | name = "skrifa" | |
| 1520 | + | version = "0.46.0" | |
| 1521 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1522 | + | checksum = "de0bbcc507e438bfe765692ae5228059959781db7430fe36e1cfd569e7c88c89" | |
| 1523 | + | dependencies = [ | |
| 1524 | + | "bytemuck", | |
| 1525 | + | "read-fonts 0.43.0", | |
| 1343 | 1526 | ] | |
| 1344 | 1527 | ||
| 1345 | 1528 | [[package]] | |
| @@ -1418,7 +1601,7 @@ | |||
| 1418 | 1601 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1419 | 1602 | checksum = "6c2499c2d826531388872b2268718aed907a39bd785ab0dcfe57fab26283f92e" | |
| 1420 | 1603 | dependencies = [ | |
| 1421 | - | "skrifa", | |
| 1604 | + | "skrifa 0.44.0", | |
| 1422 | 1605 | "yazi", | |
| 1423 | 1606 | "zeno", | |
| 1424 | 1607 | ] | |
| @@ -1588,6 +1771,12 @@ | |||
| 1588 | 1771 | "tracing-log", | |
| 1589 | 1772 | ] | |
| 1590 | 1773 | ||
| 1774 | + | [[package]] | |
| 1775 | + | name = "typenum" | |
| 1776 | + | version = "1.20.1" | |
| 1777 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1778 | + | checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" | |
| 1779 | + | ||
| 1591 | 1780 | [[package]] | |
| 1592 | 1781 | name = "unicode-ident" | |
| 1593 | 1782 | version = "1.0.24" | |
| @@ -2067,6 +2256,19 @@ | |||
| 2067 | 2256 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2068 | 2257 | checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" | |
| 2069 | 2258 | ||
| 2259 | + | [[package]] | |
| 2260 | + | name = "write-fonts" | |
| 2261 | + | version = "0.52.0" | |
| 2262 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2263 | + | checksum = "843cebb7bd36359861dbce43d54ac8b33dfdf7b80a95913adc1ef7c7bb881e62" | |
| 2264 | + | dependencies = [ | |
| 2265 | + | "font-types", | |
| 2266 | + | "indexmap", | |
| 2267 | + | "kurbo", | |
| 2268 | + | "log", | |
| 2269 | + | "read-fonts 0.43.0", | |
| 2270 | + | ] | |
| 2271 | + | ||
| 2070 | 2272 | [[package]] | |
| 2071 | 2273 | name = "xcursor" | |
| 2072 | 2274 | version = "0.3.10" | |
| @@ -2142,31 +2344,43 @@ | |||
| 2142 | 2344 | "syn 2.0.119", | |
| 2143 | 2345 | ] | |
| 2144 | 2346 | ||
| 2347 | + | [[package]] | |
| 2348 | + | name = "zip" | |
| 2349 | + | version = "6.0.0" | |
| 2350 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2351 | + | checksum = "eb2a05c7c36fde6c09b08576c9f7fb4cda705990f73b58fe011abf7dfb24168b" | |
| 2352 | + | dependencies = [ | |
| 2353 | + | "arbitrary", | |
| 2354 | + | "crc32fast", | |
| 2355 | + | "flate2", | |
| 2356 | + | "indexmap", | |
| 2357 | + | "memchr", | |
| 2358 | + | "zopfli", | |
| 2359 | + | ] | |
| 2360 | + | ||
| 2361 | + | [[package]] | |
| 2362 | + | name = "zlib-rs" | |
| 2363 | + | version = "0.6.7" | |
| 2364 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2365 | + | checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" | |
| 2366 | + | ||
| 2145 | 2367 | [[package]] | |
| 2146 | 2368 | name = "zmij" | |
| 2147 | 2369 | version = "1.0.23" | |
| 2148 | 2370 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2149 | 2371 | checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" | |
| 2150 | 2372 | ||
| 2151 | - | [[patch.unused]] | |
| 2152 | - | name = "docengine" | |
| 2153 | - | version = "0.7.0" | |
| 2154 | - | ||
| 2155 | - | [[patch.unused]] | |
| 2156 | - | name = "kberg" | |
| 2157 | - | version = "0.1.0" | |
| 2158 | - | ||
| 2159 | - | [[patch.unused]] | |
| 2160 | - | name = "ops-status" | |
| 2161 | - | version = "0.1.0" | |
| 2162 | - | ||
| 2163 | - | [[patch.unused]] | |
| 2164 | - | name = "painhours" | |
| 2165 | - | version = "0.1.0" | |
| 2166 | - | ||
| 2167 | - | [[patch.unused]] | |
| 2168 | - | name = "tagtree" | |
| 2169 | - | version = "0.4.0" | |
| 2373 | + | [[package]] | |
| 2374 | + | name = "zopfli" | |
| 2375 | + | version = "0.8.3" | |
| 2376 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2377 | + | checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" | |
| 2378 | + | dependencies = [ | |
| 2379 | + | "bumpalo", | |
| 2380 | + | "crc32fast", | |
| 2381 | + | "log", | |
| 2382 | + | "simd-adler32", | |
| 2383 | + | ] | |
| 2170 | 2384 | ||
| 2171 | 2385 | [[patch.unused]] | |
| 2172 | 2386 | name = "synckit-client" | |
| @@ -2207,3 +2421,23 @@ | |||
| 2207 | 2421 | [[patch.unused]] | |
| 2208 | 2422 | name = "quasi-webview" | |
| 2209 | 2423 | version = "0.18.0" | |
| 2424 | + | ||
| 2425 | + | [[patch.unused]] | |
| 2426 | + | name = "docengine" | |
| 2427 | + | version = "0.7.0" | |
| 2428 | + | ||
| 2429 | + | [[patch.unused]] | |
| 2430 | + | name = "kberg" | |
| 2431 | + | version = "0.1.0" | |
| 2432 | + | ||
| 2433 | + | [[patch.unused]] | |
| 2434 | + | name = "ops-status" | |
| 2435 | + | version = "0.1.0" | |
| 2436 | + | ||
| 2437 | + | [[patch.unused]] | |
| 2438 | + | name = "painhours" | |
| 2439 | + | version = "0.1.0" | |
| 2440 | + | ||
| 2441 | + | [[patch.unused]] | |
| 2442 | + | name = "tagtree" | |
| 2443 | + | version = "0.4.0" |
| @@ -2,6 +2,7 @@ | |||
| 2 | 2 | resolver = "2" | |
| 3 | 3 | members = [ | |
| 4 | 4 | "crates/shop", | |
| 5 | + | "crates/shop-font", | |
| 5 | 6 | "crates/kitty-graphics", | |
| 6 | 7 | "crates/shop-wayland", | |
| 7 | 8 | "crates/shop-render", |
| @@ -194,10 +194,26 @@ | |||
| 194 | 194 | ||
| 195 | 195 | ## Fonts | |
| 196 | 196 | ||
| 197 | - | Shop bundles one font, a nerd-patched Iosevka, so a fresh install has its icons | |
| 198 | - | without a font install first. That covers Latin, Greek, Cyrillic, combining | |
| 199 | - | marks, box drawing and braille, and it has no CJK: a nerd-font patch adds icons | |
| 200 | - | in the private use area and cannot add a script. | |
| 197 | + | Shop bundles one font, Quasi Mono, so a fresh install has its box drawing and | |
| 198 | + | its marks without a font install first. That covers Latin, Greek, Cyrillic, | |
| 199 | + | combining marks, box drawing and block elements, and it has no CJK. | |
| 200 | + | ||
| 201 | + | The face is not committed. It is cut at build time by | |
| 202 | + | [quasi-type](https://makenot.work/git/max/quasi-type), the house font pipeline: | |
| 203 | + | a pinned base plus the house glyph set in, a `Quasi <Slot>` face out. `shop-font` | |
| 204 | + | runs it from a build script and the base is cached in the target directory, so | |
| 205 | + | the first build in a fresh checkout needs network and the rest do not. A font in | |
| 206 | + | the repo would be a second source of truth that nothing rebuilds. | |
| 207 | + | ||
| 208 | + | It replaced a nerd-patched IosevkaTerm, 13 MB committed in `assets/` against 70 | |
| 209 | + | KB cut here. What that costs is braille and the legacy-computing sextants, which | |
| 210 | + | now come from fallback; nothing shop draws for itself emits either. What it buys | |
| 211 | + | is that the marks are the same drawings the rest of the house uses, rather than | |
| 212 | + | whichever face the OS happened to order first. | |
| 213 | + | ||
| 214 | + | The face is variable, `wght` 200 to 800 in one file, and **its own default is the | |
| 215 | + | light end**. Shop names the weight it draws at (`shop_font::WEIGHT`) rather than | |
| 216 | + | taking the file's, which is what a naive loader and `fc-match` both do. | |
| 201 | 217 | ||
| 202 | 218 | For anything the bundled font does not have, shop asks the system. On a miss it | |
| 203 | 219 | puts the character to fontconfig, loads whatever comes back, and keeps it for |
| @@ -20,3 +20,7 @@ | |||
| 20 | 20 | anyhow.workspace = true | |
| 21 | 21 | fontconfig.workspace = true | |
| 22 | 22 | tracing.workspace = true | |
| 23 | + | ||
| 24 | + | [dev-dependencies] | |
| 25 | + | # The bundled face, cut once by the workspace rather than three times. | |
| 26 | + | shop-font = { path = "../shop-font" } |
| @@ -19,6 +19,7 @@ | |||
| 19 | 19 | [dependencies] | |
| 20 | 20 | shop-wayland = { path = "../shop-wayland" } | |
| 21 | 21 | shop-render = { path = "../shop-render" } | |
| 22 | + | shop-font = { path = "../shop-font" } | |
| 22 | 23 | shop-pty = { path = "../shop-pty" } | |
| 23 | 24 | shop-grid = { path = "../shop-grid" } | |
| 24 | 25 | shop-xkb = { path = "../shop-xkb" } |
| @@ -1,9 +1,9 @@ | |||
| 1 | 1 | //! Asking the system which font has a character the bundled one does not. | |
| 2 | 2 | //! | |
| 3 | - | //! Shop bundles one font so that a fresh install has its icons without a font | |
| 4 | - | //! install first, and that font is Iosevka: Latin, Greek, Cyrillic, box drawing | |
| 5 | - | //! and braille in full, and no Han, kana or hangul at all. A nerd-font patch | |
| 6 | - | //! adds icons in the private use area and cannot add a script. | |
| 3 | + | //! Shop bundles one font so that a fresh install has its box drawing and its | |
| 4 | + | //! house marks without a font install first, and that font is Quasi Mono: | |
| 5 | + | //! Latin, Greek, Cyrillic, box drawing and block elements in full, and no Han, | |
| 6 | + | //! kana or hangul at all. | |
| 7 | 7 | //! | |
| 8 | 8 | //! Rather than pick a second font ourselves and ship it, ask the machine. On | |
| 9 | 9 | //! Linux fontconfig is the thing that already knows, and already carries the | |
| @@ -12,6 +12,17 @@ | |||
| 12 | 12 | //! Wayland-only, so fontconfig is present wherever shop runs, and it is | |
| 13 | 13 | //! consulted only when the bundled font misses, which for ordinary output is | |
| 14 | 14 | //! never. | |
| 15 | + | //! | |
| 16 | + | //! **What this path is for is scripts, and it is a poor answer for symbols.** | |
| 17 | + | //! `Shaper::baseline` takes the line off the primary face alone, deliberately, | |
| 18 | + | //! so a fallback face with its own metrics sits its glyphs on a different line. | |
| 19 | + | //! For a run of Han that is right; for a box-drawing character it would draw | |
| 20 | + | //! borders off the line, which is why the whole cell tier is in the face rather | |
| 21 | + | //! than left to fallback. Two blocks are outside it and now arrive this way: | |
| 22 | + | //! braille (U+2800) and the legacy-computing sextants, which IosevkaTerm | |
| 23 | + | //! carried and Quasi Mono does not. Nothing in the tree emits either - there is | |
| 24 | + | //! no `Canvas` and no `Sparkline` anywhere - and the day one does, they are | |
| 25 | + | //! generated recipes in the house set rather than a fallback. | |
| 15 | 26 | ||
| 16 | 27 | use fontconfig::{CharSet, Fontconfig, Pattern}; | |
| 17 | 28 |
| @@ -81,7 +81,20 @@ | |||
| 81 | 81 | // Rounded, because a cell has to be a whole number of pixels: a | |
| 82 | 82 | // fractional advance puts every column at a fractional x, and the | |
| 83 | 83 | // glyph is resampled rather than drawn. | |
| 84 | - | advance: exact_advance.round().max(1.0), | |
| 84 | + | // | |
| 85 | + | // **Up, not to nearest**, for the same reason the height is: a cell | |
| 86 | + | // narrower than the advance makes every glyph overlap its right-hand | |
| 87 | + | // neighbour, which corrupts the per-row render cache, where a cell | |
| 88 | + | // wider than the advance only leaves a hairline of background that | |
| 89 | + | // the furniture snap closes anyway. | |
| 90 | + | // | |
| 91 | + | // This stopped being hypothetical when shop's bundled face changed. | |
| 92 | + | // IosevkaTerm advanced 1/2 em and landed whole at every even size, | |
| 93 | + | // so `round` never rounded down in practice. Quasi Mono advances | |
| 94 | + | // 79/125 em and 79 is prime, so it is whole at 125px and nowhere | |
| 95 | + | // usable — at 15px it measures 9.48 and `round` would have taken | |
| 96 | + | // half a pixel out of every column. | |
| 97 | + | advance: exact_advance.ceil().max(1.0), | |
| 85 | 98 | // Rounded *up*, not to nearest. A cell shorter than the line clips | |
| 86 | 99 | // the descenders of every row, and a glyph bleeding into the row | |
| 87 | 100 | // below breaks the per-row render cache; the cost of rounding up is | |
| @@ -144,35 +157,54 @@ | |||
| 144 | 157 | mod tests { | |
| 145 | 158 | use super::*; | |
| 146 | 159 | ||
| 147 | - | const FONT: &[u8] = include_bytes!("../../../assets/IosevkaTermNerdFontMono-Regular.ttf"); | |
| 160 | + | const FONT: &[u8] = shop_font::FACE; | |
| 148 | 161 | ||
| 149 | 162 | // The measurement the hardcoded constants got wrong, pinned so a font swap | |
| 150 | 163 | // cannot quietly reintroduce it. | |
| 164 | + | // | |
| 165 | + | // Stated as the face's own tables rather than as two numbers, because the | |
| 166 | + | // numbers moved when the bundled face did: IosevkaTerm is 1/2 em and 14px | |
| 167 | + | // gave exactly 7.0, Quasi Mono is 79/125 and gives 8.848. A test that | |
| 168 | + | // asserted 7.0 was asserting which font shop bundles, which is what the | |
| 169 | + | // rest of this file is for. | |
| 151 | 170 | #[test] | |
| 152 | 171 | fn the_bundled_face_measures_what_its_tables_say() { | |
| 172 | + | let font = FontRef::from_index(FONT, 0).unwrap(); | |
| 173 | + | let m = font.metrics(&[]).scale(14.0); | |
| 153 | 174 | let cell = CellMetrics::measure(FONT, 14.0).unwrap(); | |
| 154 | - | // 500/1000 em at 14px, and hhea 965/-285 with no leading. | |
| 155 | 175 | assert!( | |
| 156 | - | (cell.exact_advance - 7.0).abs() < 0.01, | |
| 157 | - | "advance {}", | |
| 176 | + | (cell.exact_advance | |
| 177 | + | - font | |
| 178 | + | .glyph_metrics(&[]) | |
| 179 | + | .scale(14.0) | |
| 180 | + | .advance_width(font.charmap().map('M'))) | |
| 181 | + | .abs() | |
| 182 | + | < 0.01, | |
| 183 | + | "advance {} is not the face's own", | |
| 158 | 184 | cell.exact_advance | |
| 159 | 185 | ); | |
| 160 | 186 | assert!( | |
| 161 | - | (cell.exact_height - 17.5).abs() < 0.01, | |
| 162 | - | "height {}", | |
| 187 | + | (cell.exact_height - (m.ascent + m.descent + m.leading)).abs() < 0.01, | |
| 188 | + | "height {} is not ascent + descent + leading", | |
| 163 | 189 | cell.exact_height | |
| 164 | 190 | ); | |
| 165 | - | assert!((cell.advance - 7.0).abs() < f32::EPSILON); | |
| 166 | - | assert!((cell.height - 18.0).abs() < f32::EPSILON); | |
| 191 | + | assert!((cell.advance - cell.exact_advance.ceil()).abs() < f32::EPSILON); | |
| 192 | + | assert!((cell.height - cell.exact_height.ceil()).abs() < f32::EPSILON); | |
| 167 | 193 | } | |
| 168 | 194 | ||
| 169 | 195 | // The old constants, as a record of what was wrong rather than as a target. | |
| 196 | + | // | |
| 197 | + | // They were 8.0 x 17.0 and derived from no face at all, which is the defect: | |
| 198 | + | // against IosevkaTerm at 14px (7.0 x 17.5) every column carried a pixel of | |
| 199 | + | // dead space and every row lost half of one. That they happen to sit closer | |
| 200 | + | // to the face bundled today is luck and changes nothing — a number nothing | |
| 201 | + | // measures is wrong the moment the font moves, which it has since. | |
| 170 | 202 | #[test] | |
| 171 | - | fn the_old_constants_were_a_pixel_out_per_column() { | |
| 203 | + | fn the_old_constants_are_not_this_faces_cell_either() { | |
| 172 | 204 | let cell = CellMetrics::measure(FONT, 14.0).unwrap(); | |
| 173 | 205 | assert!( | |
| 174 | - | (8.0 - cell.exact_advance) >= 0.99, | |
| 175 | - | "the hardcoded 8.0 left {} of dead column", | |
| 206 | + | (8.0 - cell.exact_advance).abs() > 0.5, | |
| 207 | + | "the hardcoded 8.0 is {} off this face's advance", | |
| 176 | 208 | 8.0 - cell.exact_advance | |
| 177 | 209 | ); | |
| 178 | 210 | assert!( |
| @@ -164,9 +164,13 @@ | |||
| 164 | 164 | // it has to be the cell the caller actually lays out with rather than | |
| 165 | 165 | // one this constructor measures for itself. | |
| 166 | 166 | cell: crate::CellMetrics, | |
| 167 | + | // Where on the face's weight axis to draw. The bundled face is variable | |
| 168 | + | // and its own default is the light end of the axis, so this is named | |
| 169 | + | // rather than taken from the file. | |
| 170 | + | weight: f32, | |
| 167 | 171 | ) -> anyhow::Result<Self> { | |
| 168 | 172 | let atlas = GlyphAtlas::new(device, queue, ATLAS_SIZE); | |
| 169 | - | let shaper = Shaper::new(font_data, font_px, cell)?; | |
| 173 | + | let shaper = Shaper::new(font_data, font_px, cell, weight)?; | |
| 170 | 174 | ||
| 171 | 175 | let uniforms_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { | |
| 172 | 176 | label: Some("shop.text.uniforms"), | |
| @@ -410,7 +414,9 @@ | |||
| 410 | 414 | } | |
| 411 | 415 | CellText::Cluster(s) => { | |
| 412 | 416 | // The pen DOES advance across the cluster, and the marks | |
| 413 | - | // still land on the base. Measured on the bundled Iosevka: | |
| 417 | + | // still land on the base. Measured on the face bundled at | |
| 418 | + | // the time, IosevkaTerm, and it is a property of how fonts | |
| 419 | + | // are built rather than of that face: | |
| 414 | 420 | // a combining glyph carries a full cell of advance and its | |
| 415 | 421 | // ink hangs off the LEFT of its origin (acute -5px for a | |
| 416 | 422 | // 3px mark, diaeresis -7px for a 6px one). The font is | |
| @@ -425,7 +431,7 @@ | |||
| 425 | 431 | // | |
| 426 | 432 | // The two other shapes this can take are handled by the | |
| 427 | 433 | // same line: where the shaper composes the pair into one | |
| 428 | - | // precomposed glyph (Iosevka does for e + acute) there is | |
| 434 | + | // precomposed glyph (both faces do for e + acute) there is | |
| 429 | 435 | // nothing after the base to place, and where a font | |
| 430 | 436 | // positions marks by GPOS the mark's advance is zero and | |
| 431 | 437 | // its offsets do the work. |
| @@ -64,6 +64,16 @@ | |||
| 64 | 64 | scale_ctx: ScaleContext, | |
| 65 | 65 | shape_ctx: ShapeContext, | |
| 66 | 66 | px: f32, | |
| 67 | + | /// Where on the primary face's weight axis to draw. | |
| 68 | + | /// | |
| 69 | + | /// Named rather than inherited, because a variable face's default instance | |
| 70 | + | /// is whatever its designer set and is not always the weight anyone wants: | |
| 71 | + | /// Quasi Mono is cut from a base whose default is `wght` 200, so a terminal | |
| 72 | + | /// that took the file's own default would draw every prompt in ExtraLight. | |
| 73 | + | /// | |
| 74 | + | /// Applied to every face, not only the primary one. A static fallback has | |
| 75 | + | /// no axes and swash drops the setting, so this needs no branch. | |
| 76 | + | weight: f32, | |
| 67 | 77 | /// The cell this shaper is drawing into, in physical pixels. | |
| 68 | 78 | cell: CellMetrics, | |
| 69 | 79 | /// Scale that maps the face's own cell onto the pixel one, applied to | |
| @@ -102,7 +112,12 @@ | |||
| 102 | 112 | } | |
| 103 | 113 | ||
| 104 | 114 | impl Shaper { | |
| 105 | - | pub(crate) fn new(font_data: Vec<u8>, px: f32, cell: CellMetrics) -> anyhow::Result<Self> { | |
| 115 | + | pub(crate) fn new( | |
| 116 | + | font_data: Vec<u8>, | |
| 117 | + | px: f32, | |
| 118 | + | cell: CellMetrics, | |
| 119 | + | weight: f32, | |
| 120 | + | ) -> anyhow::Result<Self> { | |
| 106 | 121 | let font = FontRef::from_index(&font_data, 0) | |
| 107 | 122 | .ok_or_else(|| anyhow::anyhow!("swash: not a font"))?; | |
| 108 | 123 | let font_offset = font.offset; | |
| @@ -127,6 +142,7 @@ | |||
| 127 | 142 | scale_ctx: ScaleContext::new(), | |
| 128 | 143 | shape_ctx: ShapeContext::new(), | |
| 129 | 144 | px, | |
| 145 | + | weight, | |
| 130 | 146 | cell, | |
| 131 | 147 | snap, | |
| 132 | 148 | furniture: HashSet::new(), | |
| @@ -145,7 +161,7 @@ | |||
| 145 | 161 | /// Char → the font that has it and its glyph id there. | |
| 146 | 162 | /// | |
| 147 | 163 | /// Falls back to `(PRIMARY, 0)` when nothing has it, which draws whatever | |
| 148 | - | /// the bundled font puts at `.notdef` — a hollow box in Iosevka's case. The | |
| 164 | + | /// the bundled font puts at `.notdef` — a hollow box in most faces. The | |
| 149 | 165 | /// cell still holds its columns either way, so a missing glyph costs the | |
| 150 | 166 | /// look of the line and never its layout. | |
| 151 | 167 | pub(crate) fn glyph_id_for(&mut self, c: char) -> (FontId, GlyphId) { | |
| @@ -208,7 +224,12 @@ | |||
| 208 | 224 | offset: f.offset, | |
| 209 | 225 | key: swash::CacheKey::new(), | |
| 210 | 226 | }; | |
| 211 | - | let mut shaper = self.shape_ctx.builder(face).size(self.px).build(); | |
| 227 | + | let mut shaper = self | |
| 228 | + | .shape_ctx | |
| 229 | + | .builder(face) | |
| 230 | + | .size(self.px) | |
| 231 | + | .variations(&[("wght", self.weight)][..]) | |
| 232 | + | .build(); | |
| 212 | 233 | shaper.add_str(text); | |
| 213 | 234 | let mut out = Vec::new(); | |
| 214 | 235 | shaper.shape_with(|cluster| { | |
| @@ -235,6 +256,7 @@ | |||
| 235 | 256 | .scale_ctx | |
| 236 | 257 | .builder(face) | |
| 237 | 258 | .size(self.px) | |
| 259 | + | .variations(&[("wght", self.weight)][..]) | |
| 238 | 260 | .hint(true) | |
| 239 | 261 | .build(); | |
| 240 | 262 | let mut render = Render::new(&[Source::Outline]); | |
| @@ -292,11 +314,11 @@ | |||
| 292 | 314 | mod tests { | |
| 293 | 315 | use super::*; | |
| 294 | 316 | ||
| 295 | - | const FONT: &[u8] = include_bytes!("../../../assets/IosevkaTermNerdFontMono-Regular.ttf"); | |
| 317 | + | const FONT: &[u8] = shop_font::FACE; | |
| 296 | 318 | ||
| 297 | 319 | /// A shaper over the bundled face, drawing into the cell that face implies. | |
| 298 | 320 | fn shaper(px: f32, cell: CellMetrics) -> Shaper { | |
| 299 | - | Shaper::new(FONT.to_vec(), px, cell).expect("the bundled face loads") | |
| 321 | + | Shaper::new(FONT.to_vec(), px, cell, shop_font::WEIGHT).expect("the bundled face loads") | |
| 300 | 322 | } | |
| 301 | 323 | ||
| 302 | 324 | /// The rasterized extent of `c`, as (left, right, top, bottom) in pixels | |
| @@ -385,21 +407,24 @@ | |||
| 385 | 407 | assert!(y0 > 1.0 && y1 < cell.height - 1.0, "`x` spans {y0}..{y1}"); | |
| 386 | 408 | } | |
| 387 | 409 | ||
| 388 | - | // The case the snap was built for. At 15px the bundled face advances 7.5, | |
| 389 | - | // so the cell rounds to 8 and a full pixel of every column would be | |
| 390 | - | // background — the same shape of defect as the hardcoded 8.0, arrived at | |
| 391 | - | // honestly. An advance is a fraction of an em and lands whole only at sizes | |
| 392 | - | // that clear its denominator, so this is the general case and 14px is the | |
| 393 | - | // lucky one. | |
| 410 | + | // The case the snap was built for: a size where the advance is not a whole | |
| 411 | + | // number of pixels, so the cell rounds up and the difference would be | |
| 412 | + | // background down every column — the same shape of defect as the hardcoded | |
| 413 | + | // 8.0, arrived at honestly. | |
| 414 | + | // | |
| 415 | + | // With the face shop bundles today this is not a special case but the only | |
| 416 | + | // case: Quasi Mono's advance is 79/125 em and 79 is prime, so it lands | |
| 417 | + | // whole at 125px and nowhere usable. IosevkaTerm was 1/2 and did it at | |
| 418 | + | // every even size, which is why this test used to have to reach for 15px. | |
| 394 | 419 | #[test] | |
| 395 | 420 | fn a_size_where_the_advance_is_fractional_still_tiles() { | |
| 396 | 421 | let cell = CellMetrics::measure(FONT, 15.0).unwrap(); | |
| 397 | 422 | assert!( | |
| 398 | - | (cell.exact_advance - 7.5).abs() < 0.01, | |
| 399 | - | "advance {}", | |
| 423 | + | (cell.exact_advance - cell.exact_advance.floor()).abs() > 0.01, | |
| 424 | + | "advance {} is whole, so this test is not testing the snap", | |
| 400 | 425 | cell.exact_advance | |
| 401 | 426 | ); | |
| 402 | - | assert!((cell.advance - 8.0).abs() < f32::EPSILON); | |
| 427 | + | assert!((cell.advance - cell.exact_advance.ceil()).abs() < f32::EPSILON); | |
| 403 | 428 | ||
| 404 | 429 | let mut shaper = shaper(15.0, cell); | |
| 405 | 430 | let (x0, x1, y0, y1) = extent(&mut shaper, '\u{2588}'); |
| @@ -70,10 +70,18 @@ | |||
| 70 | 70 | }; | |
| 71 | 71 | ||
| 72 | 72 | const INITIAL: (u32, u32) = (960, 540); | |
| 73 | - | /// IosevkaTerm Nerd Font Mono, terminal-optimized variant of Iosevka with | |
| 74 | - | /// nerd-font glyphs baked in. Bundled so starship, yazi, helix and friends | |
| 75 | - | /// get their icons without a system font install. ~13 MB in the binary. | |
| 76 | - | const FONT_BYTES: &[u8] = include_bytes!("../../../assets/IosevkaTermNerdFontMono-Regular.ttf"); | |
| 73 | + | /// Quasi Mono, the house monospace face, cut at build time by `shop-font`. | |
| 74 | + | /// | |
| 75 | + | /// Bundled so a fresh install has its box drawing, block elements and the house | |
| 76 | + | /// marks without a font install first. It replaced a nerd-patched IosevkaTerm | |
| 77 | + | /// committed in `assets/`: 13 MB of binary for a Private Use Area nothing in | |
| 78 | + | /// the house emits, against 70 KB here. What the swap costs is braille and the | |
| 79 | + | /// legacy-computing sextants, which now come from fallback; nothing in the tree | |
| 80 | + | /// draws either. | |
| 81 | + | /// | |
| 82 | + | /// **Variable, and its own default is the light end of the axis.** The weight | |
| 83 | + | /// is named at `shop_font::WEIGHT` rather than taken from the file. | |
| 84 | + | const FONT_BYTES: &[u8] = shop_font::FACE; | |
| 77 | 85 | const FONT_PX: f32 = 14.0; | |
| 78 | 86 | const PAD_X: f32 = 10.0; | |
| 79 | 87 | const PAD_Y: f32 = 6.0; | |
| @@ -177,6 +185,7 @@ | |||
| 177 | 185 | --record PATH Tee the terminal output to PATH as raw bytes. | |
| 178 | 186 | -h, --help Print this help. | |
| 179 | 187 | -V, --version Print the version. | |
| 188 | + | --license Print the bundled font's licence. | |
| 180 | 189 | ||
| 181 | 190 | Options take their value either way: --theme dark or --theme=dark. | |
| 182 | 191 | ||
| @@ -220,7 +229,7 @@ | |||
| 220 | 229 | .ok_or_else(|| CliExit::Reject(format!("{flag} needs a value"))), | |
| 221 | 230 | }; | |
| 222 | 231 | match name { | |
| 223 | - | "-h" | "--help" | "-V" | "--version" if inline.is_some() => { | |
| 232 | + | "-h" | "--help" | "-V" | "--version" | "--license" if inline.is_some() => { | |
| 224 | 233 | return Err(CliExit::Reject(format!("{name} takes no value"))); | |
| 225 | 234 | } | |
| 226 | 235 | "-h" | "--help" => return Err(CliExit::Answer(HELP.into())), | |
| @@ -230,6 +239,18 @@ | |||
| 230 | 239 | env!("CARGO_PKG_VERSION") | |
| 231 | 240 | ))); | |
| 232 | 241 | } | |
| 242 | + | // OFL 1.1 requires the licence to travel with a modified build, and | |
| 243 | + | // the bundled face is one: a patched Atkinson Hyperlegible Mono. It | |
| 244 | + | // used to travel as a file in the repo, which does not reach anyone | |
| 245 | + | // holding only the binary. Now it is in the binary and reachable. | |
| 246 | + | "--license" => { | |
| 247 | + | return Err(CliExit::Answer(format!( | |
| 248 | + | "shop bundles {} {}, cut from the house glyph set.\n\n{}", | |
| 249 | + | shop_font::FAMILY, | |
| 250 | + | shop_font::DEFAULT_STYLE, | |
| 251 | + | shop_font::LICENSE | |
| 252 | + | ))); | |
| 253 | + | } | |
| 233 | 254 | "--exec" => cli.exec_cmd = Some(value("--exec")?), | |
| 234 | 255 | "--theme" => cli.theme = Some(value("--theme")?), | |
| 235 | 256 | "--record" => cli.record_path = Some(value("--record")?), | |
| @@ -411,7 +432,15 @@ | |||
| 411 | 432 | }; | |
| 412 | 433 | // Scale 1 at startup; a `wl_surface.enter` on a HiDPI output rebuilds the | |
| 413 | 434 | // renderer with the scaled cell. | |
| 414 | - | let text = TextRenderer::new(&device, &queue, format, font_data, FONT_PX, cell.scaled(1))?; | |
| 435 | + | let text = TextRenderer::new( | |
| 436 | + | &device, | |
| 437 | + | &queue, | |
| 438 | + | format, | |
| 439 | + | font_data, | |
| 440 | + | FONT_PX, | |
| 441 | + | cell.scaled(1), | |
| 442 | + | shop_font::WEIGHT, | |
| 443 | + | )?; | |
| 415 | 444 | let images = ImageRenderer::new(&device, format); | |
| 416 | 445 | ||
| 417 | 446 | let mut grid = Grid::new(cols_initial, rows_initial); | |
| @@ -1256,6 +1285,7 @@ | |||
| 1256 | 1285 | self.font_data.clone(), | |
| 1257 | 1286 | FONT_PX * new_scale as f32, | |
| 1258 | 1287 | self.cell.scaled(new_scale), | |
| 1288 | + | shop_font::WEIGHT, | |
| 1259 | 1289 | ) { | |
| 1260 | 1290 | Ok(t) => self.text = t, | |
| 1261 | 1291 | Err(e) => { | |
| @@ -2391,17 +2421,27 @@ | |||
| 2391 | 2421 | } | |
| 2392 | 2422 | ||
| 2393 | 2423 | // The regression guard the two constants never had. They were 8.0 x 17.0 | |
| 2394 | - | // against a face that is 7.0 x 17.5 at this size, so every column carried a | |
| 2395 | - | // pixel of dead space and every row lost half of one. | |
| 2424 | + | // and derived from no face at all, so every column carried a pixel of dead | |
| 2425 | + | // space and every row lost half of one. | |
| 2426 | + | // | |
| 2427 | + | // Asserted as a relationship rather than as two numbers: the numbers are | |
| 2428 | + | // the bundled face's, and the bundled face has already changed once. | |
| 2396 | 2429 | #[test] | |
| 2397 | 2430 | fn the_cell_is_the_bundled_faces_own_and_not_a_number_someone_typed() { | |
| 2398 | 2431 | let cell = test_cell(); | |
| 2399 | - | assert!((cell.advance - 7.0).abs() < f32::EPSILON, "{cell:?}"); | |
| 2400 | - | assert!((cell.height - 18.0).abs() < f32::EPSILON, "{cell:?}"); | |
| 2401 | 2432 | assert!( | |
| 2402 | - | (cell.advance - cell.exact_advance).abs() < f32::EPSILON, | |
| 2403 | - | "this face's advance is a whole pixel at 14px, so nothing is rounded away" | |
| 2433 | + | (cell.advance - cell.exact_advance.ceil()).abs() < f32::EPSILON, | |
| 2434 | + | "{cell:?}" | |
| 2404 | 2435 | ); | |
| 2436 | + | assert!( | |
| 2437 | + | (cell.height - cell.exact_height.ceil()).abs() < f32::EPSILON, | |
| 2438 | + | "{cell:?}" | |
| 2439 | + | ); | |
| 2440 | + | // Under a pixel of rounding in both axes, which is what the furniture | |
| 2441 | + | // snap absorbs. More than that would mean the cell and the face have | |
| 2442 | + | // come apart rather than been rounded. | |
| 2443 | + | let (dx, dy) = cell.rounding_error(); | |
| 2444 | + | assert!(dx < 1.0 && dy < 1.0, "{cell:?} rounds by {dx} x {dy}"); | |
| 2405 | 2445 | } | |
| 2406 | 2446 | ||
| 2407 | 2447 | // Cells tile: what the grid steps by is what a glyph fills. Asserted |
Binary file
| @@ -1,96 +1,0 @@ | |||
| 1 | - | Covers: IosevkaTermNerdFontMono-Regular.ttf | |
| 2 | - | Nerd Font patched build; the patcher is MIT and the font keeps the licence below. | |
| 3 | - | ||
| 4 | - | Copyright 2015-2025, Renzhi Li (aka. Belleve Invis, belleve@typeof.net). | |
| 5 | - | ||
| 6 | - | This Font Software is licensed under the SIL Open Font License, Version 1.1. | |
| 7 | - | This license is copied below, and is also available with a FAQ at: | |
| 8 | - | http://scripts.sil.org/OFL | |
| 9 | - | ||
| 10 | - | ||
| 11 | - | ----------------------------------------------------------- | |
| 12 | - | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 | |
| 13 | - | ----------------------------------------------------------- | |
| 14 | - | ||
| 15 | - | PREAMBLE | |
| 16 | - | The goals of the Open Font License (OFL) are to stimulate worldwide | |
| 17 | - | development of collaborative font projects, to support the font creation | |
| 18 | - | efforts of academic and linguistic communities, and to provide a free and | |
| 19 | - | open framework in which fonts may be shared and improved in partnership | |
| 20 | - | with others. | |
| 21 | - | ||
| 22 | - | The OFL allows the licensed fonts to be used, studied, modified and | |
| 23 | - | redistributed freely as long as they are not sold by themselves. The | |
| 24 | - | fonts, including any derivative works, can be bundled, embedded, | |
| 25 | - | redistributed and/or sold with any software provided that any reserved | |
| 26 | - | names are not used by derivative works. The fonts and derivatives, | |
| 27 | - | however, cannot be released under any other type of license. The | |
| 28 | - | requirement for fonts to remain under this license does not apply | |
| 29 | - | to any document created using the fonts or their derivatives. | |
| 30 | - | ||
| 31 | - | DEFINITIONS | |
| 32 | - | "Font Software" refers to the set of files released by the Copyright | |
| 33 | - | Holder(s) under this license and clearly marked as such. This may | |
| 34 | - | include source files, build scripts and documentation. | |
| 35 | - | ||
| 36 | - | "Reserved Font Name" refers to any names specified as such after the | |
| 37 | - | copyright statement(s). | |
| 38 | - | ||
| 39 | - | "Original Version" refers to the collection of Font Software components as | |
| 40 | - | distributed by the Copyright Holder(s). | |
| 41 | - | ||
| 42 | - | "Modified Version" refers to any derivative made by adding to, deleting, | |
| 43 | - | or substituting -- in part or in whole -- any of the components of the | |
| 44 | - | Original Version, by changing formats or by porting the Font Software to a | |
| 45 | - | new environment. | |
| 46 | - | ||
| 47 | - | "Author" refers to any designer, engineer, programmer, technical | |
| 48 | - | writer or other person who contributed to the Font Software. | |
| 49 | - | ||
| 50 | - | PERMISSION & CONDITIONS | |
| 51 | - | Permission is hereby granted, free of charge, to any person obtaining | |
| 52 | - | a copy of the Font Software, to use, study, copy, merge, embed, modify, | |
| 53 | - | redistribute, and sell modified and unmodified copies of the Font | |
| 54 | - | Software, subject to the following conditions: | |
| 55 | - | ||
| 56 | - | 1) Neither the Font Software nor any of its individual components, | |
| 57 | - | in Original or Modified Versions, may be sold by itself. | |
| 58 | - | ||
| 59 | - | 2) Original or Modified Versions of the Font Software may be bundled, | |
| 60 | - | redistributed and/or sold with any software, provided that each copy | |
| 61 | - | contains the above copyright notice and this license. These can be | |
| 62 | - | included either as stand-alone text files, human-readable headers or | |
| 63 | - | in the appropriate machine-readable metadata fields within text or | |
| 64 | - | binary files as long as those fields can be easily viewed by the user. | |
| 65 | - | ||
| 66 | - | 3) No Modified Version of the Font Software may use the Reserved Font | |
| 67 | - | Name(s) unless explicit written permission is granted by the corresponding | |
| 68 | - | Copyright Holder. This restriction only applies to the primary font name as | |
| 69 | - | presented to the users. | |
| 70 | - | ||
| 71 | - | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font | |
| 72 | - | Software shall not be used to promote, endorse or advertise any | |
| 73 | - | Modified Version, except to acknowledge the contribution(s) of the | |
| 74 | - | Copyright Holder(s) and the Author(s) or with their explicit written | |
| 75 | - | permission. | |
| 76 | - | ||
| 77 | - | 5) The Font Software, modified or unmodified, in part or in whole, | |
| 78 | - | must be distributed entirely under this license, and must not be | |
| 79 | - | distributed under any other license. The requirement for fonts to | |
| 80 | - | remain under this license does not apply to any document created | |
| 81 | - | using the Font Software. | |
| 82 | - | ||
| 83 | - | TERMINATION | |
| 84 | - | This license becomes null and void if any of the above conditions are | |
| 85 | - | not met. | |
| 86 | - | ||
| 87 | - | DISCLAIMER | |
| 88 | - | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 89 | - | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF | |
| 90 | - | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT | |
| 91 | - | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE | |
| 92 | - | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
| 93 | - | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL | |
| 94 | - | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 95 | - | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM | |
| 96 | - | OTHER DEALINGS IN THE FONT SOFTWARE. |
| @@ -1,0 +1,21 @@ | |||
| 1 | + | [package] | |
| 2 | + | name = "shop-font" | |
| 3 | + | version = "0.1.0" | |
| 4 | + | description = "The bundled face, cut from the house font pipeline at build time" | |
| 5 | + | publish = false | |
| 6 | + | edition.workspace = true | |
| 7 | + | rust-version.workspace = true | |
| 8 | + | authors.workspace = true | |
| 9 | + | repository.workspace = true | |
| 10 | + | license.workspace = true | |
| 11 | + | ||
| 12 | + | [lints] | |
| 13 | + | workspace = true | |
| 14 | + | ||
| 15 | + | [dependencies] | |
| 16 | + | ||
| 17 | + | [build-dependencies] | |
| 18 | + | # The house font pipeline. A git dependency rather than a vendored binary: the | |
| 19 | + | # face rebuilds from a checkout, which is the rule that keeps the glyph set and | |
| 20 | + | # the face from becoming two sources of truth. | |
| 21 | + | quasi-type = { git = "https://makenot.work/git/max/quasi-type.git", version = "0.1" } |