533 Commits

Author SHA1 Message Date
minjaesong
a5572b1d95 final touch 2026-03-05 21:14:57 +09:00
minjaesong
0f9fbe9713 glyph touchups 2026-03-05 20:49:10 +09:00
minjaesong
f2bc61928b keming calculator 2026-03-05 20:38:00 +09:00
minjaesong
0b730c7a47 support for symbols for legacy computing (unicode 17) 2026-03-05 17:54:24 +09:00
minjaesong
0afbfdf043 better proportion for uni20C1 2026-03-05 17:32:21 +09:00
minjaesong
12629ee3e8 bump for Unicode 17 2026-03-05 17:20:51 +09:00
minjaesong
99c6ed5c8c added control pictures unicode block 2026-03-05 11:22:50 +09:00
minjaesong
f7ffeec0e2 licence update 2026-03-05 10:49:48 +09:00
minjaesong
b106e1c1b0 coloured font with COLRv0 2026-03-04 20:44:16 +09:00
minjaesong
ec911e568d more fixes 2026-03-04 18:17:04 +09:00
minjaesong
68873c8d80 finally "fixed" well enough 2026-03-04 14:07:12 +09:00
minjaesong
fed73338e2 aaaaaaaaanusvara 2026-03-04 07:58:16 +09:00
minjaesong
da59fe24d4 anusvara positioning fixed for DirectWrite and CoreText but now broken for HarfBuzz 2026-03-04 07:00:14 +09:00
minjaesong
cb2f432479 fix: Armenian lowercase ho had lone misplaced dot on spritesheet 2026-03-03 19:33:16 +09:00
minjaesong
bc2dbf8b69 more attempts at cross system compatibility 2026-03-02 21:40:45 +09:00
minjaesong
3e79181aa3 somewhat working in CoreText, not at all in DirectWrite 2026-03-02 21:03:02 +09:00
minjaesong
73c2b6986d waaaaaaaaaa 2026-03-02 16:48:19 +09:00
minjaesong
f4573536e4 actually writing metadata that Windows likes 2026-03-02 15:23:09 +09:00
minjaesong
c8b197ec01 fix: hangul filler not working on OTF 2026-03-02 14:12:43 +09:00
minjaesong
c299aa8d50 fix: mixed direction diacritics stacking up wrongly 2026-03-02 13:53:37 +09:00
minjaesong
673ca100d4 builder now has opentype sanitiser 2026-03-02 13:03:53 +09:00
minjaesong
db327d8357 WOFF builder 2026-03-02 12:35:50 +09:00
minjaesong
cce9d62bd1 fix: diacritics not stacking 2026-03-02 10:07:31 +09:00
minjaesong
b3acbf1c0e old hangul composition 2026-03-02 07:21:28 +09:00
minjaesong
602923f5bc intonation marks 2026-03-02 06:04:34 +09:00
minjaesong
714cca79be tamil ii-matra adjustments 2026-03-02 05:28:11 +09:00
minjaesong
83303603c0 OTF diacritics positioning with nudging 2026-03-02 05:18:42 +09:00
minjaesong
e2550b6ef6 diacritics anchoring reads isLowheight now 2026-03-01 16:14:50 +09:00
minjaesong
a69aee9aa7 anusvara handling reworked 2026-03-01 15:10:44 +09:00
minjaesong
5c6da36fa8 fix: tamil uu-matra is disjoined by one pixel 2026-03-01 14:23:36 +09:00
minjaesong
3c9bc38dfd more minor changes 2026-03-01 14:07:47 +09:00
minjaesong
0811971a8e minor cyrillic changes 2026-03-01 11:31:33 +09:00
minjaesong
b0391e5d80 a hack was added 2026-03-01 10:59:44 +09:00
minjaesong
95fafe51a9 contextual devanagari anusvara positioning 2026-03-01 10:46:39 +09:00
minjaesong
b78b4711fb marwari dd conjuncts 2026-02-28 12:22:19 +09:00
minjaesong
35d4d94818 wrong reph_super_complex fix 2026-02-28 10:51:08 +09:00
minjaesong
7c788eb9d8 diacritics pos for glyphs sans explicit anchor tags 2026-02-28 05:55:09 +09:00
minjaesong
23e748cc88 tamil vowel positioning fix 2026-02-28 05:30:46 +09:00
minjaesong
5d10bdb8e8 improved visarga handling 2026-02-27 10:59:42 +09:00
minjaesong
95912acc32 contextual visarga 2026-02-27 08:26:19 +09:00
minjaesong
e3a3079fb2 cjk baseline fix 2026-02-27 05:25:25 +09:00
minjaesong
3e3e20e5d4 fix: stack-down diacritic with nudge-Y values for both Kotlin and OTF 2026-02-26 11:19:21 +09:00
minjaesong
f55f90352b eyelash ra 2026-02-26 10:44:21 +09:00
minjaesong
80b67a3886 shla conjunct 2026-02-26 10:24:31 +09:00
minjaesong
982fb94828 more devanagari obscurities 2026-02-26 10:15:23 +09:00
minjaesong
be1c8e2f79 Devanagari RA-appended half forms 2026-02-26 09:10:41 +09:00
minjaesong
08d1b41cc0 slightly better fi fl ligs 2026-02-26 06:12:17 +09:00
minjaesong
3f9f5fb679 more visually balanced Jya conjunct 2026-02-26 05:52:40 +09:00
minjaesong
a2a73128e0 nudge-y fixed 2026-02-26 05:25:32 +09:00
minjaesong
488304b7b4 trigraphs with reph but wip 2026-02-26 05:10:20 +09:00
minjaesong
b73aa76285 nudge-x 2026-02-26 04:31:07 +09:00
minjaesong
f38cd8f4da adding missing sirorekha for visarga 2026-02-25 05:54:16 +09:00
minjaesong
a567b9f7fc CLAUDE.md 2026-02-25 03:13:06 +09:00
minjaesong
86699af92d fix: devanagari candrabindu and anusvara off by one pixel 2026-02-25 03:10:43 +09:00
minjaesong
cdc3499f38 fix: devanagari SHA has wrong anchor point 2026-02-25 02:46:23 +09:00
minjaesong
fca02f1a3d otf more deva 2026-02-24 21:15:56 +09:00
minjaesong
73fcd7d922 otf deva complex combi 2026-02-24 09:57:43 +09:00
minjaesong
1d6eb7b2c8 otf wip5 2026-02-24 07:25:55 +09:00
minjaesong
d94bac6186 otf wip4 2026-02-24 06:01:24 +09:00
minjaesong
63adbba1bb otf wip3 2026-02-24 04:29:11 +09:00
minjaesong
8d1e669a93 otf wip2 2026-02-24 02:32:49 +09:00
minjaesong
949b6aa777 otf wip 2026-02-23 19:32:25 +09:00
minjaesong
5e2cacd491 TTF build using fontforge 2026-02-23 18:32:03 +09:00
minjaesong
208466bbb2 bitsnpicas probably not decent 2026-02-23 11:18:09 +09:00
minjaesong
b5f01a4d41 why are you still looking for tga.gz 2026-02-20 10:45:03 +09:00
minjaesong
e7afe0135e moving assets inside classpath 2026-02-19 09:17:20 +09:00
minjaesong
e3904790dc hangul: minor legibility improvements 2025-08-03 03:10:50 +09:00
minjaesong
0c3a73c2f9 symbols for legacy computing wip 2025-01-19 19:51:49 +09:00
minjaesong
648f3ffadd pua: added ESC keycap 2025-01-19 19:50:57 +09:00
minjaesong
2dc148116e minor improvements 2024-10-01 22:37:33 +09:00
minjaesong
069b1c853d more demotext changes 2024-10-01 15:00:16 +09:00
minjaesong
d5b912a64e slightly better demo text 2024-09-30 19:45:29 +09:00
minjaesong
07a4988f5f fix: no whitespaces not being committed on-time 2024-09-30 19:22:11 +09:00
minjaesong
13ef2f586e hentaigana update (2) 2024-09-24 01:54:26 +09:00
minjaesong
5882ddd974 hentaigana update 2024-09-24 01:47:05 +09:00
minjaesong
2000438c16 feature-complete Hentaigana/fix: typesetter infinete loop on unbreakable wider-than-paper chunk 2024-09-22 00:00:21 +09:00
minjaesong
bdb4e9f03f Alphanumeric Presentation Forms 2024-09-21 19:18:21 +09:00
minjaesong
3e395defb5 Unicode 16 update for Control Pictures 2024-09-21 19:17:45 +09:00
minjaesong
71371e147b fix: characters not on overriden charset would not print 2024-09-12 23:37:02 +09:00
minjaesong
fbc8aef956 more hentaigana wip 2024-06-10 01:40:06 +09:00
minjaesong
32bdc98de5 more hentaigana wip 2024-06-08 03:36:06 +09:00
minjaesong
1614260ce7 ligatures only available when interchar=0 2024-06-02 21:20:48 +09:00
minjaesong
fb60edb9a4 typesetting: minute visual adjustments 2024-06-01 16:53:31 +09:00
minjaesong
47eae57418 fi fl ligatures 2024-06-01 01:27:41 +09:00
minjaesong
2a15785f57 better interchar handling for MovableType 2024-05-23 14:26:22 +09:00
minjaesong
95dfcb91b5 better interchar handling 2024-05-23 14:04:42 +09:00
minjaesong
033fcab9ec dynamic badness 2024-05-22 14:03:01 +09:00
minjaesong
b7521ccabf more whitespaces for typesetting 2024-05-22 01:04:25 +09:00
minjaesong
3cc397d2db make SHYs work again 2024-05-22 00:36:22 +09:00
minjaesong
1f37ee1f4f fix: larger scale font whitespaces 2024-05-21 23:21:24 +09:00
minjaesong
2205e1bc44 space is 5px again but for MovableType only 2024-05-21 17:09:48 +09:00
minjaesong
3500f17e08 fix: quirks with getting width of blocks and typesetting 2024-05-21 16:47:06 +09:00
minjaesong
7c8a1be3e5 double story miniscule g 2024-05-20 03:37:41 +09:00
minjaesong
39ff4cd92c Deterministic randomiser because Timsort was complaining 2024-05-20 00:55:37 +09:00
minjaesong
2c10407c17 zwsp and thsp are now recognised as whitespaces 2024-05-19 00:46:30 +09:00
minjaesong
1f318837e7 minor changes never end 2024-05-18 20:12:07 +09:00
minjaesong
a5fc22661c fix: block involving OBJs are not properly justified 2024-05-18 02:57:28 +09:00
minjaesong
d8dc1c8cfd CodepointSequence.toReadable() is now public 2024-05-17 23:58:50 +09:00
minjaesong
42c72a6a90 drawing text without GPU 2024-05-16 01:35:16 +09:00
minjaesong
385dc8bdff fix: bad hyphenation on Hangul because the code was always assuming left part of the 'hyphenated' text always ends with a hyphen 2024-05-14 00:34:53 +09:00
minjaesong
a58abd813f til antonym of indent is exdent 2024-05-10 02:48:04 +09:00
minjaesong
8b38e3e47d fix: bad behaviour 2024-05-10 02:05:56 +09:00
minjaesong
bd333293c4 fix: bad hyphenation calc 2024-05-10 01:44:44 +09:00
minjaesong
c415c29fb5 unindentation if the line starts with hangables 2024-05-10 00:09:10 +09:00
minjaesong
d31b5e4068 two more paragraph alignment: raggedleft and centred 2024-05-09 16:47:42 +09:00
minjaesong
e2d6d3724c fix: bad width calculation on raggedright 2024-05-09 15:59:30 +09:00
minjaesong
48339b0599 hyphens are now hangable 2024-05-09 03:45:11 +09:00
minjaesong
0dade179d8 fix: nbsp not typesetted correctly 2024-05-05 01:46:13 +09:00
minjaesong
fb4cfb6e6d raggedright typesetting 2024-05-04 02:50:00 +09:00
minjaesong
06fcffde56 commenting out debug prints 2024-05-03 16:11:44 +09:00
minjaesong
accbecfb3e new charset override 'codestyle ascii' 2024-05-03 16:09:18 +09:00
minjaesong
b3e6f61f96 commenting out debug prints 2024-05-03 03:15:27 +09:00
minjaesong
c2b491cc8d more fair penaltying 2024-05-03 03:08:51 +09:00
minjaesong
74695f519b PUA chart update 2024-05-02 20:26:31 +09:00
minjaesong
adcf84140c OBJ has no glyph and has zero width now 2024-05-02 18:18:24 +09:00
minjaesong
b7ecfd910e fix: wide glue with size multiple of 16 would cause issues 2024-05-02 17:47:58 +09:00
minjaesong
a719d872c2 fix: font scaling and interchar would cause typesetting to fail 2024-05-01 02:08:38 +09:00
minjaesong
2d99f39a99 fix: colour code gets broken up by the hyphenation 2024-05-01 00:46:06 +09:00
minjaesong
d43a2d5bec more adhoc adjustments 2024-04-30 18:50:24 +09:00
minjaesong
b2acb83848 more arbitrary conditions 2024-04-30 18:32:45 +09:00
minjaesong
949187345d better penalty? 2024-04-30 18:22:54 +09:00
minjaesong
07d4504a89 testing new penalty eq 2024-04-30 18:04:47 +09:00
minjaesong
aa45f942e3 freezing slugs into a single line of text 2024-04-30 16:39:32 +09:00
minjaesong
7384d970d5 better-behaving hyphenation penalty 2024-04-30 04:59:08 +09:00
minjaesong
8e29db9c4a smarter hyphenation 2024-04-29 16:47:11 +09:00
minjaesong
8bf0642b3f fix: space before hyphen gets ignored 2024-04-29 01:40:02 +09:00
minjaesong
87492c1f0b better text scaling support 2024-04-28 22:25:00 +09:00
minjaesong
2196a4b2e7 faster typesetting by not creating glyph caches 2024-04-28 21:18:44 +09:00
minjaesong
ee51cafa6c hyphnation rules for other scripts 2024-04-28 19:41:19 +09:00
minjaesong
57ce0363f2 better tokenising for quots 2024-04-28 18:15:05 +09:00
minjaesong
77e01151b5 fix: poor handling of pre-hyphenated words 2024-04-28 17:52:41 +09:00
minjaesong
869d424292 fix: hyphenated slug would not contract 2024-04-28 17:33:37 +09:00
minjaesong
631e60bb73 hangable colon and semicolon 2024-04-28 16:24:00 +09:00
minjaesong
6410256e6a a space is narrower 4px; tokenising CamelCase 2024-04-28 14:28:17 +09:00
minjaesong
66c1a1357a control chars for movabletype 2024-04-28 01:18:21 +09:00
minjaesong
d87b0dce7c scale support for MovableType 2024-04-27 16:02:22 +09:00
minjaesong
c695a9c5f0 more bug fixes 2024-04-26 01:30:23 +09:00
minjaesong
6c63504497 more tokeniser fix 2024-04-25 21:20:45 +09:00
minjaesong
8fd3f0e99a more hentaigana wip 2024-04-08 00:59:09 +09:00
minjaesong
373af27873 better tokenising for mixed scripts (latin with numerals, korean with latin) 2024-04-06 22:23:14 +09:00
minjaesong
30327776ba typesetter: extra draw call by line number 2024-04-04 02:45:24 +09:00
minjaesong
32c859fdf8 tokenising pre-hyphen-ated words 2024-04-03 14:49:03 +09:00
minjaesong
06782fdc55 fixing quirks for more writing systems 2024-03-31 00:13:31 +09:00
minjaesong
46a57e8e50 thai text linebreaking fix 2024-03-31 00:02:19 +09:00
minjaesong
78333b3fe1 slug moving will ignore glues at line head 2024-03-30 22:14:55 +09:00
minjaesong
35325cb852 cjkpunct is now variable 2024-03-30 21:46:48 +09:00
minjaesong
1bf78cc5bb cjkpunct adjustments 2024-03-30 20:20:52 +09:00
minjaesong
5f1f740fab cjkpunct update 2024-03-30 19:57:21 +09:00
minjaesong
e9c9ae4aab better control chars handling 2024-03-30 17:00:13 +09:00
minjaesong
09b3a8c759 fix: tokeniser fails on the line starts with indentation then colour codes 2024-03-30 05:58:01 +09:00
minjaesong
cfc52386ab almost working... 2024-03-30 05:55:22 +09:00
minjaesong
c9474fac1c tokeniser: japanese small kana coalesced 2024-03-30 01:28:46 +09:00
minjaesong
9e85cae502 kana update 2024-03-30 01:16:11 +09:00
minjaesong
eacbd4fdb0 tokeniser changes 2024-03-29 23:58:58 +09:00
minjaesong
258f70dbf4 new tokeniser 2024-03-29 23:43:58 +09:00
minjaesong
f69b39df95 typesetter wip 2024-03-29 17:38:00 +09:00
minjaesong
9b554d3524 glyphs update 2024-03-29 17:37:49 +09:00
minjaesong
d6ba2eb7ce symbols sheet update 2024-03-28 21:06:48 +09:00
minjaesong
a9883fc029 more kana update 2024-03-28 19:08:23 +09:00
minjaesong
0ebbcfb8c4 control chars and colour codes 2024-03-26 02:14:58 +09:00
minjaesong
4d04f368c6 word split rule for Korean 2024-03-25 20:34:48 +09:00
minjaesong
ab04e6b735 better hyphenation 2024-03-25 20:00:22 +09:00
minjaesong
c1b7d3e036 hyphenation wip 2024-03-25 18:43:08 +09:00
minjaesong
f60454229b new tightening penalising eq 2024-03-25 16:48:53 +09:00
minjaesong
bba02d9f40 adding fullwidth comma/period to the symbol table 2024-03-25 16:34:13 +09:00
minjaesong
1a20283bb3 kerning rule for rs and dots 2024-03-25 16:23:36 +09:00
minjaesong
c644d4ad1a test materials 2024-03-25 16:03:08 +09:00
minjaesong
b9e0366512 Chinese/Japanese typesetting works in a way that it won't crash the program 2024-03-25 16:01:32 +09:00
minjaesong
e81b9f4e53 various fixes 2024-03-25 03:32:16 +09:00
minjaesong
67481f9e44 various fixes 2024-03-25 03:29:51 +09:00
minjaesong
95f42a9126 fix: test window rendering quirks 2024-03-25 03:06:28 +09:00
minjaesong
84ea79b069 justified typesetting wip 2024-03-25 02:54:21 +09:00
minjaesong
3f1364c982 more hentaigana wip 2024-03-12 21:06:46 +09:00
minjaesong
28b902b6c0 more hentaigana wip 2024-03-03 20:52:14 +09:00
minjaesong
a19ce0d833 README update 2024-03-02 14:53:45 +09:00
minjaesong
b042af1881 update for indian rupee sign 2024-03-02 14:32:54 +09:00
minjaesong
1d57958db9 more wip 2024-02-29 17:13:30 +09:00
minjaesong
a9d28510a7 minor changes 2024-02-26 22:03:06 +09:00
minjaesong
b944cf3585 more hentaigana 2024-02-26 21:03:14 +09:00
minjaesong
de23edcbda more hentaigana 2024-02-26 01:54:46 +09:00
minjaesong
68382370a4 minor edits 2024-02-24 21:12:09 +09:00
minjaesong
29c7c20844 more smol update 2024-02-23 03:18:55 +09:00
minjaesong
7037a758cf more kana update 2024-02-23 00:57:38 +09:00
minjaesong
75a3bb2198 new kana for testing 2024-02-22 21:43:18 +09:00
minjaesong
473b34dc7c experimental 2024-02-22 20:34:31 +09:00
minjaesong
6f88e37df6 new kana more wip 2024-02-22 18:56:28 +09:00
minjaesong
4ca39b3742 wip 2024-02-22 14:37:45 +09:00
minjaesong
3cc44d4421 new kana design wip 2024-02-22 03:56:25 +09:00
minjaesong
0ea7bf8c1b Update LICENSE.md 2024-02-07 01:53:56 +09:00
minjaesong
f5bc1f8178 more updates 2024-02-05 22:12:56 +09:00
minjaesong
2a39ff0d35 copyright update 2024-02-05 04:18:31 +09:00
minjaesong
ecffe22d01 more patches 2024-02-05 03:23:19 +09:00
minjaesong
e1c48ace6b a late night patch, yay 2024-02-05 03:00:31 +09:00
minjaesong
efff35f0e3 more devanagari changes 2024-02-04 19:23:23 +09:00
minjaesong
f10962ceda more devanagari changes 2024-02-04 19:12:24 +09:00
minjaesong
37d21c6f6c unicode 15.1 update 2024-02-04 15:38:19 +09:00
minjaesong
ff7c0f8df3 yet another devanagari improvements regarding superscript RA and vowel I 2024-02-02 20:38:16 +09:00
minjaesong
c863148f13 fix: positioning of diacritics after hindi II; positioning of superscript RA in sample string DARJEELING 2024-01-28 22:48:41 +09:00
minjaesong
69e12f0205 hangul changes 2024-01-28 16:42:00 +09:00
minjaesong
6bf4a5a995 Old hangul rendering fix 2024-01-19 16:01:51 +09:00
minjaesong
38bdb28b64 next release commit 2023-10-06 12:20:13 +09:00
minjaesong
8ba1a4a005 fix: TAMIL AS ABOVE SIGN being shifted 1 px up 2023-07-06 22:06:59 +09:00
minjaesong
70da6a5142 README update 2023-06-06 19:56:33 +09:00
minjaesong
2f6126d0ea texts now printed 2px lower so that the text is vertically centred against the lineheight 2023-06-06 18:07:14 +09:00
minjaesong
189a427edc sundanese update as per unicode 15 2023-01-09 15:14:48 +09:00
minjaesong
21dbf348d0 redesigned capital cyrillic DA again 2022-09-28 17:53:03 +09:00
minjaesong
ff455ef2fd fixed a bad shape mask of the bulgarian lowercase te, mask of the uppercase scha was 1 px off to the left 2022-09-28 13:13:37 +09:00
minjaesong
d7029c3841 halfwidth hangul minor change 2022-09-04 21:57:51 +09:00
minjaesong
2d894613df new typeface for the experimental typewriter 2022-08-31 23:13:47 +09:00
minjaesong
60a1da1336 more cyrillic touchups 2022-08-18 19:39:02 +09:00
minjaesong
123d856853 forgot to make tga of these files 2022-08-03 00:55:42 +09:00
minjaesong
617e0bc5b1 fixed a bug where cyrillic 'ь' was tagged as 1 px narrower than it should be 2022-08-03 00:41:42 +09:00
minjaesong
0a873b3801 fixed a bug where glyphs in alternative charcter sets (namele bulgarian and serbian) would not override the glyph props of the base glyphs 2022-08-02 22:54:18 +09:00
minjaesong
3c97253c9a minor edit 2022-07-18 13:04:07 +09:00
minjaesong
f228c331cf keycap: added ctrl and alt 2022-07-01 02:13:40 +09:00
minjaesong
464ac151bd more work files that is not a goal of v1.11 release 2022-05-24 09:40:04 +09:00
minjaesong
8f70306540 cyrillic archaeic letters wip 2022-04-26 00:23:58 +09:00
minjaesong
28166bb2ec (in a Crocodile Dundee vibe) that's not a fix; this is a fix 2022-03-22 17:07:38 +09:00
minjaesong
ac85217998 fixed a bug where diacritics with align right would not get positioned correctly 2022-03-22 15:47:24 +09:00
minjaesong
44b2450fbf setting default line height to 24 pixels on the source 2022-03-22 09:29:38 +09:00
minjaesong
9221373c53 removing single-glyph devanagary DRYA ligature 2022-03-20 18:34:51 +09:00
minjaesong
bfaa190e49 another devanagari changes 2022-03-19 10:42:00 +09:00
minjaesong
23c7ae15a2 more variable devanagari i/ii 2022-03-17 15:37:31 +09:00
minjaesong
077a832767 variable length for devanagari i/ii 2022-03-14 21:42:28 +09:00
minjaesong
8c9e912491 devanagari: five more sanskrit ligatures 2022-03-13 17:05:03 +09:00
minjaesong
8a7cf85982 an alternative sequence to write Tamil SHRII 2022-03-10 16:23:14 +09:00
minjaesong
bae9af79e9 devanagari NG.K/KH/G/GH ligatures 2022-03-09 13:22:18 +09:00
minjaesong
374dc1dd66 sundanese kerning 2022-03-08 13:06:08 +09:00
minjaesong
2812460a9c devanagari nuqta 2022-03-08 11:05:31 +09:00
minjaesong
83a885d214 sundanese on demo.png (2) 2022-03-07 18:43:57 +09:00
minjaesong
0dbb32b575 sundanese on demo.png 2022-03-07 18:27:23 +09:00
minjaesong
4fea10583c added sundanese script 2022-03-07 16:11:37 +09:00
minjaesong
ebdb0c499c fixed a bug where standalone verb II would not rendered correctly 2022-03-06 12:57:27 +09:00
minjaesong
4e6f473b6f devanagary jya ligature 2022-03-02 15:09:24 +09:00
minjaesong
7fdb7a7a91 devanagari: improved rendering of superscript RA 2022-02-25 13:39:00 +09:00
minjaesong
f8a1c4cfdf dumping temp files to OS's temp directory 2022-02-23 09:20:50 +09:00
minjaesong
afa59255cc fixed a bug where getting the width of certain text would return a bad value 2022-02-17 14:20:21 +09:00
minjaesong
23a5a8997f some extra padding on the 'linotype' and improved method of getting text width 2022-02-17 11:36:14 +09:00
minjaesong
4e705e5c7d more testing with marathi ra 2022-02-16 13:58:28 +09:00
minjaesong
c57f8e191b devanagari ligature update 2022-02-16 13:19:03 +09:00
minjaesong
ed97bafc41 pua symbols update 2022-02-14 13:43:36 +09:00
minjaesong
eebff767ce devanagari ligatures and glyph adjustments 2022-02-14 10:39:54 +09:00
minjaesong
baee9a62aa new sanskrit sample text 2022-02-13 23:27:43 +09:00
minjaesong
305dbb548c advertisement for braille support 2022-02-13 22:40:32 +09:00
minjaesong
0ccdf67f1e width fix for cyrillic g 2022-02-13 22:20:54 +09:00
minjaesong
60aad1b24d update demo.PNG 2022-02-13 22:12:34 +09:00
minjaesong
e31e088744 another redesign oof 2022-02-13 22:06:36 +09:00
minjaesong
824f11412d devanagari HA redesign 2022-02-13 18:22:38 +09:00
minjaesong
1445404005 fixed a bad RAsup positioning (hopefully) 2022-02-13 18:13:31 +09:00
minjaesong
dd851cdbc9 more ligatures 2022-02-13 16:29:49 +09:00
minjaesong
7abfb2d2f0 apparently there are lots to adjust 2022-02-13 15:06:26 +09:00
minjaesong
5e7237fb17 fixed a bug where open YA is not being applied on complex conjuncts 2022-02-13 13:31:44 +09:00
minjaesong
0cba7b96be deploying new devanagari internal mapping 2022-02-13 12:24:57 +09:00
minjaesong
1705b2752e devanagari: using new internal mapping 2022-02-13 01:22:00 +09:00
minjaesong
4614ffab92 braille support 2022-02-07 13:38:22 +09:00
minjaesong
225d908f70 fixed the demo program that it would blend the text shadow correctly on the PNG output so it would look as good as the in-game 2022-02-02 20:21:06 +09:00
minjaesong
09e7483389 w 2022-02-02 12:12:21 +09:00
minjaesong
57ab00b6e1 devanagari glyph improvements and new encoding scheme wip 2022-01-30 19:57:59 +09:00
minjaesong
23b6be74e2 devanagari RAsup anomaly fix 2022-01-29 21:35:18 +09:00
minjaesong
905a235a1b neverending adjustments 2022-01-29 12:08:16 +09:00
minjaesong
d6b1dbd1d6 more devanagari adjustments 2022-01-29 00:34:06 +09:00
minjaesong
e9c4f0723a tamil vowel I ligatures 2022-01-27 20:25:19 +09:00
minjaesong
ac6733469c doc 2022-01-27 09:37:59 +09:00
minjaesong
a7ecba905a tamil: diacritics for extrawide is not quite working but this quick fix would work well enough 2022-01-26 18:33:06 +09:00
minjaesong
d7c48b1f88 tamil wip 2022-01-26 17:49:39 +09:00
minjaesong
0c74660396 fixing one pixel mistake 2022-01-26 15:03:28 +09:00
minjaesong
b3c1327dc4 IPA intonation graph implementation 2022-01-26 14:41:54 +09:00
minjaesong
489da04d1d devanagari: fixed the RAsup positioning 2022-01-26 14:17:23 +09:00
minjaesong
a58ad756ff devanagari narrower vowels 2022-01-26 10:09:02 +09:00
minjaesong
702f1b2e2f neverending adjustments 2022-01-25 23:28:20 +09:00
minjaesong
f10413d9c4 dot position adj 2022-01-25 21:41:44 +09:00
minjaesong
80aa9d0e52 more adjustments 2022-01-25 21:21:32 +09:00
minjaesong
64c13571f9 N.T and N.T.R ligatures 2022-01-25 21:10:08 +09:00
minjaesong
a36c5df477 oops not quite 2022-01-25 19:40:23 +09:00
minjaesong
7c3069e8cf i think devanagari is fully working now 2022-01-25 16:23:02 +09:00
minjaesong
1e66cfec51 marwari dda ligatures 2022-01-25 12:01:14 +09:00
minjaesong
1e0a820817 glyph adjustments 2022-01-25 10:24:23 +09:00
minjaesong
4623975858 more ligatures with RA and their half forms 2022-01-25 10:12:06 +09:00
minjaesong
23aac0c1c1 I think I've nailed down the RAsup rendering 2022-01-24 17:34:11 +09:00
minjaesong
54d983013a using open-YA for K.YA ligature 2022-01-23 20:23:56 +09:00
minjaesong
c7507a6357 another glyph changes 2022-01-23 11:31:50 +09:00
minjaesong
0714581866 diacritics placeholder is now semitransparent 2022-01-22 11:18:24 +09:00
minjaesong
095b8e65fb glyph adjustments 2022-01-21 12:38:36 +09:00
minjaesong
e60652d705 ha ha ha 2022-01-20 15:55:56 +09:00
minjaesong
f3aeba3d70 eh whateves 2022-01-20 14:47:21 +09:00
minjaesong
ee2b1027b7 moaarrrr ligggggsssss 2022-01-19 20:26:56 +09:00
minjaesong
3b05624de5 these glyphs are killing me :( 2022-01-19 16:50:29 +09:00
minjaesong
4c0582184f moar ligs 2022-01-19 14:20:56 +09:00
minjaesong
5e6b2363b7 devanagari ru-like syllables 2022-01-19 11:41:02 +09:00
minjaesong
dbe93a26eb more consistent K.SSA ligatures 2022-01-19 11:23:05 +09:00
minjaesong
b128312e2c wippie 2022-01-19 11:16:28 +09:00
minjaesong
07ca429483 more devanagari ligatures 2022-01-19 10:20:42 +09:00
minjaesong
9675c40216 devanagari ligation wip 2022-01-18 17:56:15 +09:00
minjaesong
0870856674 wip?? 2022-01-15 17:57:53 +09:00
minjaesong
3c6f18efbf more devanagari glyphs 2022-01-15 15:55:59 +09:00
minjaesong
2fdc6f85f5 devanagari eyelash RA 2022-01-14 17:59:36 +09:00
minjaesong
4ed2c26b35 new paper size 2022-01-11 18:01:05 +09:00
minjaesong
3ef7410e45 devanagari and bengali are separated internally 2022-01-11 16:47:08 +09:00
minjaesong
ba730fafe3 new devanagari wip 2022-01-10 15:53:02 +09:00
minjaesong
ab925bc8c5 fixing my stupid mistake 2022-01-05 17:20:40 +09:00
minjaesong
b1362f3a3b hangul: giyeok accompanied by UU or EU will have slightly curved tail 2022-01-05 16:03:33 +09:00
minjaesong
f60eb6b3fc hangul redraw 2022-01-05 14:47:06 +09:00
minjaesong
88f013a304 testing texts 2022-01-02 17:14:15 +09:00
minjaesong
ea9f8fae4c tamil ligature kssa and shrii 2022-01-02 17:13:39 +09:00
minjaesong
a6363131ac tamil almost working...? 2022-01-02 16:53:40 +09:00
minjaesong
fc60356b8b larger Anusvara for Bengali script 2022-01-02 14:01:20 +09:00
minjaesong
755420d5b3 control picture update to use some of the ISO 2047 symbols 2022-01-02 12:58:08 +09:00
minjaesong
0200aa5f85 Unicode Control Pictures 2022-01-02 11:43:58 +09:00
CuriousTorvald
7dff623b75 Update CONTRIBUTING.md 2022-01-02 10:54:16 +09:00
minjaesong
49f5e649b7 Making FontDemoGDX release-able again 2022-01-02 10:06:48 +09:00
minjaesong
1656e50c3b new year new copyright date 2022-01-01 22:06:26 +09:00
minjaesong
99a1648734 some edits 2022-01-01 19:12:25 +09:00
minjaesong
9672aa8e05 PUA realloc; tamil ligatures 2021-12-29 13:50:55 +09:00
minjaesong
d72ecb5cfd minor fix 2021-12-28 20:11:54 +09:00
minjaesong
39fccbe6cb some random miscellaneous work 2021-12-28 17:43:26 +09:00
minjaesong
84efb253cf testification 2021-12-26 17:41:52 +09:00
minjaesong
4c63860dc1 amendments 2021-12-21 15:26:44 +09:00
minjaesong
36acc7e523 more doc thingy 2021-12-21 14:56:11 +09:00
minjaesong
700397a995 more fixes for diacritics system 2021-12-21 14:05:25 +09:00
minjaesong
dc6bd89497 expanding diacritics types to correctly implement thai diacritics 2021-12-21 12:01:48 +09:00
minjaesong
c2ed83d511 halfwidth ㅆ had bad replacement chars defined 2021-12-20 22:55:33 +09:00
minjaesong
0a41da1659 minor changes 2021-12-20 01:51:15 +09:00
minjaesong
96e412414a slight changes in glyphs 2021-12-18 11:53:08 +09:00
minjaesong
483476b5fa halfwidth and fullwidth forms 2021-12-17 15:19:36 +09:00
minjaesong
ed8bcf7dc8 icon for mouse drag 2021-12-16 16:58:40 +09:00
minjaesong
376c7cf9a4 phonetic extensions 2021-12-14 17:16:13 +09:00
minjaesong
05d34d563d removing global flipY key 2021-12-13 02:52:45 +09:00
minjaesong
e59db6954a fixing wrong kerning tag for 'y'-like characters 2021-12-12 19:32:18 +09:00
minjaesong
9c05fb399b minute thai kerning change 2021-12-12 13:58:49 +09:00
minjaesong
40d4bc41c6 finally working on phonetic extensions 2021-12-09 17:38:11 +09:00
minjaesong
1f9a79db48 Enclosed Alphanumeric Supplement 2021-12-08 15:05:16 +09:00
minjaesong
0943d38926 implementation of "replacewith" compiler directive 2021-12-08 11:12:05 +09:00
minjaesong
5469b56841 minor changes on fraktur letters 2021-12-08 09:32:54 +09:00
minjaesong
d31730b5c3 arts for letterlike_symbols 2021-12-07 15:21:23 +09:00
minjaesong
bd36ee7399 wip 2021-12-06 09:14:15 +09:00
minjaesong
b8f5f57878 more fraktur 2021-12-05 22:04:13 +09:00
minjaesong
63de646eae font wip 2021-12-05 17:50:04 +09:00
minjaesong
e70ee44a37 fixing yet another bug on thai diacritics 2021-12-04 17:36:50 +09:00
minjaesong
6ee66fdccc tsalagi kerning 2021-11-30 14:47:40 +09:00
minjaesong
5ecffa1352 hayeren kerning 2021-11-30 13:04:03 +09:00
minjaesong
46efc42e24 kartuli kerning 2021-11-30 12:41:01 +09:00
minjaesong
35829ce982 greek and insular kerning 2021-11-30 12:21:58 +09:00
minjaesong
756ef4fae5 cyrillic kerning 2021-11-30 11:14:47 +09:00
minjaesong
ce31a5a6ef making use of the long-neglected scale variable 2021-11-28 12:42:34 +09:00
minjaesong
14ea4d8d84 thai kerning 2021-11-27 17:59:20 +09:00
minjaesong
f2eb1464f6 kerning ipa-ext 2021-11-27 11:56:06 +09:00
minjaesong
45b464ee13 hopefully alleviating a bug involving multithreaded (or mixed thread?) usage 2021-11-26 21:06:05 +09:00
minjaesong
c3f9ea47fd fixed a bug where shift+space and shift+backspace would play wrong sound 2021-11-26 17:10:18 +09:00
minjaesong
87012d7671 typewriter: faster typing sound, backspace is working again 2021-11-26 16:58:54 +09:00
minjaesong
1fce4055d7 some sort of an intro for the typewriter 2021-11-26 16:18:13 +09:00
minjaesong
b83dd501fc typewriting sound on typewriter 2021-11-26 15:27:09 +09:00
minjaesong
94a40a4a87 diacritics bit is now colour-coded 2021-11-26 10:59:30 +09:00
minjaesong
2fa867ce44 width adjustment for qwerty typewriter 2021-11-25 17:16:49 +09:00
minjaesong
4d1a599263 typewriter accepting new tagging system 2021-11-25 17:03:11 +09:00
minjaesong
8641c95169 new tagging system 2021-11-25 16:46:22 +09:00
minjaesong
4f6584ac27 minor change in kerning rule 2021-11-25 13:44:18 +09:00
minjaesong
cb25c5bf56 hangul typewriter: more faithful to the machine i'm copying 2021-11-25 11:48:45 +09:00
minjaesong
5c534ed388 glyph control bits spec change 2021-11-25 10:31:16 +09:00
minjaesong
368bf0ee15 typewriter hangul: trying fixed 7 px width... 2021-11-25 09:20:45 +09:00
minjaesong
c9ccf3e7f8 punctuations kerning 2021-11-24 17:57:17 +09:00
minjaesong
0ced94cb57 0000FC for superscripts! 2021-11-24 17:44:44 +09:00
minjaesong
b158e11e25 extd kerned 2021-11-24 17:35:41 +09:00
minjaesong
f4658daa9a extc 2021-11-24 16:19:34 +09:00
minjaesong
56b4ccb848 typewriter minor adjustments 2021-11-24 15:52:19 +09:00
minjaesong
2748eeb367 typewriter keymap minor change 2021-11-24 13:32:09 +09:00
minjaesong
8647578802 wtf is this char??? 2021-11-24 10:05:53 +09:00
minjaesong
3422c20322 latin ext-b updated for unicode 14.0 2021-11-24 09:52:54 +09:00
minjaesong
4561b06428 latin ext-d updated for unicode 14.0 2021-11-24 09:49:16 +09:00
minjaesong
652f239af3 extb 2021-11-21 22:37:14 +09:00
minjaesong
c2428ff7c2 extb wip 2021-11-21 17:46:04 +09:00
minjaesong
2d5e592622 kerning data for ascii,exta,extadditional 2021-11-21 16:55:34 +09:00
minjaesong
3a569a2e2e suppress debug prints if debug=false 2021-11-21 12:19:03 +09:00
minjaesong
f600764364 automatically creating symmetrical dual for kerning rules 2021-11-20 21:05:54 +09:00
minjaesong
ab2669b555 kerning for the pairs such as Pr,Fu,Fo,etc 2021-11-20 18:36:45 +09:00
minjaesong
f43a81de3f kerning bits for 0000..00ff 2021-11-20 16:37:25 +09:00
minjaesong
33ae3fa48a First working version of the Keming Machine 2021-11-20 15:46:43 +09:00
minjaesong
12fc3eee03 fixed a bug where dots on i and j were always being removed 2021-11-20 14:36:53 +09:00
minjaesong
396954e0ee extra args not needed when you've got flipY 2021-11-18 17:42:51 +09:00
minjaesong
d65aaa6da6 added a way to change diacritic shifting direction 2021-11-18 17:34:16 +09:00
minjaesong
ae67833cb2 diacritic dot removal for i and j 2021-11-18 17:18:42 +09:00
minjaesong
4261652bdf thai glyph update 2021-11-16 22:29:35 +09:00
minjaesong
49d83a297f greek F 2021-11-16 17:57:06 +09:00
minjaesong
b3a48b23ee added diacritics placeholders to PUA 2021-11-16 17:04:49 +09:00
minjaesong
b802f9b02e changed the shape of cyrillic palochka 2021-11-16 11:12:45 +09:00
minjaesong
0afdb9c2cf minor modifications on LatinExtABCD; added support for unicode Currency Symbols 2021-11-16 11:11:13 +09:00
minjaesong
c295430866 more errorOnUnknownChar flag check 2021-11-11 16:48:53 +09:00
minjaesong
883375dc9b minor modification on Ł ł and · (middot) 2021-11-09 22:32:22 +09:00
minjaesong
6a1208b45d more wip 2021-11-08 22:23:05 +09:00
minjaesong
25084b7a94 alphanumeric wip 2021-11-07 13:51:49 +09:00
minjaesong
c325c9c30c typewriter: latin alph capitals 2021-11-06 17:28:49 +09:00
minjaesong
d89409cc73 omg i think this is fimally working 2021-11-06 13:54:04 +09:00
minjaesong
0ff564a192 another minor mod 2021-11-05 23:07:35 +09:00
minjaesong
930804cf48 minor mod on Choseong HIEUH 2021-11-05 22:52:32 +09:00
minjaesong
699981d9a2 probably useable now 2021-11-05 22:47:59 +09:00
minjaesong
947c966660 typewriter font is working but not quite 2021-11-05 17:18:11 +09:00
minjaesong
554d9a5d0b will use config string this time 2021-11-04 22:44:40 +09:00
minjaesong
45e5c39739 making new font class for typewriter; renaming existing one with more sensible name 2021-11-04 21:42:00 +09:00
minjaesong
74e540cb61 future work: typewriter input on PUA 2021-11-04 17:49:02 +09:00
minjaesong
c1ec50e166 caching is back to the hashset as we don't iterate over them 2021-10-29 17:20:30 +09:00
minjaesong
58ce18f981 hopefully normalising malformed hangul IPF coding 2021-10-22 17:50:52 +09:00
minjaesong
f1fa98880f print with codepoints will try to normalise them 2021-10-22 17:24:29 +09:00
minjaesong
0c754d09ed making CodepointSequence more useful 2021-10-21 19:21:07 +09:00
minjaesong
b40e4fcc26 wtf with the premultiplying i didn't even told it to do so 2021-10-21 12:19:08 +09:00
minjaesong
a7bb33c3a0 more glyph updates 2021-10-14 20:41:52 +09:00
minjaesong
c3930b2e08 kerning set for slashes; re-doing lowercase k 2021-10-14 15:07:00 +09:00
minjaesong
979180860e flippin' options for TextureRegionPack 2021-09-21 00:26:44 +09:00
minjaesong
61fb70975c shadow opacity configuration 2021-09-18 13:42:37 +09:00
minjaesong
1af9f7c915 adjustments on hangul letters, colon, semicolon, comma and period; importing work-files from the main game to this repo 2021-09-17 10:06:30 +09:00
minjaesong
810411de7e improved caching performance; lwjgl3 for fonttest 2021-09-16 14:51:23 +09:00
minjaesong
b9bf9ca10d copyright date update 2021-09-15 23:58:45 +09:00
minjaesong
526c16daa9 more kerning pairs 2021-09-15 23:10:28 +09:00
minjaesong
e58104d5b5 fixed an overlook where charset override control codes were not actually strings (because UTF-16) but array of characters 2021-09-15 22:43:50 +09:00
minjaesong
92aeb3c8aa update to kotlin 1.4; fixed a bug where trying to colour a text using gdxbatch's colour would cause undesirable results 2021-09-13 01:21:43 +09:00
minjaesong
3841611780 1.9 release 2021-03-15 19:56:53 +09:00
minjaesong
06bc8c9e2d forgot to update demo.png 2020-10-16 15:20:07 +09:00
minjaesong
21415c6f8d add: kerning sets for b and d 2020-10-06 16:31:24 +09:00
minjaesong
22c2a7aa52 fix: issue #12 2020-10-06 15:14:57 +09:00
minjaesong
0eb181f315 time to get off so i'll just commit this 2020-09-29 18:04:58 +09:00
minjaesong
45b431f953 fix: partial fix for issue #12 2020-09-29 14:07:35 +09:00
minjaesong
bbee554fec add: error msg for illegal linotype dimension 2020-09-29 11:36:59 +09:00
minjaesong
c4c67f489e fixing idiotic priority cache impl where they won't get younger 2020-05-04 00:09:05 +09:00
minjaesong
4d867d6523 replacing personalised test text 2020-05-03 23:42:06 +09:00
minjaesong
6620c11178 hangul: minor texture changes 2020-05-03 23:28:59 +09:00
minjaesong
db06cab07c cleaning up unnecessary functions 2020-05-03 22:26:45 +09:00
minjaesong
90b11cdd97 hangul: more microkerning shit 2020-05-03 20:34:12 +09:00
minjaesong
7bb92d1b4e hangul: some of the microkerning implemented in dirty way 2020-05-03 18:13:17 +09:00
minjaesong
b5dd744bd5 hangul: WEO to use wider initials just like WI and YI 2020-05-02 00:03:50 +09:00
minjaesong
34903b10d4 more hangul improvements 2020-05-01 17:12:13 +09:00
minjaesong
0c5b7c8b70 more letters for kerning charsets 2020-04-23 02:50:03 +09:00
minjaesong
cdffca98ef even more kerning pairs i can think of WIP 2020-04-23 01:16:18 +09:00
minjaesong
ce30d1d5fd major modification on hangul font 2020-04-23 01:02:57 +09:00
minjaesong
ab067044f8 more kerning pairs i can think of WIP 2020-04-21 05:17:19 +09:00
minjaesong
3e04b8bbe6 more kerning pairs WIP 2020-04-19 15:03:37 +09:00
minjaesong
4556a9e244 very crude kerning impl for Ys and Ts and AV-VA pairs 2020-04-14 23:56:08 +09:00
minjaesong
e5d04de250 character sets to use ints rather than chars 2020-04-14 23:45:35 +09:00
minjaesong
b7df181729 minor improvement on rotund r 2020-04-14 07:25:11 +09:00
minjaesong
2fd3fcfd46 support for LatinExt-D 2020-04-14 06:41:11 +09:00
minjaesong
73251d70fa completely dropping Slick2d stuffs 2020-01-14 16:26:10 +09:00
minjaesong
971f98beb3 shadow inversion to make recessed texts 2020-01-06 15:19:07 +09:00
minjaesong
abe1da35a0 kana minor edits, fixing wrong case 2019-10-03 01:24:39 +09:00
Minjae Song
29885f3ac9 updating demo.png 2019-05-31 04:14:32 +09:00
Minjae Song
56d2a98a5b hangul update 2019-05-30 14:10:27 +09:00
Minjae Song
43342fff00 demo: updating unicode references 2019-05-14 13:33:14 +09:00
Minjae Song
51d4dec6d3 new redesigned hangul 2019-05-14 13:17:20 +09:00
Minjae Song
81dc38d242 support for Compatibility Hangul 0x3130..0x318F 2019-05-13 02:27:56 +09:00
Minjae Song
863f9d91c8 hangul redraw wip -- less "laundry line" 2019-05-12 23:51:50 +09:00
Minjae Song
4c99cca7ff internal test mode 2019-05-12 17:20:22 +09:00
Minjae Song
b807b96b5f hangul redraw and punct adjustments 2019-05-12 17:10:05 +09:00
Minjae Song
91c9a7a99c TextureRegionPack is now Gdx.disposable 2019-03-10 16:43:32 +09:00
Minjae Song
778e2b0afb tweaks on left/right bumper labels 2019-02-23 23:48:18 +09:00
Minjae Song
c9055ef7b8 PUA: LR buttons are now xbox shaped, added xbox one's View and Menu buttons 2019-02-13 17:52:45 +09:00
Minjae Song
e60b95efb8 bgBG: one dark pixel fixed on 'zhe' 2019-02-08 09:54:54 +09:00
Minjae Song
9ca70a601b fixing memory leak caused by my sloppy-ass coding 2019-02-07 17:30:15 +09:00
Minjae Song
6cacc56ea8 Update README.md 2019-02-07 03:25:59 +09:00
Minjae Song
6d142f082c readme: gitlab link -> github link 2019-01-29 23:42:20 +09:00
Minjae Song
db3b13800b Merge branch 'cpu-render-pixmap-cache' 2019-01-22 22:39:15 +09:00
Minjae Song
7fe0cc9527 copyright update 2019-01-13 04:42:14 +09:00
Minjae Song
b62fee89cb fixed getWidth() not returning correct width
getWidth() is heavier than ever now, at least it's cached
2019-01-10 20:11:36 +09:00
Minjae Song
d88443814f sheet name comment on coderange, default cache size is 64 2019-01-10 18:25:24 +09:00
Minjae Song
dfeab56bb5 Update CONTRIBUTING.md 2018-12-02 02:35:58 +09:00
Minjae Song
b623727b1a potential memory leak fixed (unbound cache size) 2018-11-03 16:35:26 +09:00
minjaesong
048b683cb2 sorta works but poor performance on wall of text 2018-09-19 02:54:07 +09:00
minjaesong
35b384af53 proper render, except there can be NO transparency in color & spritesheet 2018-09-19 00:07:32 +09:00
minjaesong
cebfa27d59 this is cursed; new shadowing method pls code 2018-09-18 01:08:28 +09:00
minjaesong
c57de3a21f we have a render (but not colour) 2018-09-17 23:10:29 +09:00
minjaesong
4ad5e47758 little diagnose print 2018-09-17 21:29:55 +09:00
Minjae Song
f06ed4529c how the fuck this does not work 2018-09-17 17:51:36 +09:00
Minjae Song
8da321bdc2 fixed bad offset but still bad render 2018-09-17 17:02:09 +09:00
Minjae Song
920b11e3e9 WIP, at least it draws something 2018-09-17 16:26:25 +09:00
minjaesong
d8dd88c6a7 aggressive cacheing (ShittyGlyphLayout) 2018-09-16 18:51:11 +09:00
minjaesong
ba28b6d10c improved shadowing perf at the cost of reduced quality
black text on white background should work now
2018-09-16 16:08:34 +09:00
minjaesong
50ae1789bf this should fix issue #11
If performance is not a concern (which is WIP)
2018-09-16 14:48:02 +09:00
minjaesong
bd9784a516 release candidate 1.7 2018-09-15 16:29:27 +09:00
minjaesong
0bafde9914 latin extended-c 2018-09-10 21:31:00 +09:00
minjaesong
b3bfe6035f support for polytonic greek 2018-09-10 01:26:58 +09:00
minjaesong
c3cf795e9a I still suck at Armenian; polytone greek WIP 2018-09-10 01:02:27 +09:00
minjaesong
cefc7860b3 new armenian (#10)
- Redesigned glyphs
- Update to Unicode 11 (2 chrs added)
2018-09-09 19:51:43 +09:00
minjaesong
b8963cd3a9 full support of old hangul 2018-08-29 00:35:46 +09:00
minjaesong
32290dd504 selectively skipping spritesheets 2018-08-24 02:36:29 +09:00
minjaesong
2702f850c2 elaboration of before-and-after tag; spec change
- RTL tag removed
- we're calling the program part "the font compiler"
2018-08-24 00:37:48 +09:00
minjaesong
2e10249f27 unicode super/subscripts (fractions) 2018-08-23 03:37:22 +09:00
minjaesong
04a375328c fixing up diacritics; complete unipunct 2018-08-22 18:17:48 +09:00
minjaesong
97ef1ddc27 more fucking bengali 2018-08-21 19:54:36 +09:00
minjaesong
4057d86176 more works on IPA
diacritics Overlay and Joiner has implemented
2018-08-14 23:37:29 +09:00
minjaesong
654f7b9ac4 I overwrote the fucking demo text 2018-08-14 19:36:00 +09:00
minjaesong
91afc4af31 tʰɛstɪŋgə tʰɛstɪŋ wʌn tʉ θɹɪ
isOverlay not actually implemented
2018-08-14 19:21:12 +09:00
minjaesong
1440908820 tags definition update to accomodate more weirdness 2018-08-11 04:39:44 +09:00
minjaesong
4ce58cf5d1 made demo program actually run 2018-08-11 00:33:52 +09:00
minjaesong
620794dc57 diacritics overlap artefacts fixed
not as I intended. fuck my life
2018-08-11 00:04:19 +09:00
minjaesong
128eeec6b5 more demotexts (more caps, sanskrit)
about to make substantial changes
2018-08-10 22:18:33 +09:00
minjaesong
33468d5ded impl of georgian mtavruli 2018-08-10 22:00:13 +09:00
minjaesong
96e5bb9a7d bitmap for Georgian Mtavruli (Georgian all-caps) 2018-08-10 21:52:15 +09:00
minjaesong
6300b29ab5 fixed up Thai and Devanagari Anusvara
not perfect but it'll work
2018-08-10 04:20:03 +09:00
minjaesong
a0e2cbf756 Devanagari BETA; broke Thai diacritics again 2018-08-10 01:29:58 +09:00
minjaesong
4a36d3e7f1 bitmap for Devanagari (only) 2018-08-09 21:11:11 +09:00
minjaesong
ab0d483cb1 somehow fixed issue #8 2018-08-09 19:32:30 +09:00
minjaesong
cdd0514ca0 fixed up the test app
- exports the drawn text as demo.PNG
- scrollable with scroll wheels and arrow keys
2018-08-09 17:42:16 +09:00
minjaesong
cf3091421f bringing the shadow back 2018-08-09 15:46:57 +09:00
minjaesong
15fb3d15c0 Kana suppelements and Insular script 2018-08-09 15:34:08 +09:00
minjaesong
45532ca9b7 lemme have some debugging helper 2018-08-09 02:54:43 +09:00
minjaesong
6714bbb510 now everything is converted to UTF-32 internally
- To make working with other planes easier
2018-08-09 02:44:29 +09:00
minjaesong
77f2314b24 stacked diacritics seems working 2018-08-08 13:03:34 +09:00
minjaesong
dcd6390dbf new tagging system (see #5) WIP
- diacritics stacking does not work
- centre align with align-to-this-x-pos is untested
2018-08-08 02:22:40 +09:00
minjaesong
ecac1dc8af contribution guideline on separate md 2018-08-01 08:55:19 +09:00
minjaesong
b3174171c4 Cherokee whitespace adjustments 2018-07-29 07:09:09 +09:00
minjaesong
a0608bf032 README update 2018-07-28 05:48:33 +09:00
minjaesong
c920eb1fe4 forgot to build font demo--as always 2018-07-28 05:38:05 +09:00
minjaesong
6cf1206716 wall of demo texts; release candidate 1 2018-07-28 05:23:17 +09:00
minjaesong
dca8ed7392 A wild Cherokee language appeared! 2018-07-28 04:52:42 +09:00
minjaesong
55bdaef897 colour code is now ARGB instead of RGBA 2018-07-27 03:51:15 +09:00
minjaesong
2cc9f0ba8c more tweaks on cyrillic family; control char impl to override RU/BG/SR mode 2018-07-27 03:26:06 +09:00
minjaesong
496c17276d more sample text 2018-07-27 01:02:11 +09:00
minjaesong
6a54ea1cca refined cyrillic letters
- more based on the handwriting
2018-07-26 11:38:36 +09:00
minjaesong
3b79a5c93f glyph fixes for vietnamese 2018-07-26 05:48:38 +09:00
minjaesong
f6e2a3b049 working demo; fixed chinese shading bug
operation not guaranteed on Slick2d
2018-07-26 05:32:28 +09:00
minjaesong
379c84c22e changed project structure so that they make sense
Font tester tool (GDX only) is coming!
2018-07-26 05:10:25 +09:00
minjaesong
42e4ae298b tweaks on armenian lowercase 2017-12-10 05:08:34 +09:00
minjaesong
f3b00c83ce new shape for the asterisk 2017-11-25 23:09:09 +09:00
minjaesong
fdcba3f6e6 no message 2017-11-25 14:19:09 +09:00
minjaesong
24cb04cdef font showcase img update 2017-10-27 05:58:44 +09:00
minjaesong
e47d2c91b1 lower Eszett more distinguishable from its caps 2017-10-27 05:56:15 +09:00
minjaesong
ff0b574c45 not enough spikey for the currency sign 2017-10-23 01:01:40 +09:00
minjaesong
279ca757d9 always draw at integer position 2017-10-21 04:10:01 +09:00
minjaesong
08d54e13c0 added missing Yen sign, adjustments on some Latin-1 symbols and Ext-A long-S 2017-07-14 22:45:16 +09:00
minjaesong
5aa6177b50 added texture filter control for gdx (whether it has some effect or not) 2017-07-14 02:36:00 +09:00
minjaesong
73c4e96359 vietnamese: it was horn, not comma... 2017-07-12 22:24:34 +09:00
minjaesong
cf1ac67cb0 assets for vietnamese support 2017-07-12 21:53:46 +09:00
minjaesong
0bb8133764 more contribution guidelines 2017-07-11 15:29:28 +09:00
minjaesong
eb114d72f3 adjusted hangul (again!) and sample update 2017-07-08 00:16:19 +09:00
minjaesong
233d4ad054 runic letters 2017-07-08 00:15:51 +09:00
minjaesong
6852ec1255 little elaboration on colour code 2017-07-04 00:14:06 +09:00
minjaesong
df51ca165b fixed a bug where GDX version's getWidth() returns wrong value (and I don't really use GlyphLayout...) 2017-07-04 00:07:10 +09:00
minjaesong
a59655d628 no message 2017-07-03 13:52:16 +09:00
219 changed files with 13832 additions and 4142 deletions

13
.gitattributes vendored Normal file
View File

@@ -0,0 +1,13 @@
*.psd filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.tga filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.opus filter=lfs diff=lfs merge=lfs -text
*.pdf filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.kra filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.tga binary diff=hex
*.kra binary diff=hex

20
.gitignore vendored Normal file → Executable file
View File

@@ -2,4 +2,22 @@ demo/out/*
demo/lib/*
demo/assets/*
out/*
lib/*
lib/*
Terrarum-sans-bitmap*.zip
TerrarumSansBitmap*.jar
Font*.jar
tmp_*
*~
.tmp*
tmp_*
*.bak
*-autosave.kra
.directory
*/__pycache__
OTFbuild/*.ttf
OTFbuild/*.otf
OTFbuild/*.woff
OTFbuild/*.woff2
*.fea
*.xdp-*

0
.idea/.name generated Normal file → Executable file
View File

87
.idea/artifacts/FontDemoGDX.xml generated Executable file
View File

@@ -0,0 +1,87 @@
<component name="ArtifactManager">
<artifact type="jar" name="FontDemoGDX">
<output-path>$PROJECT_DIR$/</output-path>
<root id="archive" name="FontDemoGDX.jar">
<element id="module-output" name="FontTestGDX" />
<element id="directory" name="META-INF">
<element id="file-copy" path="$PROJECT_DIR$/FontTestGDX/META-INF/MANIFEST.MF" />
</element>
<element id="artifact" artifact-name="TerrarumSansBitmap" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-test.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jnlp.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-1.10.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-3.2.3.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jinput-2.0.5.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jutils-1.0.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jorbis-0.0.17.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jlayer-1.0.1-gdx.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-glfw-3.2.3.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/TerrarumSansBitmap.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-1.10.0-javadoc.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-1.10.0-sources.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-openal-3.2.3.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-opengl-3.2.3.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-platform-1.10.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-3.2.3-javadoc.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-3.2.3-sources.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jinput-2.0.5-javadoc.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jinput-2.0.5-sources.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jutils-1.0.0-javadoc.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jutils-1.0.0-sources.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-jemalloc-3.2.3.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jorbis-0.0.17-javadoc.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jorbis-0.0.17-sources.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jlayer-1.0.1-gdx-javadoc.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jlayer-1.0.1-gdx-sources.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-glfw-3.2.3-javadoc.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-glfw-3.2.3-sources.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-backend-lwjgl3-1.10.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-3.2.3-natives-linux.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-3.2.3-natives-macos.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-openal-3.2.3-javadoc.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-openal-3.2.3-sources.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-opengl-3.2.3-javadoc.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-opengl-3.2.3-sources.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-3.2.3-natives-windows.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-jemalloc-3.2.3-javadoc.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-jemalloc-3.2.3-sources.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-glfw-3.2.3-natives-linux.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-glfw-3.2.3-natives-macos.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-3.2.3-natives-linux-arm32.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-3.2.3-natives-linux-arm64.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-3.2.3-natives-windows-x86.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-glfw-3.2.3-natives-windows.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-openal-3.2.3-natives-linux.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-openal-3.2.3-natives-macos.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-opengl-3.2.3-natives-linux.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-opengl-3.2.3-natives-macos.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-backend-lwjgl3-1.10.0-javadoc.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-backend-lwjgl3-1.10.0-sources.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jinput-platform-2.0.5-natives-osx.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-jemalloc-3.2.3-natives-linux.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-jemalloc-3.2.3-natives-macos.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-openal-3.2.3-natives-windows.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-opengl-3.2.3-natives-windows.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-platform-1.10.0-natives-desktop.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jinput-platform-2.0.5-natives-linux.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-glfw-3.2.3-natives-linux-arm32.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-glfw-3.2.3-natives-linux-arm64.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-glfw-3.2.3-natives-windows-x86.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-jemalloc-3.2.3-natives-windows.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jinput-platform-2.0.5-natives-windows.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-openal-3.2.3-natives-linux-arm32.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-openal-3.2.3-natives-linux-arm64.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-openal-3.2.3-natives-windows-x86.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-opengl-3.2.3-natives-linux-arm32.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-opengl-3.2.3-natives-linux-arm64.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-opengl-3.2.3-natives-windows-x86.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-jemalloc-3.2.3-natives-linux-arm32.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-jemalloc-3.2.3-natives-linux-arm64.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-jemalloc-3.2.3-natives-windows-x86.jar" path-in-jar="/" />
</root>
</artifact>
</component>

6
.idea/artifacts/TerrarumSansBitmap.xml generated Normal file → Executable file
View File

@@ -1,12 +1,12 @@
<component name="ArtifactManager">
<artifact type="jar" name="TerrarumSansBitmap">
<output-path>$PROJECT_DIR$</output-path>
<artifact type="jar" build-on-make="true" name="TerrarumSansBitmap">
<output-path>$PROJECT_DIR$/lib</output-path>
<root id="archive" name="TerrarumSansBitmap.jar">
<element id="module-output" name="BuildJAR_TerrarumSansBitmap" />
<element id="directory" name="META-INF">
<element id="file-copy" path="$PROJECT_DIR$/META-INF/MANIFEST.MF" />
</element>
<element id="dir-copy" path="$PROJECT_DIR$/src" />
<element id="module-output" name="BuildJAR_TerrarumSansBitmap" />
</root>
</artifact>
</component>

9
.idea/codeStyles/Project.xml generated Executable file
View File

@@ -0,0 +1,9 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="kotlin">
<option name="ELSE_ON_NEW_LINE" value="true" />
<option name="CATCH_ON_NEW_LINE" value="true" />
<option name="FINALLY_ON_NEW_LINE" value="true" />
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Executable file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

13
.idea/kotlinc.xml generated Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JsCompilerArguments">
<option name="moduleKind" value="plain" />
</component>
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="21" />
</component>
<component name="KotlinCommonCompilerArguments">
<option name="apiVersion" value="2.0" />
<option name="languageVersion" value="2.0" />
</component>
</project>

View File

@@ -1,12 +1,33 @@
<component name="libraryTable">
<library name="KotlinJavaRuntime">
<library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.21" />
<CLASSES>
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-runtime.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.21/kotlin-stdlib-jdk8-2.1.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.21/kotlin-stdlib-2.1.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.21/kotlin-stdlib-jdk7-2.1.21.jar!/" />
</CLASSES>
<JAVADOC />
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.21/kotlin-stdlib-jdk8-2.1.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.21/kotlin-stdlib-2.1.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.21/kotlin-stdlib-jdk7-2.1.21-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-runtime-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.21/kotlin-stdlib-jdk8-2.1.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.21/kotlin-stdlib-2.1.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.21/kotlin-stdlib-jdk7-2.1.21-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@@ -4,7 +4,13 @@
<root url="file://$PROJECT_DIR$/lib" />
</CLASSES>
<JAVADOC />
<SOURCES />
<NATIVE>
<root url="file://$PROJECT_DIR$/lib" />
</NATIVE>
<SOURCES>
<root url="file://$PROJECT_DIR$/lib" />
</SOURCES>
<jarDirectory url="file://$PROJECT_DIR$/lib" recursive="false" />
<jarDirectory url="file://$PROJECT_DIR$/lib" recursive="false" type="SOURCES" />
</library>
</component>

71
.idea/markdown-navigator.xml generated Executable file
View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownProjectSettings">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_EMOJI_URL" value="false" />
<option name="GITHUB_LISTS" value="false" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="$ProjectFileDir$" targetDir="$ProjectFileDir$" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>

View File

@@ -0,0 +1,3 @@
<component name="MarkdownNavigator.ProfileManager">
<settings default="" pdf-export="" />
</component>

70
.idea/misc.xml generated Normal file → Executable file
View File

@@ -1,74 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownProjectSettings">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_EMOJI_URL" value="false" />
<option name="GITHUB_LISTS" value="false" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="$ProjectFileDir$" targetDir="$ProjectFileDir$" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

2
.idea/modules.xml generated Normal file → Executable file
View File

@@ -3,6 +3,8 @@
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/BuildJAR_TerrarumSansBitmap.iml" filepath="$PROJECT_DIR$/BuildJAR_TerrarumSansBitmap.iml" />
<module fileurl="file://$PROJECT_DIR$/FontTestGDX/FontTestGDX.iml" filepath="$PROJECT_DIR$/FontTestGDX/FontTestGDX.iml" />
<module fileurl="file://$PROJECT_DIR$/OTFbuild/OTFbuild.iml" filepath="$PROJECT_DIR$/OTFbuild/OTFbuild.iml" />
</modules>
</component>
</project>

124
.idea/uiDesigner.xml generated Normal file
View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

0
.idea/vcs.xml generated Normal file → Executable file
View File

778
.idea/workspace.xml generated
View File

@@ -5,356 +5,125 @@
<artifact name="TerrarumSansBitmap" />
</artifacts-to-build>
</component>
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="22c5bc80-996c-4846-b173-7dc8c2096fe3" name="Default" comment="">
<change type="DELETED" beforePath="$PROJECT_DIR$/TerrarumSansBitmap.jar" afterPath="" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/artifacts/TerrarumSansBitmap.xml" afterPath="$PROJECT_DIR$/.idea/artifacts/TerrarumSansBitmap.xml" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/workspace.xml" afterPath="$PROJECT_DIR$/.idea/workspace.xml" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/demo/.idea/workspace.xml" afterPath="$PROJECT_DIR$/demo/.idea/workspace.xml" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt" afterPath="$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/slick2d/GameFontBase.kt" afterPath="$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/slick2d/GameFontBase.kt" />
<list default="true" id="22c5bc80-996c-4846-b173-7dc8c2096fe3" name="Default" comment="why are you still looking for tga.gz">
<change beforePath="$PROJECT_DIR$/.idea/modules.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt" beforeDir="false" afterPath="$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt" afterDir="false" />
</list>
<ignored path="$PROJECT_DIR$/out/" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
<component name="FileEditorManager">
<splitter split-orientation="horizontal" split-proportion="0.5">
<split-first>
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file leaf-file-name="GameFontBase.kt" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/slick2d/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="5600">
<caret line="455" column="22" lean-forward="true" selection-start-line="455" selection-start-column="22" selection-end-line="455" selection-end-column="22" />
<folding>
<element signature="e#1206#3954#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf>
</split-first>
<split-second>
<leaf>
<file leaf-file-name="GameFontBase.kt" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="602">
<caret line="66" column="93" lean-forward="false" selection-start-line="66" selection-start-column="93" selection-end-line="66" selection-end-column="93" />
<folding>
<element signature="n#!!doc" expanded="true" />
<marker date="1498933632357" expanded="true" signature="21660:22104" ph="{...}" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf>
</split-second>
</splitter>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>getWidth</find>
<find>getG</find>
<find>getWidt</find>
<find>batch.color =</find>
</findStrings>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Interface" />
<option value="Class" />
<option value="Kotlin Class" />
</list>
</option>
</component>
<component name="Git.Settings">
<excluded-from-favorite>
<branch-storage>
<map>
<entry type="LOCAL">
<value>
<list>
<branch-info repo="$PROJECT_DIR$" source="master" />
</list>
</value>
</entry>
</map>
</branch-storage>
</excluded-from-favorite>
<option name="RECENT_BRANCH_BY_REPOSITORY">
<map>
<entry key="$PROJECT_DIR$" value="ttf-otf-build-system" />
</map>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GradleLocalSettings">
<option name="externalProjectsViewState">
<projects_view />
</option>
<component name="GitSEFilterConfiguration">
<file-type-list>
<filtered-out-file-type name="LOCAL_BRANCH" />
<filtered-out-file-type name="REMOTE_BRANCH" />
<filtered-out-file-type name="TAG" />
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
</file-type-list>
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/.gitignore" />
<option value="$PROJECT_DIR$/lib/slick.jar!/org/newdawn/slick/Color.class" />
<option value="$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/slick2d/GameFontBase.kt" />
<option value="$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt" />
</list>
</option>
<component name="HighlightingSettingsPerFile">
<setting file="jar://$PROJECT_DIR$/lib/gdx-1.10.0-sources.jar!/com/badlogic/gdx/Input.java" root0="SKIP_INSPECTION" />
<setting file="jar://$PROJECT_DIR$/lib/gdx-1.10.0-sources.jar!/com/badlogic/gdx/graphics/g2d/BitmapFont.java" root0="SKIP_INSPECTION" />
</component>
<component name="ProjectFrameBounds">
<option name="x" value="-8" />
<option name="y" value="-8" />
<option name="width" value="1936" />
<option name="height" value="1176" />
<component name="KotlinCompilerWorkspaceSettings">
<option name="preciseIncrementalEnabled" value="false" />
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1">
<flattenPackages />
<showMembers />
<showModules />
<showLibraryContents />
<hideEmptyPackages />
<abbreviatePackageNames />
<autoscrollToSource />
<autoscrollFromSource />
<sortByType />
<manualOrder />
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="BuildJAR_TerrarumSansBitmap" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="Terrarum-sans-bitmap" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="BuildJAR_TerrarumSansBitmap" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="Terrarum-sans-bitmap" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="src" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="terrarumsansbitmap" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="BuildJAR_TerrarumSansBitmap" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="Terrarum-sans-bitmap" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="src" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="terrarumsansbitmap" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="slick2d" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="BuildJAR_TerrarumSansBitmap" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="Terrarum-sans-bitmap" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="src" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="terrarumsansbitmap" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="gdx" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
</subPane>
</pane>
<pane id="Scratches" />
<pane id="PackagesPane" />
<pane id="Scope" />
</panes>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="PropertiesComponent">
<property name="project.structure.last.edited" value="Artifacts" />
<property name="project.structure.proportion" value="0.15" />
<property name="project.structure.side.proportion" value="0.32068965" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<component name="ProjectColorInfo">{
&quot;customColor&quot;: &quot;&quot;,
&quot;associatedIndex&quot;: 2
}</component>
<component name="ProjectId" id="1aVE5t6KObkWt36lb07GBy1GY1S" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;Kotlin.FontTestGDXKt.executor&quot;: &quot;Debug&quot;,
&quot;Kotlin.TypewriterGDXKt.executor&quot;: &quot;Debug&quot;,
&quot;RunOnceActivity.CodyAccountHistoryMigration&quot;: &quot;true&quot;,
&quot;RunOnceActivity.CodyAccountsIdsRefresh&quot;: &quot;true&quot;,
&quot;RunOnceActivity.CodyAssignOrphanedChatsToActiveAccount&quot;: &quot;true&quot;,
&quot;RunOnceActivity.CodyConvertUrlToCodebaseName&quot;: &quot;true&quot;,
&quot;RunOnceActivity.CodyHistoryLlmMigration&quot;: &quot;true&quot;,
&quot;RunOnceActivity.CodyMigrateChatHistory-v2&quot;: &quot;true&quot;,
&quot;RunOnceActivity.CodyProjectSettingsMigration&quot;: &quot;true&quot;,
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ToggleCodyToolWindowAfterMigration&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/torvald/Documents/Terrarum-sans-bitmap&quot;,
&quot;project.structure.last.edited&quot;: &quot;Modules&quot;,
&quot;project.structure.proportion&quot;: &quot;0.15&quot;,
&quot;project.structure.side.proportion&quot;: &quot;0.20724516&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;project.kotlinCompiler&quot;
}
}</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="C:\Users\minjaesong\Documents\Terrarum-sans-bitmap\" />
<recent name="C:\Users\minjaesong\Documents\Terrarum-sans-bitmap" />
<recent name="C:\Users\minjaesong\Documents\Terrarum-sans-bitmap\FontTestGDX\src" />
</key>
</component>
<component name="RunManager">
<configuration default="true" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType" factoryName="Plugin">
<component name="RunManager" selected="Kotlin.FontTestGDXKt">
<configuration default="true" type="Applet">
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType">
<module name="" />
<option name="VM_PARAMETERS" value="-Xmx512m -Xms256m -XX:MaxPermSize=250m -ea" />
<option name="PROGRAM_PARAMETERS" />
<predefined_log_file id="idea.log" enabled="true" />
<method />
</configuration>
<configuration default="true" type="Applet" factoryName="Applet">
<option name="HTML_USED" value="false" />
<option name="WIDTH" value="400" />
<option name="HEIGHT" value="300" />
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
<module />
<method />
</configuration>
<configuration default="true" type="Application" factoryName="Application">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="ENABLE_SWING_INSPECTOR" value="false" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="" />
<envs />
<method />
</configuration>
<configuration default="true" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list />
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<method />
</configuration>
<configuration default="true" type="JUnit" factoryName="JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" />
<option name="MAIN_CLASS_NAME" />
<option name="METHOD_NAME" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method />
</configuration>
<configuration default="true" type="JarApplication" factoryName="JAR Application">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<envs />
<method />
</configuration>
<configuration default="true" type="Java Scratch" factoryName="Java Scratch">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="SCRATCH_FILE_ID" value="0" />
<option name="MAIN_CLASS_NAME" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="ENABLE_SWING_INSPECTOR" value="false" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="" />
<envs />
<method />
</configuration>
<configuration default="true" type="JetRunConfigurationType" factoryName="Kotlin">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="BuildJAR_TerrarumSansBitmap" />
<envs />
<method />
</configuration>
<configuration default="true" type="KotlinStandaloneScriptRunConfigurationType" factoryName="Kotlin script">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="filePath" />
<option name="vmParameters" />
<option name="alternativeJrePath" />
<option name="programParameters" />
<option name="passParentEnvs" value="true" />
<option name="workingDirectory" />
<option name="isAlternativeJrePathEnabled" value="false" />
<envs />
<method />
</configuration>
<configuration default="true" type="Remote" factoryName="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true" />
<option name="SERVER_MODE" value="false" />
<option name="SHMEM_ADDRESS" value="javadebug" />
<option name="HOST" value="localhost" />
<option name="PORT" value="5005" />
<method />
</configuration>
<configuration default="true" type="TestNG" factoryName="TestNG">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="SUITE_NAME" />
<option name="PACKAGE_NAME" />
<option name="MAIN_CLASS_NAME" />
<option name="METHOD_NAME" />
<option name="GROUP_NAME" />
<option name="TEST_OBJECT" value="CLASS" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
<option name="OUTPUT_DIRECTORY" />
<option name="ANNOTATION_TYPE" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<option name="USE_DEFAULT_REPORTERS" value="false" />
<option name="PROPERTIES_FILE" />
<envs />
<properties />
<listeners />
<method />
<predefined_log_file enabled="true" id="idea.log" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="executeSpecs" factoryName="Gauge Execution">
<setting name="environment" value="" />
@@ -369,9 +138,61 @@
<setting name="rowsRange" value="" />
<method />
</configuration>
<configuration default="true" type="Application" factoryName="Application">
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="JUnit" factoryName="JUnit">
<option name="TEST_OBJECT" value="class" />
<option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="FontTestGDXKt" type="JetRunConfigurationType" temporary="true">
<option name="MAIN_CLASS_NAME" value="FontTestGDXKt" />
<module name="FontTestGDX" />
<method v="2">
<option name="Make" enabled="true" />
<option name="BuildArtifacts" enabled="true">
<artifact name="TerrarumSansBitmap" />
</option>
</method>
</configuration>
<configuration name="TypewriterGDXKt" type="JetRunConfigurationType" temporary="true" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="TypewriterGDXKt" />
<module name="FontTestGDX" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="KotlinStandaloneScriptRunConfigurationType">
<option name="filePath" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="TestNG">
<option name="TEST_OBJECT" value="CLASS" />
<option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
<properties />
<listeners />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<recent_temporary>
<list>
<item itemvalue="Kotlin.FontTestGDXKt" />
<item itemvalue="Kotlin.TypewriterGDXKt" />
</list>
</recent_temporary>
</component>
<component name="ShelveChangesManager" show_recycled="false">
<option name="remove_strategy" value="false" />
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
@@ -381,245 +202,74 @@
<option name="presentableId" value="Default" />
<updated>1497950823354</updated>
</task>
<task id="LOCAL-00001" summary="Old hangul rendering fix">
<option name="closed" value="true" />
<created>1705647715000</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1705647715000</updated>
</task>
<task id="LOCAL-00002" summary="fix: characters not on overriden charset would not print">
<option name="closed" value="true" />
<created>1726151824465</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1726151824465</updated>
</task>
<task id="LOCAL-00003" summary="moving assets inside classpath">
<option name="closed" value="true" />
<created>1771460240293</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1771460240293</updated>
</task>
<task id="LOCAL-00004" summary="why are you still looking for tga.gz">
<option name="closed" value="true" />
<created>1771551906182</created>
<option name="number" value="00004" />
<option name="presentableId" value="LOCAL-00004" />
<option name="project" value="LOCAL" />
<updated>1771551906182</updated>
</task>
<option name="localTasksCounter" value="5" />
<servers />
</component>
<component name="ToolWindowManager">
<frame x="-8" y="-8" width="1936" height="1176" extended-state="6" />
<editor active="true" />
<layout>
<window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3295519" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Palette&#9;" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="true" content_ui="tabs" />
<window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.12953092" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
</layout>
<component name="TodoView">
<todo-panel id="selected-file">
<is-autoscroll-to-source value="true" />
</todo-panel>
<todo-panel id="all">
<are-packages-shown value="true" />
<is-autoscroll-to-source value="true" />
</todo-panel>
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
<component name="XDebuggerManager">
<breakpoint-manager />
<watches-manager />
<component name="VcsManagerConfiguration">
<MESSAGE value="Old hangul rendering fix" />
<MESSAGE value="fix: characters not on overriden charset would not print" />
<MESSAGE value="moving assets inside classpath" />
<MESSAGE value="why are you still looking for tga.gz" />
<option name="LAST_COMMIT_MESSAGE" value="why are you still looking for tga.gz" />
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />
<select />
</component>
<component name="antWorkspaceConfiguration">
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
<option name="FILTER_TARGETS" value="false" />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/slick2d/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="112">
<caret line="40" column="2" lean-forward="false" selection-start-line="40" selection-start-column="2" selection-end-line="40" selection-end-column="2" />
<folding>
<element signature="e#1206#3954#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="336">
<caret line="24" column="14" lean-forward="false" selection-start-line="24" selection-start-column="14" selection-end-line="24" selection-end-column="14" />
<folding>
<element signature="n#!!doc" expanded="true" />
<marker date="1498933632357" expanded="true" signature="21660:22104" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/slick2d/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="112">
<caret line="40" column="2" lean-forward="false" selection-start-line="40" selection-start-column="2" selection-end-line="40" selection-end-column="2" />
<folding>
<element signature="e#1206#3954#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="28">
<caret line="24" column="14" lean-forward="false" selection-start-line="24" selection-start-column="14" selection-end-line="24" selection-end-column="14" />
<folding>
<element signature="n#!!doc" expanded="true" />
<marker date="1498933632357" expanded="true" signature="21660:22104" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/slick2d/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#1206#3954#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="504">
<caret line="43" column="39" lean-forward="false" selection-start-line="43" selection-start-column="39" selection-end-line="43" selection-end-column="39" />
<folding>
<element signature="n#!!doc" expanded="true" />
<marker date="1498933632357" expanded="true" signature="21660:22104" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/slick2d/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#1206#3954#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="532">
<caret line="67" column="39" lean-forward="false" selection-start-line="67" selection-start-column="39" selection-end-line="67" selection-end-column="39" />
<folding>
<element signature="n#!!doc" expanded="true" />
<marker date="1498933632357" expanded="true" signature="21660:22104" ph="{...}" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="56">
<caret line="4" column="5" lean-forward="false" selection-start-line="4" selection-start-column="5" selection-end-line="4" selection-end-column="5" />
</state>
</provider>
</entry>
<entry file="jar://$PROJECT_DIR$/lib/slick.jar!/org/newdawn/slick/Color.class">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="339">
<caret line="76" column="11" lean-forward="false" selection-start-line="76" selection-start-column="11" selection-end-line="76" selection-end-column="11" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/slick2d/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="5600">
<caret line="455" column="22" lean-forward="true" selection-start-line="455" selection-start-column="22" selection-end-line="455" selection-end-column="22" />
<folding>
<element signature="e#1206#3954#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="602">
<caret line="66" column="93" lean-forward="false" selection-start-line="66" selection-start-column="93" selection-end-line="66" selection-end-column="93" />
<folding>
<element signature="n#!!doc" expanded="true" />
<marker date="1498933632357" expanded="true" signature="21660:22104" ph="{...}" />
</folding>
</state>
</provider>
</entry>
</component>
<component name="masterDetails">
<states>
<state key="ArtifactsStructureConfigurable.UI">
<settings>
<artifact-editor />
<last-edited>TerrarumSansBitmap</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.32068965" />
<option value="0.5" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="FacetStructureConfigurable.UI">
<settings>
<last-edited>No facets are configured</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="GlobalLibrariesConfigurable.UI">
<settings>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="JdkListConfigurable.UI">
<settings>
<last-edited>1.8</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="ModuleStructureConfigurable.UI">
<settings>
<last-edited>BuildJAR_TerrarumSansBitmap</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.32068965" />
<option value="0.6" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="ProjectLibrariesConfigurable.UI">
<settings>
<last-edited>lib</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.32068965" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>

View File

@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/out/production/BuildJAR_TerrarumSansBitmap" />
<output-test url="file://$MODULE_DIR$/out/test/BuildJAR_TerrarumSansBitmap" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />

82
CLAUDE.md Normal file
View File

@@ -0,0 +1,82 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Commands
### Building the JAR
The project uses IntelliJ IDEA project files (`.iml`) for building. Build the main library:
- Main library JAR: `lib/TerrarumSansBitmap.jar`
- Font test application JAR: `FontDemoGDX.jar`
### Testing Font Rendering
Run the font test application:
```bash
java -jar FontDemoGDX.jar
```
The test application demonstrates font rendering with text from `demotext_unaligned.txt` and outputs to `demo.PNG`.
### Key Development Files
- **Source code**: `src/net/torvald/terrarumsansbitmap/`
- **Font assets**: `assets/` directory (TGA format with alpha channel)
- **Test text**: `demotext.txt`, `demotext_unaligned.txt`, `testtext.txt`
- **Demo output**: Generated PNG files for visual verification
## Architecture Overview
### Core Components
**TerrarumSansBitmap** (`src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt`)
- Main font class extending LibGDX's BitmapFont
- Handles font asset loading from TGA sprite sheets
- Manages variable-width character rendering with complex glyph tagging system
- Supports multiple writing systems (Latin, CJK, Cyrillic, etc.)
**MovableType** (`src/net/torvald/terrarumsansbitmap/MovableType.kt`)
- Advanced typesetting engine with justified text layout
- Implements line-breaking, hyphenation, and kerning
- Supports multiple typesetting strategies (justified, ragged, centered)
- Handles complex text shaping for international scripts
**GlyphProps** (`src/net/torvald/terrarumsansbitmap/GlyphProps.kt`)
- Defines glyph properties including width, diacritics anchors, alignment
- Manages kerning data and special rendering directives
- Handles complex glyph tagging system for font behavior
### Font Asset System
**Glyph Encoding**
- Font data stored in TGA sprite sheets with embedded metadata
- Width encoded in binary dots on rightmost column
- Complex tagging system for diacritics, kerning, and special behaviors
- Variable-width sheets use `_variable` naming convention
**Character Support**
- Latin scripts with full diacritics support
- CJK ideographs (Chinese variant)
- Korean Hangul with syllable composition
- Cyrillic with Bulgarian/Serbian variants (requires control characters U+FFFC1, U+FFFC2)
- Devanagari, Tamil with ligature support
- Many other scripts (see assets directory)
**Typewriter Font**
- Separate typewriter bitmap font in `src/net/torvald/terrarumtypewriterbitmap/`
- Includes audio feedback system with typing sounds
- Supports international QWERTY and Korean 3-set layouts
### Key Technical Details
**Color Coding System**
- Uses Unicode private use area for color codes
- Utility functions: `GameFontBase.toColorCode()` for ARGB4444 format
- U+100000 disables color codes
**Korean Hangul Assembly**
- Decomposes Unicode Hangul into jamo components
- Assembles glyphs from initial/medial/final sprite pieces
- Supports modern Hangul range (U+AC00-U+D7A3)
**Font Metrics**
- Variable-width sheets parse glyph tags from sprite metadata
- Fixed-width sheets: `cjkpunct` (10px), `kana`/`hangul_johab` (12px), `wenquanyi` (16px)
- Diacritics positioning via anchor point system

260
CONTRIBUTING.md Executable file
View File

@@ -0,0 +1,260 @@
#### Pixel Artists Wanted: for Arabic (all four forms) and other Indian scripts (all the ligatures). Must willing to follow the styles and have a knowledge in Unicode. Your name will be inscribed in the list of contributors.
You can contribute to the font by fixing wrong glyphs, suggesting better ones, extending character set (letters for other writing systems or filling in the blanks on the existing ones), or code for other game frameworks (not limited to Java). Please leave pull request for that.
Font Spritesheets are stored in ```assets/graphics/fonts``` directory. Image format must be TGA with Alpha — no PNG. If someone needs PNG, they can batch-convert the font using utils like ImageMagick.
#### Before getting started, you did read our design goals, right? Good. Now you may continue your awesome work.
## Ascenders, descenders, width informations (aka Glyph Tags)
![Alas, use more modern browser or get better internet connexion!](glyph_height_pos_annotation.png)
Above image is a reference you can use while you draw some letters. Capital B is drawn as a reference. Orange-tinted area is for lowercase, x-height must be the same as that of said tinted area (lowercase Alpha is also drawn for the reference). NOTE THAT x-height is taller than centre bar (capital A is an exception). Height of the ascender of the lowercase letters must be the same as height of capital letters.
Red-tinted area SHOULD NOT CONTAIN any dots, it's emptied for compatibility. (Slick2d—you can define size of "gaps" of the spritesheet, but you can't define horizontal and vertical gap separately)
Blue-tinted area cotains width of the glyph in binary, uppermost dot is the Least Significant Bit.
Green-tinted area contains extra informations, left blank for most cases. We'll call it Glyph Tags.
Tinted-in-magenta shows the height where diacritics should be placed, for both uppercase and lowercase.
Each cell is 16 px wide, and any glyph you draw **must be contained within leftside FIFTEEN pixels**.
## Font Metrics for variable-width font sheets
Although the font is basically a Spritesheet, some of the sheet expects variable widths to be supported. Any sheets with ```_variable``` means it expects variable widths. Anything else expects fixed width (regular Spritesheet behaviour). ```cjkpunct``` has width of 10, ```kana``` and ```hangul_johab``` has width of 12, ```wenquanyi``` has width of 16.
### Parsing Glyph Tags
![Sample of Font Spritesheet with annotation](width_bit_encoding_annotated.png)
Width is encoded in binary bits, on pixels. On the font spritesheet, every glyph has vertical dots on their top-right side (to be exact, every (16k - 1)th pixel on x axis). Above image is a sample of the font, with width information coloured in magenta. From top to bottom, each dot represents 1, 2, 4 and 8. For example, in the above image, ! (exclamation mark) has width of 5, " (double quote) has width of 6, # (octothorp) has width of 8, $ (dollar sign) has width of 9.
### Glyph Tags
Rightmost vertical column (should be 20 px tall) contains the tags. Tags are defined as following:
```
(LSB) W -,
W |
W |= Width of the character
W |
W -'
m --Is this character lowheight?
K -,
K |= Tags used by the "Keming Machine"
K -'
Q ---Compiler Directive (see below)
n --,
Y -, `-Nudging Bits (see below)
X |
Y |= Diacritics Anchor Points (see below)
X -'
A -,_ 0 Align 1 Align 0 Align 1 Align before
A -' 0 Left 0 Right 1 Centre 1 the glyph
D --Diacritics Type Bit (see below; not all diacritics are actually marked as a diacritics on the spritesheet)
S -,_ 0 Stack 1 Stack 0 Before 1 Up &
(MSB) S -' 0 up 0 down 1 &After 1 Down* (e.g. U+0C48)
Align Centre and Align Right will respect the diacritics anchor points.
The Align Right AND Custom anchor point is being used for Thai diacritics.
When a width AND diacritics type bit is set, the entire glyph is shifted to the right as specified and the space of same width will be inserted.
(See SUNDANESE PAMINGKAL U+1BA1)
Up&Down:
1. when two pixels are both #00FF00 it's "don't stack"
2. otherwise, it's actually up&down
```
#### Nudging Bits Encoding
<MSB,Red> SXXXXXXX SYYYYYYY 00000000 <LSB,Blue>
Each X and Y numbers are Signed 8-Bit Integer.
X-positive: nudges towards left
Y-positive: nudges towards up
#### Diacritics Anchor Point Encoding
4 Pixels are further divided as follows:
| LSB | | Red | Green | Blue |
| ------------ | ------------ | ------------ | ------------ | ------------ |
| Y | Anchor point Y for: | undefined | undefined | undefined |
| X | Anchor point X for: | undefined | undefined | undefined |
| Y | Anchor point Y for: | (unused) | (unused) | (unused) |
| X | Anchor point X for: | Type-0 | Type-1 | Type-2 |
| **MSB** | | | | |
<MSB,Red> 1Y1Y1Y1Y 1Y2Y2Y2Y 1Y3Y3Y3Y <LSB,Blue>
<MSB,Red> 1X1X1X1X 1X2X2X2X 1X3X3X3X <LSB,Blue>
where Red is first, Green is second, Blue is the third diacritics.
MSB for each word must be set so that the pixel would appear brighter on the image editor.
(the font program will only read low 7 bits for each RGB channel)
#### Diacritics Type Bit Encoding
<MSB,Red> FFFFFFFF FFFFFFFF FFFFFFFF <LSB,Blue> (For Type-0)
<MSB,Red> TTTT0000 00000000 00000000 <LSB,Blue> (For Type-1 to Type-15)
Certain types of diacritics have predefined meanings (but some writing systems define their own meaning e.g. Devanagari):
* Type-0: Above
* Type-1: Below (when it should be separated from being above)
* Type-2: Overlaid (will shift down 2 pixels for lowheight glyphs instead of the default of 4 pixels)
#### Compiler Directives
<MSB,Red> [Opcode] [arg1] [arg2] <LSB,Blue>
Currently supported opcodes:
*00000000: No-operation; does not use the Compiler Directive system.
*10000111: Replace a character with maximum 7 subchars.
Replacement characters are encoded vertically from X-zero, bit by bit
(colour of the pixel doesn't matter) with LSB sitting on Y-zero.
*11111111: Tagging Used by the Subsystems. e.g. #FF0000 marks invalid combination.
#### Stack Up/Down
When the tag is stack-up, it'll be drawn 4 px lower if the underlying
character is lowercase.
#### Align-To-This-X-Pos
Since this tag does not make sense for diacritics, they will use the value for compeletely different purpose:
0 : Nothing special
1 : Covers previous character; it's neither stack-up nor down.
Will be drawn 2 px lower if the underlying character is lowercase
2 : Joiner.
3..15: undefined
#### Diacritics That Comes Before and After
When this tag is set, the font compiler will replace this glyph with two extra code points given in the bitmap.
To implement those, this two extra code points are needed, which are provided in the Unicode's Reference Chart (www.unicode.org/charts/PDF/Uxxxx.pdf) The code points must be "drawn" in the bitmap, in the same manor as a tagging system. The zeroth column (x = 0) has the "before" character, the first column (x = 1) has the "after". All nineteen pixels (bits) are read by the font, which encompasses U+0000..U+EFFFF
For working examples, take a note at the bengali sprite sheet.
This tag might be exploited as a general "replace this with these" directive, as long as you're replacing it into two letters. Such construction is FORBIDDEN due to diacritics incompatibility. Use Compiler Directives for such purposes.
Also note that the font compiler will not "stack" these diacritics.
#### The Keming Machine Tags
Keming Machine Tags define the rough shape of the glyph. Please read `keming_machine.txt` for further information.
#### NOTES
- If glyphs are right or centre aligned, they must be aligned in the same way inside of the bitmap; the font compiler assumes every variable-width glyphs to have a width of 15, regardless of the tagged width.
- If the diacritic is aligned before the glyph, the diacritic itself is always assumed as left-aligned, as the font compiler will exchange position of said diacritic and the glyph right before it.
![Visual representation of left/right/centre align](alignment_illustration.jpg)
(fun fact: it was drawn on Rhodia memopad with Lamy 2000, then photographed and edited on my iPhone. Letter used is a Cherokee WE Ꮺ)
## Technical Limitations
- Each spritesheet is 4096x4096 maximum, which is a size of 4K Texture. However it is recommended to be smaller or equal to 1024x1024.
- Glyphs exceeding 15px of width needs to be broken down with 2 or more characters, or use EXTRAWIDE spritesheets.
- Due to how the compiler is coded, actual glyph must have alpha value of 255, the tags must have alpha values LESS THAN 255 (and obviously greater than zero). RGB plane of the TGA image doesn't do anything, keep it as #FFFFFF white.
## Implementation of the Korean writing system
On this font, Hangul letters are printed by assemblying two or three letter pieces. There are 10 sets of Hangul letter pieces on the font. Top 6 are initials, middle 2 are medials, and bottom 2 are finals. On the rightmost side, there's eight assembled glyphs to help you with (assuming you have basic knowledge on the writing system). Top 6 tells you how to use 6 initials, and bottom 2 tells you how to use 2 finals.
This is a Kotlin-like pseudocode for assembling the glyph:
// NOTE: this code implements modern Hangul only, in the unicode range of 0xAC00..0xD7A3.
// the spritesheet is made to accomodate Johab encoding scheme, but can still be used with the following code.
// for the code for full Johab encoding (U+1100.. that includes Old Korean), please refer to the actual code in the repo.
function getHanChosung(hanIndex: Int) = hanIndex / (21 * 28)
function getHanJungseong(hanIndex: Int) = hanIndex / 28 % 21
function getHanJongseong(hanIndex: Int) = hanIndex % 28
jungseongWide = arrayOf(9,13,14,18,19)
jungseongComplex = arrayOf(10,11,12,15,16,17,20,23)
function getHanInitialRow(hanIndex: Int): Int {
val ret: Int
if (isJungseongWide(hanIndex))
ret = 2
else if (isJungseongComplex(hanIndex))
ret = 4
else
ret = 0
return if (getHanJongseong(hanIndex) == 0) ret else ret + 1
}
function isJungseongWide(hanIndex: Int) = jungseongWide.contains(getHanJungseong(hanIndex))
function isJungseongComplex(hanIndex: Int) = jungseongComplex.contains(getHanJungseong(hanIndex))
function getHanInitialRow(hanIndex: Int): Int {
val ret: Int
if (isJungseongWide(hanIndex))
ret = 2
else if (isJungseongComplex(hanIndex))
ret = 4
else
ret = 0
return if (getHanJongseong(hanIndex) == 0) ret else ret + 1
}
function getHanMedialRow(hanIndex: Int) = if (getHanJongseong(hanIndex) == 0) 6 else 7
function getHanFinalRow(hanIndex: Int): Int {
val jungseongIndex = getHanJungseong(hanIndex)
return if (jungseongWide.contains(jungseongIndex))
8
else
9
}
function isHangul(c: Char) = c.toInt() >= 0xAC00 && c.toInt() < 0xD7A4
...
for (each Char on the string) {
if (isHangul(Char)) {
val hIndex = Char.toInt() - 0xAC00
val indexCho = getHanChosung(hIndex)
val indexJung = getHanJungseong(hIndex)
val indexJong = getHanJongseong(hIndex)
val choRow = getHanInitialRow(hIndex)
val jungRow = getHanMedialRow(hIndex)
val jongRow = getHanFinalRow(hIndex)
// get sub image from sprite sheet
val choseongImage = hangulSheet.getSubImage(indexCho, choRow)
val jungseongImage = hangulSheet.getSubImage(indexJung, jungRow)
val jongseongImage = hangulSheet.getSubImage(indexJong, jongRow)
// actual drawing part
draw choseongImage to somewhere you want
draw jungseongImage on top of choseongImage
draw jongseongImage on top of choseongImage
}
...
}

9
CONTRIBUTORS.txt Executable file
View File

@@ -0,0 +1,9 @@
Project Maintainer and Programmer
- minjaesong (Torvald)
This font was not possible without following artists:
- minjaesong (Torvald)
<< Append your name here. If you're getting consistent merge conflicts, push without this and leave a Issue on the GitHub page >>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="BuildJAR_TerrarumSansBitmap" scope="PROVIDED" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="lib" level="project" />
</component>
</module>

View File

@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: FontTestGDXKt

365
FontTestGDX/src/FontTestGDX.kt Executable file
View File

@@ -0,0 +1,365 @@
import com.badlogic.gdx.*
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.badlogic.gdx.utils.ScreenUtils
import com.badlogic.gdx.utils.StreamUtils
import net.torvald.terrarumsansbitmap.MovableType
import net.torvald.terrarumsansbitmap.gdx.CodepointSequence
import net.torvald.terrarumsansbitmap.gdx.TerrarumSansBitmap
import java.io.File
import java.io.IOException
import java.io.OutputStream
/**
* Created by minjaesong on 2018-07-26.
*/
class FontTestGDX : Game() {
lateinit var font: TerrarumSansBitmap
lateinit var inputText: String
lateinit var batch: FlippingSpriteBatch
lateinit var frameBuffer: FrameBuffer
lateinit var camera: OrthographicCamera
private val testing = false
private val demotextName = if (testing) "testtext.txt" else "demotext_unaligned.txt"
private val outimageName = if (testing) "testing.PNG" else "demo.PNG"
private lateinit var faketex: Texture
private val lineHeight = 24
lateinit var layout: MovableType
private lateinit var testtex: TextureRegion
override fun create() {
font = TerrarumSansBitmap(debug = true, flipY = false, errorOnUnknownChar = false, shadowAlpha = 0.5f) // must test for two flipY cases
// font.scale = 2
// font.interchar = 1
testtex = TextureRegion(Texture("./testtex.tga"))
val inTextFile = Gdx.files.internal("./$demotextName")
val reader = inTextFile.reader("UTF-8")
inputText = reader.readLines().joinToString("\n")
reader.close()
batch = FlippingSpriteBatch()
// create faketex
val fakepix = Pixmap(1,1,Pixmap.Format.RGBA8888)
fakepix.drawPixel(0,0,-1)
faketex = Texture(fakepix)
fakepix.dispose()
frameBuffer = FrameBuffer(Pixmap.Format.RGBA8888, TEXW, TEXH, true)
camera = OrthographicCamera(TEXW.toFloat(), TEXH.toFloat())
camera.translate(0f, 0f)
camera.setToOrtho(false, TEXW.toFloat(), TEXH.toFloat())
camera.update()
Gdx.input.inputProcessor = Navigator(this)
println(font.charsetOverrideCodestyle)
println(font.charsetOverrideDefault)
val cs1 = CodepointSequence(listOf(0, 65, 0))
val cs2 = CodepointSequence(listOf(0, 65))
val cs3 = CodepointSequence(listOf(65, 0))
val cs4 = CodepointSequence(listOf(65))
println("w1: ${font.getWidthNormalised(cs1)}; ${font.getWidth(cs1)}")
println("w2: ${font.getWidthNormalised(cs2)}; ${font.getWidth(cs2)}")
println("w3: ${font.getWidthNormalised(cs3)}; ${font.getWidth(cs3)}")
println("w4: ${font.getWidthNormalised(cs4)}; ${font.getWidth(cs4)}")
println("These four numbers must match, even with interchar > 0")
layout = font.typesetParagraph(batch, inputText, TEXW - 48)
}
override fun getScreen(): Screen? {
return null
}
var scrollOffsetY = 0f
override fun setScreen(screen: Screen?) {
}
var tex: Texture? = null
var screenshotExported = false
private val backcol = Color(.141f, .141f, .141f, 1f)
override fun render() {
if (tex == null) {
frameBuffer.begin()
Gdx.gl.glClearColor(.141f, .141f, .141f, 1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
Gdx.gl.glEnable(GL20.GL_BLEND)
batch.setBlendFunctionSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA) // for not premultiplied textures
batch.projectionMatrix = camera.combined
batch.begin()
batch.color = backcol
batch.draw(faketex, 0f, 0f, TEXW.toFloat(), TEXH.toFloat())
batch.flush()
batch.color = Color.WHITE
// inputText.forEachIndexed { index, s ->
// font.draw(batch, s, 10f, TEXH - 30f - index * lineHeight)
// }
// draw position debuggers
// font.draw(batch, "soft\uFE0F\u00ADhyphen\uFE0F\u00ADated", 24f, 12f)
// batch.draw(testtex, 24f, 12f)
// val layoutDrawCall = { x: Float, y: Float, _: Int -> batch.draw(testtex, x, y) }
// layout.draw(batch, 24f, 12f, mapOf(0 to layoutDrawCall))
// end of draw position debuggers
layout.draw(batch, 24f, 12f)
batch.end()
// dump to file
if (!screenshotExported) {
val pixmap = ScreenUtils.getFrameBufferPixmap(0, 0, frameBuffer.width, frameBuffer.height)
PixmapIO.writePNG(Gdx.files.local(outimageName), pixmap)
// writeTGA(Gdx.files.local(outimageName), pixmap, false)
pixmap.dispose()
screenshotExported = true
}
frameBuffer.end()
///////////////
tex = frameBuffer.colorBufferTexture
}
Gdx.gl.glClearColor(.141f, .141f, .141f, 1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
Gdx.gl.glEnable(GL20.GL_BLEND)
batch.setBlendFunctionSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA) // for not premultiplied textures
camera.setToOrtho(true, WIDTH.toFloat(), HEIGHT.toFloat())
batch.projectionMatrix = camera.combined
batch.begin()
batch.color = Color.WHITE
batch.draw(tex!!, 0f, scrollOffsetY)
batch.end()
}
override fun pause() {
}
override fun resume() {
}
override fun resize(width: Int, height: Int) {
}
override fun dispose() {
font.dispose()
faketex.dispose()
testtex.texture.dispose()
}
fun scrollAdd(x: Int = 1) {
scrollOffsetY += lineHeight * x
}
fun scrollSub(x: Int = 1) {
scrollOffsetY -= lineHeight * x
}
class Navigator(val main: FontTestGDX) : InputAdapter() {
override fun scrolled(amountX: Float, amountY: Float): Boolean {
if (amountY >= 0)
main.scrollSub(amountY.toInt())
else
main.scrollAdd(-amountY.toInt())
return true
}
override fun keyDown(keycode: Int): Boolean {
if (keycode == Input.Keys.UP)
main.scrollAdd()
else if (keycode == Input.Keys.DOWN)
main.scrollSub()
return true
}
}
@Throws(IOException::class)
private fun writeTGA(file: FileHandle, pixmap: Pixmap, flipY: Boolean) {
val output = file.write(false)
try {
_writeTGA(output, pixmap, true, flipY)
} finally {
StreamUtils.closeQuietly(output)
}
}
@Throws(IOException::class)
private fun _writeTGA(out: OutputStream, pixmap: Pixmap, verbatim: Boolean, flipY: Boolean) {
val width: ByteArray = toShortLittle(pixmap.width)
val height: ByteArray = toShortLittle(pixmap.height)
val zero: ByteArray = toShortLittle(0)
out.write(0) // ID field: empty
out.write(0) // no colour map, but should be ignored anyway as it being unmapped RGB
out.write(2) // 2 means unmapped RGB
out.write(byteArrayOf(0, 0, 0, 0, 0)) // color map spec: empty
out.write(zero) // x origin: 0
out.write(zero) // y origin: 0
out.write(width) // width
out.write(height) // height
out.write(32) // image pixel size: we're writing 32-bit image (8bpp BGRA)
out.write(8) // image descriptor: dunno, Photoshop writes 8 in there
// write actual image data
// since we're following Photoshop's conventional header, we also follows Photoshop's
// TGA saving scheme, that is:
// 1. BGRA order
// 2. Y-Flipped but not X-Flipped
if (!flipY) {
for (y in pixmap.height - 1 downTo 0) {
for (x in 0 until pixmap.width) {
writeTga(x, y, verbatim, pixmap, out)
}
}
} else {
for (y in 0 until pixmap.height) {
for (x in 0 until pixmap.width) {
writeTga(x, y, verbatim, pixmap, out)
}
}
}
// write footer
// 00 00 00 00 00 00 00 00 TRUEVISION-XFILE 2E 00
out.write(byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0))
if (verbatim) out.write("TRUEVISION-XFILE".toByteArray()) else out.write("TerrarumHappyTGA".toByteArray())
out.write(byteArrayOf(0x2E, 0))
out.flush()
out.close()
}
private val zeroalpha = byteArrayOf(0, 0, 0, 0)
@Throws(IOException::class)
private fun writeTga(x: Int, y: Int, verbatim: Boolean, pixmap: Pixmap, out: OutputStream) {
val color = pixmap.getPixel(x, y)
// if alpha == 0, write special value instead
if (verbatim && color and 0xFF == 0) {
out.write(zeroalpha)
} else {
out.write(RGBAtoBGRA(color))
}
}
private fun toShortLittle(i: Int): ByteArray {
return byteArrayOf(
(i and 0xFF).toByte(),
(i ushr 8 and 0xFF).toByte()
)
}
private fun RGBAtoBGRA(rgba: Int): ByteArray {
return byteArrayOf(
(rgba ushr 8 and 0xFF).toByte(),
(rgba ushr 16 and 0xFF).toByte(),
(rgba ushr 24 and 0xFF).toByte(),
(rgba and 0xFF).toByte()
)
}
}
class FlippingSpriteBatch(size: Int = 1000) : SpriteBatch(size) {
/**
* This function draws the flipped version of the image by giving flipped uv-coord to the SpriteBatch
*/
override fun draw(texture: Texture, x: Float, y: Float, width: Float, height: Float) =
draw(texture, x, y, width, height, 0f, 0f, 1f, 1f)
override fun draw(texture: Texture, x: Float, y: Float) =
draw(texture, x, y, texture.width.toFloat(), texture.height.toFloat(), 0f, 0f, 1f, 1f)
fun drawFlipped(texture: Texture, x: Float, y: Float, width: Float, height: Float) =
draw(texture, x, y, width, height, 0f, 1f, 1f, 0f)
fun drawFlipped(texture: Texture, x: Float, y: Float) =
draw(texture, x, y, texture.width.toFloat(), texture.height.toFloat(), 0f, 1f, 1f, 0f)
/**
* This function does obey the flipping set to the TextureRegion and try to draw flipped version of it,
* without touching the flipping setting of the given region.
*/
override fun draw(region: TextureRegion, x: Float, y: Float, width: Float, height: Float) =
draw(region.texture, x, y, width, height, region.u, region.v, region.u2, region.v2)
override fun draw(region: TextureRegion, x: Float, y: Float) =
draw(region.texture, x, y, region.regionWidth.toFloat(), region.regionHeight.toFloat(), region.u, region.v, region.u2, region.v2)
fun drawFlipped(region: TextureRegion, x: Float, y: Float, width: Float, height: Float) =
draw(region.texture, x, y, width, height, region.u, region.v2, region.u2, region.v)
fun drawFlipped(region: TextureRegion, x: Float, y: Float) =
draw(region.texture, x, y, region.regionWidth.toFloat(), region.regionHeight.toFloat(), region.u, region.v2, region.u2, region.v)
/**
* NOTE TO SELF:
*
* It seems that original SpriteBatch Y-flips when it's drawing a texture, but NOT when it's drawing a textureregion
*
* (textureregion's default uv-coord is (0,0,1,1)
*/
}
lateinit var appConfig: Lwjgl3ApplicationConfiguration
const val TEXW = 800
const val TEXH = 24 * 171
const val WIDTH = TEXW
const val HEIGHT = 768
fun main(args: Array<String>) {
appConfig = Lwjgl3ApplicationConfiguration()
appConfig.useVsync(false)
appConfig.setResizable(false)
appConfig.setWindowedMode(WIDTH, HEIGHT)
appConfig.setTitle("Terrarum Sans Bitmap Test")
Lwjgl3Application(FontTestGDX(), appConfig)
}

View File

@@ -0,0 +1,246 @@
import com.badlogic.gdx.Game
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import com.badlogic.gdx.InputAdapter
import com.badlogic.gdx.audio.AudioDevice
import com.badlogic.gdx.audio.Sound
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.GL20
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.utils.GdxRuntimeException
import net.torvald.terrarum.gamecontroller.InputStrober
import net.torvald.terrarumsansbitmap.gdx.CodepointSequence
import net.torvald.terrarumtypewriterbitmap.gdx.TerrarumTypewriterBitmap
import java.io.StringReader
import kotlin.math.roundToInt
/**
* Created by minjaesong on 2021-11-05.
*/
class TypewriterGDX(val width: Int, val height: Int, val cols: Int, val hmargin: Int, val vmargin: Int) : Game() {
lateinit var font: TerrarumTypewriterBitmap
lateinit var batch: SpriteBatch
// lateinit var frameBuffer: FrameBuffer
lateinit var camera: OrthographicCamera
lateinit var inputStrober: InputStrober
lateinit var sndMovingkey: Sound
lateinit var sndDeadkey: Sound
lateinit var sndShiftin: Sound
lateinit var sndShiftout: Sound
lateinit var sndSpace: Sound
lateinit var sndCRs: Array<Sound>
lateinit var sndLF: Sound
override fun create() {
font = TerrarumTypewriterBitmap(
StringReader(
"""ko_kr_3set-390_typewriter,typewriter_ko_3set-390.tga,16
|en_intl_qwerty_typewriter,typewriter_intl_qwerty.tga,0
""".trimMargin()
),
true, false, 256, true
)
batch = SpriteBatch()
// frameBuffer = FrameBuffer(Pixmap.Format.RGBA8888, TEXW, TEXH, true)
camera = OrthographicCamera(width.toFloat(), height.toFloat())
camera.translate(width.div(2f), 0f)
camera.setToOrtho(true, width.toFloat(), height.toFloat())
camera.update()
inputStrober = InputStrober(this)
try {
sndMovingkey = Gdx.audio.newSound(Gdx.files.classpath("assets/typewriter/audio/movingkey.wav"))
sndDeadkey = Gdx.audio.newSound(Gdx.files.classpath("assets/typewriter/audio/deadkey.wav"))
sndShiftin = Gdx.audio.newSound(Gdx.files.classpath("assets/typewriter/audio/shiftin.wav"))
sndShiftout = Gdx.audio.newSound(Gdx.files.classpath("assets/typewriter/audio/shiftout.wav"))
sndSpace = Gdx.audio.newSound(Gdx.files.classpath("assets/typewriter/audio/space.wav"))
sndCRs = Array(6) {
Gdx.audio.newSound(Gdx.files.classpath("assets/typewriter/audio/cr$it.wav"))
}
sndLF = Gdx.audio.newSound(Gdx.files.classpath("assets/typewriter/audio/crlf.wav"))
}
catch (e: GdxRuntimeException) {
e.printStackTrace()
}
}
private val intro = listOf(
39,50,29, // kva (HANG_GONG)
42,31, // nc (HANG_SE)
74,48,51, // ;tw (HANG_BEOL)
62, // space
0x561F71, // shiftin
184,164,171,170, // >HON (ASC_3-90)
0x561F70, // shiftout
62, // space
74,48, // ;t (HANG_BEO)
43,12, // o5 (HANG_CYU)
38,48,51, // jtw (HANG_EOL)
0x561F71, // shiftin
164, // H (ASC_-)
0x561F70, // shiftout
75,34, // 'f (HANG_TA)
40,34, // lf (HANG_JA)
39,32, // kd (HANG_GI)
Input.Keys.ENTER,Input.Keys.ENTER
)
private val textbuf: ArrayList<CodepointSequence> = arrayListOf(
CodepointSequence()
)
var keylayoutbase = 0xFA000
private val printableKeys = ((Input.Keys.NUM_0..Input.Keys.NUM_9) + (Input.Keys.A..Input.Keys.PERIOD) + 62 + (Input.Keys.BACKSPACE..Input.Keys.SLASH)).toHashSet()
var initDone = false
var initTimer = 0f
var initTypingCursor = 0
var keystrokeDelay = 0.08f
fun acceptKey(keycode: Int, force: Boolean = false) {
if (initDone || force) {
// println("[TypewriterGDX] Accepting key: $keycode")
val lowkeycode = keycode and 127
if (keycode == Input.Keys.ENTER) {
val tbufsize = textbuf.last().size.div(cols.toFloat()).times(6f).coerceIn(0f, 6f).roundToInt() // 0..6
textbuf.add(CodepointSequence())
if (tbufsize == 0) sndLF.play()
else sndCRs[tbufsize - 1].play()
} else if (printableKeys.contains(lowkeycode)) {
val cp = keycode + keylayoutbase
textbuf.last().add(cp)
// println("[TypewriterGDX] width: ${font.glyphProps[cp]}")
// play audio
val isDeadkey = font.glyphProps[cp]?.width == 0
if (isDeadkey) {
sndDeadkey.play()
} else if (lowkeycode == Input.Keys.SPACE || lowkeycode == Input.Keys.BACKSPACE) {
sndSpace.play()
} else {
sndMovingkey.play()
}
} else if (lowkeycode == Input.Keys.SHIFT_LEFT || lowkeycode == Input.Keys.SHIFT_RIGHT) {
sndShiftin.play()
}
}
}
/**
* For Shift-out only
*/
fun shiftOut() {
sndShiftout.play()
}
private val textCol = Color(0.1f,0.1f,0.1f,1f)
override fun render() {
Gdx.gl.glClearColor(0.97f,0.96f,0.95f,1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
Gdx.gl.glEnable(GL20.GL_BLEND)
Gdx.gl.glBlendFuncSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE)
batch.projectionMatrix = camera.combined
batch.begin()
batch.color = textCol
try {
textbuf.forEachIndexed { index, s ->
font.draw(batch, s, hmargin.toFloat(), vmargin.toFloat() + 20 * index)
}
}
catch (e: ConcurrentModificationException) {}
batch.end()
if (!initDone) {
while (initTimer > keystrokeDelay) {
val keyToType = intro[initTypingCursor]
if (keyToType < 256) {
acceptKey(keyToType, true)
}
else if (keyToType == 0x561F71) {
acceptKey(Input.Keys.SHIFT_LEFT + 128, true)
}
else if (keyToType == 0x561F70) {
shiftOut()
}
initTypingCursor += 1
initTimer -= keystrokeDelay
if (keyToType == Input.Keys.ENTER)
initTimer -= 0.35f
else if (keyToType == 0x561F71)
initTimer -= 0.15f
else if (keyToType == 0x561F70)
initTimer -= 0.1f
else
initTimer -= Math.random().toFloat() * 0.04f
}
initTimer += Gdx.graphics.deltaTime
if (initTypingCursor >= intro.size) {
initDone = true
}
}
}
override fun dispose() {
font.dispose()
batch.dispose()
inputStrober.dispose()
sndMovingkey.dispose()
sndDeadkey.dispose()
sndShiftin.dispose()
sndShiftout.dispose()
sndSpace.dispose()
sndCRs.forEach { it.dispose() }
sndLF.dispose()
}
}
class TypewriterInput(val main: TypewriterGDX) : InputAdapter() {
private var shiftIn = false
override fun keyDown(keycode: Int): Boolean {
// FIXME this shiftIn would not work at all...
shiftIn = (keycode == Input.Keys.SHIFT_LEFT || keycode == Input.Keys.SHIFT_RIGHT)
if (keycode < 128 && keycode != Input.Keys.SHIFT_LEFT && keycode != Input.Keys.SHIFT_RIGHT) {
main.acceptKey(shiftIn.toInt() * 128 + keycode)
}
return true
}
private fun Boolean.toInt() = if (this) 1 else 0
}
fun main(args: Array<String>) {
appConfig = Lwjgl3ApplicationConfiguration()
appConfig.useVsync(false)
appConfig.setResizable(false)
appConfig.setWindowedMode(534, 668)
appConfig.setTitle("Terrarum Typewriter Bitmap Test")
Lwjgl3Application(TypewriterGDX(534, 668, 64, 32, 24), appConfig)
}

View File

@@ -0,0 +1,401 @@
package net.torvald.terrarum.gamecontroller
import TypewriterGDX
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
/**
* Created by minjaesong on 2021-11-06.
*/
class InputStrober(val typewriter: TypewriterGDX) {
companion object {
const val KEY_DOWN = 0
const val KEY_CHANGE = 1
const val N_KEY_ROLLOVER = 8
}
var KEYBOARD_DELAYS = longArrayOf(0L,250000000L,0L,25000000L,0L)
private var stroboTime = 0L
private var stroboStatus = 0
private var repeatCount = 0
private var oldKeys = IntArray(N_KEY_ROLLOVER) { 0 }
/** always Low Layer */
// private var keymap = IME.getLowLayerByName(App.getConfigString("basekeyboardlayout"))
private val thread = Thread { while (!Thread.interrupted()) {
if (Gdx.input != null) withKeyboardEvent()
} }
init {
// println("InputStrobe start")
thread.start()
}
fun dispose() {
thread.interrupt()
}
fun resetKeyboardStrobo() {
stroboStatus = 0
repeatCount = 0
}
// code proudly stolen from tsvm's TVDOS.SYS
private fun withKeyboardEvent() {
val keys = strobeKeys()
var keyChanged = !arrayEq(keys, oldKeys)
val keyDiff = arrayDiff(keys, oldKeys)
// println("Key strobed: ${keys.joinToString()}")
if (stroboStatus % 2 == 0 && (keys[0] != 0 || oldKeys[0] != 0)) {
stroboStatus += 1
stroboTime = System.nanoTime()
repeatCount += 1
val shiftin = keys.contains(Input.Keys.SHIFT_LEFT) || keys.contains(Input.Keys.SHIFT_RIGHT)
val newKeysym0 = keysToStr(keyDiff)
val newKeysym = if (newKeysym0 == null) null
else if (shiftin && newKeysym0.size > 1 && newKeysym0[1]?.isNotBlank() == true) newKeysym0[1]
else newKeysym0[0]
val headKeyCode = (if (keyDiff.size < 1) keys[0] else keyDiff[0]).and(127) or (if (shiftin) 128 else 0)
if (repeatCount == 1) {
if (!keyChanged) {
// println("KEY_DOWN '$keysym' ($headKeyCode) $repeatCount; ${keys.joinToString()}")
// App.inputStrobed(TerrarumKeyboardEvent(KEY_DOWN, keysym, headKeyCode, repeatCount, keys))
typewriter.acceptKey(headKeyCode)
} else if (newKeysym != null) {
// println("KEY_DOWC '$newKeysym' ($headKeyCode) $repeatCount; ${keys.joinToString()}")
// App.inputStrobed(TerrarumKeyboardEvent(KEY_DOWN, newKeysym, headKeyCode, repeatCount, keys))
typewriter.acceptKey(headKeyCode)
}
// println("shiftin=${shiftin} oldkeys=${oldKeys.joinToString()}")
if (!shiftin && (oldKeys.contains(Input.Keys.SHIFT_LEFT) || oldKeys.contains(Input.Keys.SHIFT_RIGHT))) {
typewriter.shiftOut()
}
}
oldKeys = keys // don't put this outside of if-cascade
}
else if (keyChanged || keys[0] == 0) {
stroboStatus = 0
repeatCount = 0
if (keys[0] == 0) keyChanged = false
}
else if (stroboStatus % 2 == 1 && System.nanoTime() - stroboTime < KEYBOARD_DELAYS[stroboStatus]) {
Thread.sleep(1L)
}
else {
stroboStatus += 1
if (stroboStatus >= 4)
stroboStatus = 2
}
}
private fun keysToStr(keys: IntArray): Array<String?>? {
if (keys.isEmpty()) return null
val headkey = keys[0]
return keymap[headkey]
}
private fun strobeKeys(): IntArray {
var keysPushed = 0
val keyEventBuffers = IntArray(N_KEY_ROLLOVER) { 0 }
for (k in 1..254) {
if (Gdx.input.isKeyPressed(k)) {
keyEventBuffers[keysPushed] = k
keysPushed += 1
}
if (keysPushed >= N_KEY_ROLLOVER) break
}
return keyEventBuffers
}
private fun arrayEq(a: IntArray, b: IntArray): Boolean {
for (i in a.indices) {
if (a[i] != b.getOrNull(i)) return false
}
return true
}
private fun arrayDiff(a: IntArray, b: IntArray): IntArray {
return a.filter { !b.contains(it) }.toIntArray()
}
private val keymap = arrayOf(arrayOf<String?>(""),arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>("<HOME>"),
arrayOf<String?>(null),
arrayOf<String?>("<CALL>"),
arrayOf<String?>("<ENDCALL>"),
arrayOf<String?>("0",")"),
arrayOf<String?>("1","!"),
arrayOf<String?>("2","@"),
arrayOf<String?>("3","#"),
arrayOf<String?>("4","$"),
arrayOf<String?>("5","%"),
arrayOf<String?>("6","^"),
arrayOf<String?>("7","&"),
arrayOf<String?>("8","*"),
arrayOf<String?>("9","("),
arrayOf<String?>("*"),
arrayOf<String?>("#"),
arrayOf<String?>("<UP>"),
arrayOf<String?>("<DOWN>"),
arrayOf<String?>("<LEFT>"),
arrayOf<String?>("<RIGHT>"),
arrayOf<String?>("<CENTER>"),
arrayOf<String?>("<VOL_UP>"),
arrayOf<String?>("<VOL_DOWN>"),
arrayOf<String?>("<POWER>"),
arrayOf<String?>("<CAMERA>"),
arrayOf<String?>("<CLEAR>"),
arrayOf<String?>("a","A"),
arrayOf<String?>("b","B"),
arrayOf<String?>("c","C"),
arrayOf<String?>("d","D"),
arrayOf<String?>("e","E"),
arrayOf<String?>("f","F"),
arrayOf<String?>("g","G"),
arrayOf<String?>("h","H"),
arrayOf<String?>("i","I"),
arrayOf<String?>("j","J"),
arrayOf<String?>("k","K"),
arrayOf<String?>("l","L"),
arrayOf<String?>("m","M"),
arrayOf<String?>("n","N"),
arrayOf<String?>("o","O"),
arrayOf<String?>("p","P"),
arrayOf<String?>("q","Q"),
arrayOf<String?>("r","R"),
arrayOf<String?>("s","S"),
arrayOf<String?>("t","T"),
arrayOf<String?>("u","U"),
arrayOf<String?>("v","V"),
arrayOf<String?>("w","W"),
arrayOf<String?>("x","X"),
arrayOf<String?>("y","Y"),
arrayOf<String?>("z","Z"),
arrayOf<String?>(",","<"),
arrayOf<String?>(".",">"),
arrayOf<String?>("<ALT_L>"),
arrayOf<String?>("<ALT_R>"),
arrayOf<String?>("<SHIFT_L>"),
arrayOf<String?>("<SHIFT_R>"),
arrayOf<String?>("<TAB>"),
arrayOf<String?>(" "),
arrayOf<String?>("<SYM>"),
arrayOf<String?>("<EXPLORER>"),
arrayOf<String?>("<ENVELOPE>"),
arrayOf<String?>("\n"),
arrayOf<String?>("\u0008"),
arrayOf<String?>("`","~"),
arrayOf<String?>("-","_"),
arrayOf<String?>("=","+"),
arrayOf<String?>("arrayOf<String?>(","{"),
arrayOf<String?>(")","}"),
arrayOf<String?>("\\","|"),
arrayOf<String?>(";",":"),
arrayOf<String?>("'","\""),
arrayOf<String?>("/","?"),
arrayOf<String?>("<AT>"),
arrayOf<String?>("<NUM_LOCK>"),
arrayOf<String?>("<HEADSETHOOK>"),
arrayOf<String?>("<FOCUS>"),
arrayOf<String?>("+"),
arrayOf<String?>("<MENU>"),
arrayOf<String?>("<NOTIFICATION>"),
arrayOf<String?>("<SEARCH>"),
arrayOf<String?>("<PLAY_PAUSE>"),
arrayOf<String?>("<STOP>"),
arrayOf<String?>("<NEXT>"),
arrayOf<String?>("<PREV>"),
arrayOf<String?>("<REW>"),
arrayOf<String?>("<FFWD>"),
arrayOf<String?>("<MUTE>"),
arrayOf<String?>("<PAGE_UP>"),
arrayOf<String?>("<PAGE_DOWN>"),
arrayOf<String?>("<PICTSYMBOLS>"),
arrayOf<String?>("<SW:>TCH_CHARSET>"),
arrayOf<String?>("<:A:>"),
arrayOf<String?>("<:B:>"),
arrayOf<String?>("<:C:>"),
arrayOf<String?>("<:X:>"),
arrayOf<String?>("<:Y:>"),
arrayOf<String?>("<:Z:>"),
arrayOf<String?>("<:L1:>"),
arrayOf<String?>("<:R1:>"),
arrayOf<String?>("<:L2:>"),
arrayOf<String?>("<:R2:>"),
arrayOf<String?>("<:TL:>"),
arrayOf<String?>("<:TR:>"),
arrayOf<String?>("<:START:>"),
arrayOf<String?>("<:SELECT:>"),
arrayOf<String?>("<:MODE:>"),
arrayOf<String?>("<ESC>"),
arrayOf<String?>("<DEL>"),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>("<CAPS_LOCK>"),
arrayOf<String?>("<SCROLL_LOCK>"),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>("<PRINT_SCREEN_SYS_RQ>"),
arrayOf<String?>("<PAUSE_BREAK>"),
arrayOf<String?>(null),
arrayOf<String?>("<END>"),
arrayOf<String?>("<INSERT>"),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>("<CTRL_L>"),
arrayOf<String?>("<CTRL_R>"),
arrayOf<String?>("<F1>"),
arrayOf<String?>("<F2>"),
arrayOf<String?>("<F3>"),
arrayOf<String?>("<F4>"),
arrayOf<String?>("<F5>"),
arrayOf<String?>("<F6>"),
arrayOf<String?>("<F7>"),
arrayOf<String?>("<F8>"),
arrayOf<String?>("<F9>"),
arrayOf<String?>("<F10>"),
arrayOf<String?>("<F11>"),
arrayOf<String?>("<F12>"),
arrayOf<String?>("<NUM_LOCK>"),
arrayOf<String?>("0"),
arrayOf<String?>("1"),
arrayOf<String?>("2"),
arrayOf<String?>("3"),
arrayOf<String?>("4"),
arrayOf<String?>("5"),
arrayOf<String?>("6"),
arrayOf<String?>("7"),
arrayOf<String?>("8"),
arrayOf<String?>("9"),
arrayOf<String?>("/"),
arrayOf<String?>("*"),
arrayOf<String?>("-"),
arrayOf<String?>("+"),
arrayOf<String?>("."),
arrayOf<String?>("."),
arrayOf<String?>("\n"),
arrayOf<String?>("="),
arrayOf<String?>("("),
arrayOf<String?>(")"),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>(null),
arrayOf<String?>("<:CIRCLE:>")
)
}
data class TerrarumKeyboardEvent(
val type: Int,
val character: String?, // representative key symbol
val headkey: Int, // representative keycode
val repeatCount: Int,
val keycodes: IntArray
)

67
LICENSE.md Normal file → Executable file
View File

@@ -1,21 +1,54 @@
Terrarum Sans Bitmap
Copyright (c) 2017-2026 CuriousTorvald (curioustorvald.com), with Reserved Font Name TERRARUM.
Copyright (c) 2017 Minjae Song (Torvald) and the contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-———————————————————————
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-———————————————————————
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
DEFINITIONS
“Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
“Reserved Font Name” refers to any names specified as such after the copyright statement(s).
“Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s).
“Modified Version” refers to any derivative made by adding to, deleting, or substituting in part or in whole any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
“Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

0
META-INF/MANIFEST.MF Normal file → Executable file
View File

328
OTFbuild/CLAUDE.md Normal file
View File

@@ -0,0 +1,328 @@
# OTFbuild
Python toolchain that builds an OpenType (CFF) and Web Open Font (WOFF2) font from the TGA sprite sheets used by the bitmap font engine.
## Building
```bash
# builds both OTF and WOFF2
make all
```
## Debugging with HarfBuzz
Install `uharfbuzz` for shaping tests:
```bash
pip install uharfbuzz
```
Shape text and inspect glyph substitutions, advances, and positioning:
```python
import uharfbuzz as hb
from fontTools.ttLib import TTFont
with open('OTFbuild/TerrarumSansBitmap.otf', 'rb') as f:
font_data = f.read()
blob = hb.Blob(font_data)
face = hb.Face(blob)
font = hb.Font(face)
text = "ऐतिहासिक"
buf = hb.Buffer()
buf.add_str(text)
buf.guess_segment_properties()
hb.shape(font, buf)
ttfont = TTFont('OTFbuild/TerrarumSansBitmap.otf')
glyph_order = ttfont.getGlyphOrder()
for info, pos in zip(buf.glyph_infos, buf.glyph_positions):
name = glyph_order[info.codepoint]
print(f" {name} advance=({pos.x_advance},{pos.y_advance}) cluster={info.cluster}")
```
Key things to check:
- **advance=(0,0)** on a visible character means the glyph is zero-width (likely missing outline or failed GSUB substitution)
- **glyph name starts with `uF0`** means GSUB substituted to an internal PUA form (expected for Devanagari consonants, Hangul jamo variants, etc.)
- **cluster** groups glyphs that originated from the same input character(s)
### Inspecting GSUB tables
```python
from fontTools.ttLib import TTFont
font = TTFont('OTFbuild/TerrarumSansBitmap.otf')
gsub = font['GSUB']
# List scripts and their features
for sr in gsub.table.ScriptList.ScriptRecord:
tag = sr.ScriptTag
if sr.Script.DefaultLangSys:
for idx in sr.Script.DefaultLangSys.FeatureIndex:
fr = gsub.table.FeatureList.FeatureRecord[idx]
print(f" {tag}/{fr.FeatureTag}: lookups={fr.Feature.LookupListIndex}")
# Inspect a specific lookup's substitution mappings
lookup = gsub.table.LookupList.Lookup[18] # e.g. DevaConsonantMap
for st in lookup.SubTable:
for src, dst in st.mapping.items():
print(f" {src} -> {dst}")
```
### Checking glyph outlines and metrics
```python
font = TTFont('OTFbuild/TerrarumSansBitmap.otf')
hmtx = font['hmtx']
cff = font['CFF ']
name = 'uni0915' # Devanagari KA
w, lsb = hmtx[name]
cs = cff.cff.topDictIndex[0].CharStrings[name]
cs.decompile()
has_outlines = len(cs.program) > 2 # more than just width + endchar
print(f"{name}: advance={w}, has_outlines={has_outlines}")
```
## Architecture
### Build pipeline (`font_builder.py`)
1. **Parse sheets**`glyph_parser.py` reads each TGA sprite sheet, extracts per-glyph bitmaps and tag-column metadata (width, alignment, diacritics anchors, kerning data, directives)
2. **Compose Hangul**`hangul.py` assembles 11,172 precomposed Hangul syllables from jamo components and stores jamo variants in PUA for GSUB
3. **Populate Devanagari** — consonants U+0915-0939 have width=0 in the sprite sheet (the Kotlin engine normalises them to PUA forms); the builder copies PUA glyph data back to the Unicode positions so they render without GSUB
4. **Expand replacewith** — glyphs with the `replacewith` directive (opcode 0x80-0x87) are collected for GSUB multiple substitution (e.g. U+0910 -> U+090F U+0947)
5. **Build glyph order and cmap** — PUA internal forms (0xF0000-0xF0FFF) get glyphs but no cmap entries
6. **Trace bitmaps**`bitmap_tracer.py` converts 1-bit bitmaps to CFF rectangle contours (50 units/pixel)
7. **Set metrics** — hmtx, hhea, OS/2, head, name, post tables
8. **OpenType features**`opentype_features.py` generates feaLib code, compiled via `fontTools.feaLib`
9. **Bitmap strike** — optional EBDT/EBLC at 20ppem via TTX import
### Module overview
| Module | Purpose |
|---|---|
| `build_font.py` | CLI entry point |
| `font_builder.py` | Orchestrates the build pipeline |
| `sheet_config.py` | Sheet indices, code ranges, index functions, metric constants, Hangul/Devanagari/Tamil/Sundanese constants |
| `glyph_parser.py` | TGA sprite sheet parsing; extracts bitmaps and tag-column properties |
| `tga_reader.py` | Low-level TGA image reader |
| `bitmap_tracer.py` | Converts 1-bit bitmaps to CFF outlines (rectangle merging) |
| `opentype_features.py` | Generates GSUB/GPOS feature code for feaLib |
| `keming_machine.py` | Generates kerning pairs from glyph kern masks |
| `hangul.py` | Hangul syllable composition and jamo GSUB data |
| `otf2woff2.py` | OTF to WOFF2 wrapper |
### OpenType features generated (`opentype_features.py`)
- **ccmp** — replacewith expansions (DFLT); consonant-to-PUA mapping + vowel decompositions + anusvara upper (dev2/deva); vowel decompositions (tml2)
- **kern** — pair positioning from `keming_machine.py`
- **liga** — Latin ligatures (ff, fi, fl, ffi, ffl, st) and Armenian ligatures
- **locl** — Bulgarian/Serbian Cyrillic alternates; Devanagari consonant-to-PUA mapping + vowel decompositions + anusvara upper (dev2/deva, duplicated from ccmp for DirectWrite compatibility)
- **nukt, akhn, half, blwf, cjct, pres, blws, rphf, abvs, psts, calt** — Devanagari complex script shaping (all under both `script dev2` and `script deva`)
- **pres** (tml2) — Tamil consonant+vowel ligatures
- **pres** (sund) — Sundanese diacritic combinations
- **ljmo, vjmo, tjmo** — Hangul jamo positional variants
- **mark** — GPOS mark-to-base diacritics positioning
- **mkmk** — GPOS mark-to-mark diacritics stacking (successive marks shift by H_DIACRITICS)
### Devanagari PUA mapping
The bitmap font engine normalises Devanagari consonants to internal PUA forms before rendering. The OTF builder mirrors this:
| Unicode range | PUA range | Purpose |
|---|---|---|
| U+0915-0939 | 0xF0140-0xF0164 | Base consonants |
| U+0915-0939 +48 | 0xF0170-0xF0194 | Nukta forms (consonant + U+093C) |
| U+0915-0939 +240 | 0xF0230-0xF0254 | Half forms (consonant + virama) |
| U+0915-0939 +480 | 0xF0320-0xF0404 | RA-appended forms (consonant + virama + RA) |
| U+0915-0939 +720 | 0xF0410-0xF04F4 | RA-appended half forms (consonant + virama + RA + virama) |
Mapping formula: `to_deva_internal(c)` = `c - 0x0915 + 0xF0140` for U+0915-0939.
### Script tag gotcha
When a script-specific feature exists in GSUB (e.g. `ccmp` under `dev2`), HarfBuzz uses **only** the script-specific lookups and does **not** fall back to the DFLT script's lookups for that feature. Any substitutions needed for a specific script must be registered under that script's tag.
### languagesystem and language records
The `languagesystem` declarations in the preamble control which script/language records are created in the font tables. Key rules:
- `languagesystem` declarations must be at the **top level** of the feature file, not inside any `feature` block. Putting them inside `feature aalt { }` is invalid feaLib syntax and causes silent compilation failure.
- When a language-specific record exists (e.g. `dev2/MAR` from `languagesystem dev2 MAR;`), features registered under `script dev2;` only populate `dev2/dflt` — they are **not** automatically copied to `dev2/MAR`. The language record inherits only from DFLT, resulting in incomplete feature sets.
- Only declare language-specific records when you have `locl` or other language-differentiated features. Otherwise, use only `languagesystem <script> dflt;` to avoid partial feature inheritance that breaks DirectWrite and CoreText.
### Inspecting feature registration per script
To verify that features are correctly registered under each script:
```python
from fontTools.ttLib import TTFont
font = TTFont('OTFbuild/TerrarumSansBitmap.otf')
gsub = font['GSUB']
for sr in gsub.table.ScriptList.ScriptRecord:
tag = sr.ScriptTag
if sr.Script.DefaultLangSys:
feats = []
for idx in sr.Script.DefaultLangSys.FeatureIndex:
fr = gsub.table.FeatureList.FeatureRecord[idx]
feats.append(fr.FeatureTag)
print(f"{tag}/dflt: {' '.join(sorted(set(feats)))}")
for lsr in (sr.Script.LangSysRecord or []):
feats = []
for idx in lsr.LangSys.FeatureIndex:
fr = gsub.table.FeatureList.FeatureRecord[idx]
feats.append(fr.FeatureTag)
print(f"{tag}/{lsr.LangSysTag}: {' '.join(sorted(set(feats)))}")
```
Expected output for dev2: `dev2/dflt: abvs akhn blwf blws calt ccmp cjct half liga locl nukt pres psts rphf`. If language-specific records (e.g. `dev2/MAR`) appear with only `ccmp liga`, the language records have incomplete feature inheritance — remove the corresponding `languagesystem` declaration.
### Debugging feature compilation failures
The build writes `debugout_features.fea` with the raw feature code before compilation. When compilation fails, inspect this file to find syntax errors. Common issues:
- **`languagesystem` inside a feature block** — must be at the top level
- **Named lookup defined inside a feature block** — applies unconditionally to all input. Define the lookup outside the feature block and reference it via contextual rules inside.
- **Glyph not in font** — a substitution references a glyph name that doesn't exist in the font's glyph order (e.g. a control character was removed)
### HarfBuzz Indic shaper (dev2) feature order
Understanding feature application order is critical for Devanagari debugging:
1. **Pre-reordering** (Unicode order): `ccmp`
2. **Reordering**: HarfBuzz reorders pre-base matras (e.g. I-matra U+093F moves before the consonant)
3. **Post-reordering**: `nukt``akhn``rphf``half``blwf``cjct``pres``abvs``blws``psts``haln``calt`
4. **GPOS**: `kern``mark`/`abvm``mkmk`
Implication: GSUB rules that need to match pre-base matras adjacent to post-base marks (e.g. anusvara substitution triggered by I-matra) must go in `ccmp`, not `psts`, because reordering separates them.
### Cross-platform shaper differences (DirectWrite, CoreText, HarfBuzz)
The three major shapers behave differently for Devanagari. The font registers all Devanagari features under **both** `dev2` (new Indic) and `deva` (old Indic) script tags. HarfBuzz and DirectWrite use `dev2`; CoreText uses `deva`.
#### Script tag selection
| Shaper | Script tag used | Indic model |
|---|---|---|
| HarfBuzz | `dev2` | New Indic (ot-indic2) |
| DirectWrite | `dev2` | New Indic |
| CoreText | `deva` | Old Indic |
Both tags must exist, and all GSUB/GPOS features must be registered under both, otherwise CoreText silently breaks.
#### Feature order differences
**HarfBuzz (dev2, reference implementation)**:
1. Pre-reordering: `locl``ccmp`
2. Reordering (I-matra moves before consonant, reph moves to end)
3. Post-reordering: `nukt``akhn``rphf``half``blwf``cjct``pres``abvs``blws``psts``haln``calt`
4. GPOS: `kern``abvm``blwm`
**DirectWrite (dev2)**:
- `locl``nukt``akhn``rphf``rkrf``blwf``half``vatu``cjct``pres``abvs``blws``psts``haln``calt`
- GPOS: `kern``dist``abvm``blwm`
- **Does NOT apply `ccmp`** for the dev2 script. All lookups that must run before `nukt` (e.g. consonant-to-PUA mapping, anusvara upper) must be registered under `locl` instead.
**CoreText (deva)**:
- Applies `locl` and `ccmp`, but may apply `ccmp` **after** reordering (unlike HarfBuzz).
- Post-reordering features same as above: `nukt``akhn``rphf` → ... → `abvs` → ... → `psts`
- GPOS: `kern``abvm` (+ `mark`/`mkmk` if registered under `deva`)
#### Key behavioural differences
**1. ccmp timing (CoreText vs HarfBuzz)**
HarfBuzz applies `ccmp` in Unicode order (before reordering). CoreText may apply it after reordering. This breaks adjacency-based rules:
```
# In ccmp — works on HarfBuzz (Unicode order: C + matra + anusvara):
sub uni093F uni0902' lookup AnusvaraUpper; # I-matra + anusvara
# After reordering on CoreText: I-matra + [consonants] + anusvara
# The I-matra and anusvara are no longer adjacent → rule fails
```
**Fix**: duplicate these rules in `abvs` (post-reordering) with wildcard gaps:
```
sub uni093F @devaAny uni0902' lookup AnusvaraUpper;
sub uni093F @devaAny @devaAny uni0902' lookup AnusvaraUpper;
```
**2. Reph eligibility testing**
| Shaper | Method |
|---|---|
| HarfBuzz | Pattern-based (RA + halant + consonant at syllable start) |
| DirectWrite | `would_substitute([RA, virama], rphf)` with **Unicode** codepoints |
| CoreText | `would_substitute()` with Unicode codepoints (same as DW) |
The `rphf` feature must include a rule with the Unicode form of RA (`uni0930`), not just the PUA form. Otherwise DW and CT won't detect reph.
**3. Within-lookup glyph visibility (CoreText)**
In OpenType, a single lookup processes the glyph string left-to-right. Per spec, a substitution at position N should be visible when the lookup reaches position N+1. CoreText appears to **not** propagate substitutions within a single lookup pass to subsequent positions' backtrack context.
Example: two rules in one anonymous lookup:
```
sub @trigger uF010C' lookup ComplexReph; # rule at pos N: uF010C → uF010D
sub uF010D uF016C' lookup AnusvaraLower; # rule at pos N+1: needs uF010D in backtrack
```
On HarfBuzz/DirectWrite, rule 2 sees the updated `uF010D` at position N. On CoreText, it still sees the original `uF010C` → rule 2 fails to match.
**Fix**: split into separate **named lookups** so each runs as an independent pass:
```
lookup AbvsPass1 {
sub @trigger uF010C' lookup ComplexReph;
} AbvsPass1;
lookup AbvsPass2 {
sub uF010D uF016C' lookup AnusvaraLower;
} AbvsPass2;
feature abvs {
script dev2; lookup AbvsPass1; lookup AbvsPass2;
script deva; lookup AbvsPass1; lookup AbvsPass2;
} abvs;
```
**4. GPOS mark stacking heuristics**
When two marks share the same base without MarkToMark, each shaper applies different internal Y adjustments:
| Shaper | Internal Y shift |
|---|---|
| HarfBuzz | 0 (no heuristic) |
| DirectWrite | -100 |
| CoreText | -200 |
No single GPOS Y value satisfies all three. **Fix**: use explicit MarkToMark positioning (e.g. `AnusvaraToComplexReph`) which suppresses shaper heuristics and gives consistent results across all three.
**5. GPOS double-application with dev2+deva**
When both script tags exist, CoreText/DirectWrite may merge lookup lists from both scripts. Inline (anonymous) GPOS rules create separate lookups per script → cumulative positioning doubles. **Fix**: use **named lookups** for all GPOS contextual positioning so both scripts reference the same lookup index.
**6. mark/mkmk feature scoping**
The `mark` and `mkmk` features are registered under `deva` (for CoreText) but **not** `dev2`. Under `dev2`, all mark positioning goes through `abvm` instead. This prevents double-application on HarfBuzz/DirectWrite where `abvm` already contains the same mark/mkmk lookups.
```
# GPOS features per script:
# dev2/dflt: abvm kern
# deva/dflt: abvm kern mark mkmk
```
#### Practical rules
1. **Standalone lookups**: define all substitution/positioning lookups (e.g. `DevaConsonantMap`, `DevaVowelDecomp`, `ComplexReph`) **outside** any feature block, then reference from both `locl`/`ccmp` and script-specific features.
2. **locl mirrors ccmp** for Devanagari: DirectWrite skips `ccmp`, so anything that must run early (consonant mapping, anusvara upper, vowel decomposition) must also be in `locl`.
3. **abvs post-reordering fallbacks**: rules that depend on matra+anusvara adjacency (broken by reordering on CoreText) need wildcard-gap variants in `abvs`.
4. **Separate lookup passes**: if rule B's backtrack context depends on rule A's output at an adjacent position, put them in separate named lookups. CoreText may not propagate within-pass substitutions.
5. **Named GPOS lookups**: all contextual GPOS rules must use named lookups to avoid double-application across dev2/deva.
6. **MarkToMark for multi-mark stacking**: never rely on shaper heuristics for positioning multiple marks on the same base — always provide explicit MarkToMark.
Source: [Microsoft Devanagari shaping spec](https://learn.microsoft.com/en-us/typography/script-development/devanagari)

19
OTFbuild/Makefile Normal file
View File

@@ -0,0 +1,19 @@
PYTHON ?= python3
ASSETS ?= ../src/assets
OTF = TerrarumSansBitmap.otf
WOFF2 = TerrarumSansBitmap.woff2
all: $(OTF) $(WOFF2)
$(OTF): $(wildcard $(ASSETS)/*.tga) build_font.py font_builder.py glyph_parser.py \
bitmap_tracer.py tga_reader.py keming_machine.py hangul.py sheet_config.py \
opentype_features.py
$(PYTHON) build_font.py $(ASSETS) -o $@
$(WOFF2): $(OTF) otf2woff2.py
$(PYTHON) otf2woff2.py $< $@
clean:
rm -f $(OTF) $(WOFF2)
.PHONY: all clean

97
OTFbuild/bitmap_tracer.py Normal file
View File

@@ -0,0 +1,97 @@
"""
Convert 1-bit bitmap arrays to TrueType quadratic outlines.
Each set pixel becomes part of a rectangle contour drawn clockwise.
Adjacent identical horizontal runs are merged vertically into rectangles.
Scale: x_left = col * SCALE, y_top = (BASELINE_ROW - row) * SCALE
where BASELINE_ROW = 16 (ascent in pixels).
"""
from typing import Dict, List, Tuple
import sheet_config as SC
SCALE = SC.SCALE
BASELINE_ROW = 16 # pixels from top to baseline
def trace_bitmap(bitmap, glyph_width_px):
"""
Convert a bitmap to a list of rectangle contours.
Each rectangle is ((x0, y0), (x1, y1)) in font units, where:
- (x0, y0) is bottom-left
- (x1, y1) is top-right
Returns list of (x0, y0, x1, y1) tuples representing rectangles.
"""
if not bitmap or not bitmap[0]:
return []
h = len(bitmap)
w = len(bitmap[0])
# Step 1: Find horizontal runs per row
runs = [] # list of (row, col_start, col_end)
for row in range(h):
col = 0
while col < w:
if bitmap[row][col]:
start = col
while col < w and bitmap[row][col]:
col += 1
runs.append((row, start, col))
else:
col += 1
# Step 2: Merge vertically adjacent identical runs into rectangles
rects = [] # (row_start, row_end, col_start, col_end)
used = [False] * len(runs)
for i, (row, cs, ce) in enumerate(runs):
if used[i]:
continue
# Try to extend this run downward
row_end = row + 1
j = i + 1
while j < len(runs):
r2, cs2, ce2 = runs[j]
if r2 > row_end:
break
if r2 == row_end and cs2 == cs and ce2 == ce and not used[j]:
used[j] = True
row_end = r2 + 1
j += 1
rects.append((row, row_end, cs, ce))
# Step 3: Convert to font coordinates
contours = []
for row_start, row_end, col_start, col_end in rects:
x0 = col_start * SCALE
x1 = col_end * SCALE
y_top = (BASELINE_ROW - row_start) * SCALE
y_bottom = (BASELINE_ROW - row_end) * SCALE
contours.append((x0, y_bottom, x1, y_top))
return contours
def draw_glyph_to_pen(contours, pen, x_offset=0, y_offset=0):
"""
Draw rectangle contours to a TTGlyphPen or similar pen.
Each rectangle is drawn as a clockwise closed contour (4 on-curve points).
x_offset/y_offset shift all contours (used for alignment positioning).
"""
for x0, y0, x1, y1 in contours:
ax0 = x0 + x_offset
ax1 = x1 + x_offset
ay0 = y0 + y_offset
ay1 = y1 + y_offset
# Clockwise: bottom-left -> top-left -> top-right -> bottom-right
pen.moveTo((ax0, ay0))
pen.lineTo((ax0, ay1))
pen.lineTo((ax1, ay1))
pen.lineTo((ax1, ay0))
pen.closePath()

93
OTFbuild/build_font.py Normal file
View File

@@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""
Terrarum Sans Bitmap OTF Builder v2 — Python + fonttools
Builds a TTF font with both vector-traced outlines (TrueType glyf)
and embedded bitmap strike (EBDT/EBLC) from TGA sprite sheets.
Usage:
python3 OTFbuild/build_font.py src/assets -o OTFbuild/TerrarumSansBitmap.otf
Options:
--no-bitmap Skip EBDT/EBLC bitmap strike
--no-features Skip GSUB/GPOS OpenType features
"""
import argparse
import sys
import os
# Add OTFbuild dir to path for imports
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from font_builder import build_font
def main():
parser = argparse.ArgumentParser(
description="Build Terrarum Sans Bitmap TTF from TGA sprite sheets"
)
parser.add_argument(
"assets_dir",
help="Path to assets directory containing TGA sprite sheets"
)
parser.add_argument(
"-o", "--output",
default="OTFbuild/TerrarumSansBitmap.otf",
help="Output OTF file path (default: OTFbuild/TerrarumSansBitmap.otf)"
)
parser.add_argument(
"--no-bitmap",
action="store_true",
help="Skip EBDT/EBLC bitmap strike"
)
parser.add_argument(
"--no-features",
action="store_true",
help="Skip GSUB/GPOS OpenType features"
)
args = parser.parse_args()
if not os.path.isdir(args.assets_dir):
print(f"Error: assets directory not found: {args.assets_dir}", file=sys.stderr)
sys.exit(1)
# Ensure output directory exists
output_dir = os.path.dirname(args.output)
if output_dir:
os.makedirs(output_dir, exist_ok=True)
print(f"Terrarum Sans Bitmap OTF Builder v2")
print(f" Assets: {args.assets_dir}")
print(f" Output: {args.output}")
print()
build_font(
assets_dir=args.assets_dir,
output_path=args.output,
no_bitmap=args.no_bitmap,
no_features=args.no_features,
)
# Run OpenType Sanitizer to catch issues browsers would reject
try:
import ots
print("\nRunning OpenType Sanitizer...")
result = ots.sanitize(args.output, capture_output=True)
if result.returncode == 0:
print(" OTS: passed")
else:
print(f" OTS: FAILED (exit code {result.returncode})", file=sys.stderr)
if result.stderr:
for line in result.stderr.decode().strip().splitlines():
print(f" {line}", file=sys.stderr)
sys.exit(1)
except ImportError:
print("\nWarning: opentype-sanitizer not installed, skipping OTS validation",
file=sys.stderr)
print(" Install with: pip install opentype-sanitizer", file=sys.stderr)
if __name__ == "__main__":
main()

Binary file not shown.

721
OTFbuild/font_builder.py Normal file
View File

@@ -0,0 +1,721 @@
"""
Orchestrate fonttools TTFont assembly.
1. Parse all sheets -> glyphs dict
2. Compose Hangul -> add to dict
3. Expand replacewith directives
4. Create glyph order and cmap
5. Trace all bitmaps -> CFF charstrings
6. Set hmtx, hhea, OS/2, head, name, post
7. Generate and compile OpenType features via feaLib
8. Add EBDT/EBLC bitmap strike at ppem=20
9. Save OTF
"""
import time
from typing import Dict
from fontTools.fontBuilder import FontBuilder
from fontTools.pens.t2CharStringPen import T2CharStringPen
from fontTools.feaLib.builder import addOpenTypeFeatures
from fontTools.ttLib import TTFont
import io
from glyph_parser import ExtractedGlyph, GlyphProps, parse_all_sheets
from hangul import compose_hangul, get_jamo_gsub_data, HANGUL_PUA_BASE
from bitmap_tracer import trace_bitmap, draw_glyph_to_pen, SCALE, BASELINE_ROW
from keming_machine import generate_kerning_pairs
from opentype_features import generate_features, glyph_name
import sheet_config as SC
FONT_VERSION = "1.15"
# Codepoints that get cmap entries (user-visible)
# PUA forms used internally by GSUB get glyphs but NO cmap entries
def _should_have_cmap(cp):
"""Determine if a codepoint should have a cmap entry."""
# Standard Unicode characters always get cmap entries
if cp < 0xE000:
return True
# Custom sym PUA range
if 0xE000 <= cp <= 0xE0FF:
return True
# Codestyle PUA
if 0xF0520 <= cp <= 0xF057F:
return True
# Hangul syllables
if 0xAC00 <= cp <= 0xD7A3:
return True
# Hangul compat jamo
if 0x3130 <= cp <= 0x318F:
return True
# SMP characters (Enclosed Alphanumeric Supplement, Hentaigana, etc.)
if 0x1F100 <= cp <= 0x1F1FF:
return True
if 0x1B000 <= cp <= 0x1B16F:
return True
# Unicode noncharacters — never map these (U+FFFE, U+FFFF are reserved;
# format 4 cmap uses 0xFFFF as a sentinel, so mapping it causes OTS rejection)
if cp >= 0xFFFE and cp <= 0xFFFF:
return False
# Everything in standard Unicode ranges (up to 0xFFFF plus SMP)
if cp <= 0xFFFF:
return True
# Internal PUA forms — GSUB-only, no cmap
if 0xF0000 <= cp <= 0xF0FFF:
return False
# Internal control characters
if 0xFFE00 <= cp <= 0xFFFFF:
return False
return True
def _expand_replacewith(glyphs):
"""
Find glyphs with 'replacewith' directive and generate GSUB multiple
substitution data. Returns list of (source_cp, [target_cp, ...]).
A replacewith glyph's extInfo contains up to 7 codepoints that the
glyph expands to (e.g. U+01C7 "LJ" → [0x4C, 0x4A]).
"""
replacements = []
for cp, g in glyphs.items():
if g.props.is_pragma("replacewith"):
targets = []
count = g.props.required_ext_info_count()
for i in range(count):
val = g.props.ext_info[i]
if val != 0:
targets.append(val)
if targets:
replacements.append((cp, targets))
return replacements
def build_font(assets_dir, output_path, no_bitmap=False, no_features=False):
"""Build the complete OTF font."""
t0 = time.time()
# Step 1: Parse all sheets
print("Step 1: Parsing glyph sheets...")
glyphs = parse_all_sheets(assets_dir)
print(f" Parsed {len(glyphs)} glyphs from sheets")
# Step 2: Compose Hangul
print("Step 2: Composing Hangul syllables...")
hangul_glyphs = compose_hangul(assets_dir)
glyphs.update(hangul_glyphs)
print(f" Total glyphs after Hangul: {len(glyphs)}")
# Step 2b: Copy PUA consonant glyphs to Unicode positions
# In the bitmap font, consonants U+0915-0939 have width=0 and empty bitmaps
# because the engine normalises them to PUA forms (0xF0140+) before rendering.
# For OTF, we need the Unicode positions to have actual outlines so that
# consonants render even without GSUB shaping.
print("Step 2b: Populating Devanagari consonant glyphs from PUA forms...")
deva_copied = 0
for uni_cp in range(0x0915, 0x093A):
try:
pua_cp = SC.to_deva_internal(uni_cp)
except ValueError:
continue
if pua_cp in glyphs and uni_cp in glyphs:
pua_g = glyphs[pua_cp]
uni_g = glyphs[uni_cp]
if uni_g.props.width == 0 and pua_g.props.width > 0:
uni_g.props.width = pua_g.props.width
uni_g.bitmap = pua_g.bitmap
uni_g.color_bitmap = pua_g.color_bitmap
deva_copied += 1
# Also copy nukta consonant forms U+0958-095F
for uni_cp in range(0x0958, 0x0960):
try:
pua_cp = SC.to_deva_internal(uni_cp)
except ValueError:
continue
if pua_cp in glyphs and uni_cp in glyphs:
pua_g = glyphs[pua_cp]
uni_g = glyphs[uni_cp]
if uni_g.props.width == 0 and pua_g.props.width > 0:
uni_g.props.width = pua_g.props.width
uni_g.bitmap = pua_g.bitmap
uni_g.color_bitmap = pua_g.color_bitmap
deva_copied += 1
print(f" Copied {deva_copied} consonant glyphs from PUA forms")
# Step 3: Expand replacewith directives
print("Step 3: Processing replacewith directives...")
replacewith_subs = _expand_replacewith(glyphs)
print(f" Found {len(replacewith_subs)} replacewith substitutions")
# Step 3b: Compose fallback bitmaps for replacewith glyphs
# Glyphs with replacewith directives have width=0 and no bitmap; they
# rely on GSUB ccmp to expand into their target sequence. Renderers
# without GSUB support would show whitespace. Build a composite
# bitmap by concatenating the target glyphs' bitmaps side by side.
print("Step 3b: Composing fallback bitmaps for replacewith glyphs...")
composed = 0
for src_cp, target_cps in replacewith_subs:
src_g = glyphs.get(src_cp)
if src_g is None or src_g.props.width > 0:
continue # already has content (e.g. Deva consonants fixed above)
# Resolve target glyphs
target_gs = [glyphs.get(t) for t in target_cps]
if not all(target_gs):
continue
# Compute total advance and composite height
total_width = sum(g.props.width for g in target_gs)
if total_width == 0:
continue
bm_height = max((len(g.bitmap) for g in target_gs if g.bitmap), default=SC.H)
# Build composite bitmap
composite = [[0] * total_width for _ in range(bm_height)]
x = 0
for tg in target_gs:
if not tg.bitmap:
x += tg.props.width
continue
cols = min(tg.props.width, len(tg.bitmap[0])) if tg.props.width > 0 else len(tg.bitmap[0])
nudge = tg.props.nudge_x
for row in range(min(len(tg.bitmap), bm_height)):
for col in range(cols):
dst_col = x + col - nudge
if 0 <= dst_col < total_width and tg.bitmap[row][col]:
composite[row][dst_col] = 1
if tg.props.width > 0:
x += tg.props.width
# Zero-width targets (combining marks) overlay at current position
src_g.props.width = total_width
src_g.bitmap = composite
composed += 1
print(f" Composed {composed} fallback bitmaps")
# Step 3c: Identify combining marks for zero advance width
# Glyphs with write_on_top >= 0 are combining marks positioned via
# GPOS mark-to-base. In OpenType they must have zero advance width;
# otherwise the cursor advances past the base and diacritics appear
# shifted to the right. We record them here but keep props.width
# intact so the mark anchor calculation can use the original width.
mark_cps = set()
for cp, g in glyphs.items():
if g.props.write_on_top >= 0 and g.props.width > 0:
mark_cps.add(cp)
if mark_cps:
print(f"Step 3c: Found {len(mark_cps)} combining marks to zero in hmtx")
# Step 4: Create glyph order and cmap
print("Step 4: Building glyph order and cmap...")
glyph_order = [".notdef"]
cmap = {}
glyph_set = set()
# Sort codepoints for deterministic output
sorted_cps = sorted(glyphs.keys())
for cp in sorted_cps:
g = glyphs[cp]
if g.props.is_illegal:
continue
# Skip C0/C1 control characters and DEL — some platforms render
# their traced bitmaps, which is undesirable.
if cp <= 0x001F or cp == 0x007F or 0x0080 <= cp <= 0x009F:
continue
name = glyph_name(cp)
if name == ".notdef":
continue
if name in glyph_set:
continue
glyph_order.append(name)
glyph_set.add(name)
if _should_have_cmap(cp):
cmap[cp] = name
print(f" Glyph order: {len(glyph_order)} glyphs, cmap: {len(cmap)} entries")
# Step 4a: Detect coloured glyphs and prepare COLR layer data
print("Step 4a: Detecting coloured glyphs...")
colr_layer_data = {} # base_name -> list of (layer_name, colour_rgb)
palette_colours = {} # (r, g, b) -> palette_index
layer_bitmaps = {} # layer_name -> 1-bit bitmap
layer_insert = [] # (after_name, [layer_names]) for glyph_order insertion
for cp in sorted_cps:
g = glyphs[cp]
if g.props.is_illegal or g.color_bitmap is None:
continue
name = glyph_name(cp)
if name == ".notdef" or name not in glyph_set:
continue
# Group pixels by RGB value -> per-colour 1-bit masks
colour_pixels = {} # (r, g, b) -> set of (row, col)
cbm = g.color_bitmap
for row in range(len(cbm)):
for col in range(len(cbm[row])):
px = cbm[row][col]
a = px & 0xFF
if a == 0:
continue
r = (px >> 24) & 0xFF
g_ch = (px >> 16) & 0xFF
b = (px >> 8) & 0xFF
rgb = (r, g_ch, b)
if rgb not in colour_pixels:
colour_pixels[rgb] = set()
colour_pixels[rgb].add((row, col))
if not colour_pixels:
continue
if len(colour_pixels) == 1 and (255, 255, 255) in colour_pixels:
# Only white pixels — no colour layers needed
continue
# Assign palette indices for each unique colour
for rgb in colour_pixels:
if rgb not in palette_colours:
palette_colours[rgb] = len(palette_colours)
# Generate layer glyphs
h = len(cbm)
w = len(cbm[0]) if h > 0 else 0
layers = []
layer_names = []
for i, (rgb, positions) in enumerate(sorted(colour_pixels.items())):
layer_name = f"{name}.clr{i}"
# Build 1-bit mask for this colour
mask = [[0] * w for _ in range(h)]
for (row, col) in positions:
mask[row][col] = 1
layer_bitmaps[layer_name] = mask
layers.append((layer_name, rgb))
layer_names.append(layer_name)
colr_layer_data[name] = layers
layer_insert.append((name, layer_names))
# Insert layer glyph names into glyph_order immediately after their base glyph
for base_name, lnames in layer_insert:
idx = glyph_order.index(base_name)
for j, ln in enumerate(lnames):
glyph_order.insert(idx + 1 + j, ln)
glyph_set.add(ln)
if colr_layer_data:
print(f" Found {len(colr_layer_data)} coloured glyphs, {len(palette_colours)} palette colours, {sum(len(v) for v in colr_layer_data.values())} layer glyphs")
else:
print(" No coloured glyphs found")
# Step 5: Build font with fonttools (CFF/OTF)
print("Step 5: Building font tables...")
fb = FontBuilder(SC.UNITS_PER_EM, isTTF=False)
fb.setupGlyphOrder(glyph_order)
fb.setupCharacterMap(cmap)
# Step 6: Trace bitmaps -> CFF charstrings
print("Step 6: Tracing bitmaps to CFF outlines...")
charstrings = {}
# .notdef glyph (empty box)
pen = T2CharStringPen(SC.UNITS_PER_EM // 2, None)
pen.moveTo((0, 0))
pen.lineTo((0, SC.ASCENT))
pen.lineTo((SC.UNITS_PER_EM // 2, SC.ASCENT))
pen.lineTo((SC.UNITS_PER_EM // 2, 0))
pen.closePath()
_m = 2 * SCALE
pen.moveTo((_m, _m))
pen.lineTo((SC.UNITS_PER_EM // 2 - _m, _m))
pen.lineTo((SC.UNITS_PER_EM // 2 - _m, SC.ASCENT - _m))
pen.lineTo((_m, SC.ASCENT - _m))
pen.closePath()
charstrings[".notdef"] = pen.getCharString()
_unihan_cps = set(SC.CODE_RANGE[SC.SHEET_UNIHAN])
_base_offsets = {} # glyph_name -> (x_offset, y_offset) for COLR layers
traced_count = 0
for cp in sorted_cps:
g = glyphs[cp]
if g.props.is_illegal:
continue
name = glyph_name(cp)
if name == ".notdef" or name not in glyph_set:
continue
advance = 0 if cp in mark_cps else g.props.width * SCALE
# Compute alignment offset (lsb shift).
# The Kotlin code draws the full cell at an offset position:
# ALIGN_LEFT: offset = 0
# ALIGN_RIGHT: offset = width - W_VAR_INIT (negative)
# ALIGN_CENTRE: offset = ceil((width - W_VAR_INIT) / 2) (negative)
# ALIGN_BEFORE: offset = 0
# The bitmap cell width depends on the sheet type.
# nudge_x shifts the glyph left by that many pixels in the
# bitmap engine. The Kotlin engine always applies nudge_x to
# the drawing position (posXbuffer = -nudgeX + ...) and the
# next glyph compensates via extraWidth, so the effective
# origin-to-origin advance stays at `width`. We must bake
# the same leftward shift into the contour x_offset.
import math
# The Kotlin engine always uses W_VAR_INIT for alignment calculations,
# even for EXTRAWIDE sheets. Use W_VAR_INIT here to match.
bm_cols = SC.W_VAR_INIT
if g.props.align_where == SC.ALIGN_RIGHT:
x_offset = (g.props.width - bm_cols) * SCALE
elif g.props.align_where == SC.ALIGN_CENTRE:
x_offset = math.ceil((g.props.width - bm_cols) / 2) * SCALE
else:
x_offset = 0
x_offset -= g.props.nudge_x * SCALE
# For STACK_DOWN marks (below-base diacritics), negative nudge_y
# means "shift content down to below baseline". The sign convention
# is opposite to non-marks where positive nudge_y means shift down.
if g.props.stack_where == SC.STACK_DOWN and g.props.write_on_top >= 0:
y_offset = g.props.nudge_y * SCALE
else:
y_offset = -g.props.nudge_y * SCALE
# Unihan glyphs are 16px tall in a 20px cell; the bitmap engine
# centres them vertically with offsetUnihan = (H - H_UNIHAN) / 2.
if cp in _unihan_cps:
y_offset -= ((SC.H - SC.H_UNIHAN) // 2) * SCALE
# Hangul jungseong/jongseong PUA variants (rows 15-18) have zero
# advance and overlay the preceding choseong. Shift their outlines
# left by one syllable cell width so they render at the same position.
if cp >= HANGUL_PUA_BASE:
_pua_row = (cp - HANGUL_PUA_BASE) // 256
if 15 <= _pua_row <= 18:
x_offset -= SC.W_HANGUL_BASE * SCALE
# Store offsets for COLR layer glyphs
if name in colr_layer_data:
_base_offsets[name] = (x_offset, y_offset)
contours = trace_bitmap(g.bitmap, g.props.width)
pen = T2CharStringPen(advance, None)
if contours:
draw_glyph_to_pen(contours, pen, x_offset=x_offset, y_offset=y_offset)
traced_count += 1
charstrings[name] = pen.getCharString()
# Trace COLR layer glyphs
layer_traced = 0
for base_name, layers in colr_layer_data.items():
base_xoff, base_yoff = _base_offsets.get(base_name, (0, 0))
for layer_name, _rgb in layers:
lbm = layer_bitmaps[layer_name]
# Find the effective glyph width from the base glyph's bitmap
lw = len(lbm[0]) if lbm and lbm[0] else 0
contours = trace_bitmap(lbm, lw)
pen = T2CharStringPen(0, None) # advance width 0 for layers
if contours:
draw_glyph_to_pen(contours, pen, x_offset=base_xoff, y_offset=base_yoff)
layer_traced += 1
charstrings[layer_name] = pen.getCharString()
print(f" Traced {traced_count} glyphs with outlines" + (f" + {layer_traced} colour layers" if layer_traced else ""))
fb.setupCFF(
psName="TerrarumSansBitmap-Regular",
fontInfo={},
charStringsDict=charstrings,
privateDict={},
)
# Step 7: Set metrics
print("Step 7: Setting font metrics...")
metrics = {}
metrics[".notdef"] = (SC.UNITS_PER_EM // 2, 0)
for cp in sorted_cps:
g = glyphs[cp]
if g.props.is_illegal:
continue
name = glyph_name(cp)
if name == ".notdef" or name not in glyph_set:
continue
advance = 0 if cp in mark_cps else g.props.width * SCALE
metrics[name] = (advance, 0)
# Add zero-advance metrics for COLR layer glyphs
for _base_name, layers in colr_layer_data.items():
for layer_name, _rgb in layers:
metrics[layer_name] = (0, 0)
fb.setupHorizontalMetrics(metrics)
fb.setupHorizontalHeader(
ascent=SC.ASCENT,
descent=-SC.DESCENT
)
fb.setupNameTable({
"copyright": "Copyright (c) 2026 CuriousTorvald (curioustorvald.com), with Reserved Font Name TERRARUM.",
"familyName": "Terrarum Sans Bitmap",
"styleName": "Regular",
"uniqueFontIdentifier": "TerrarumSansBitmap-Regular-"+FONT_VERSION,
"fullName": "Terrarum Sans Bitmap Regular",
"psName": "TerrarumSansBitmap-Regular",
"version": FONT_VERSION,
"licenseDescription": "This Font Software is licensed under the SIL Open Font License, Version 1.1.",
"licenseInfoURL": "https://openfontlicense.org/"
})
fb.setupOS2(
sTypoAscender=SC.ASCENT,
sTypoDescender=-SC.DESCENT,
sTypoLineGap=SC.LINE_GAP,
usWinAscent=SC.ASCENT,
usWinDescent=SC.DESCENT,
sxHeight=SC.X_HEIGHT,
sCapHeight=SC.CAP_HEIGHT,
fsType=0,
)
unix_ts = int(time.time())
opentype_ts = unix_ts + 2082844800
fb.setupPost()
fb.setupHead(
unitsPerEm=SC.UNITS_PER_EM,
created=opentype_ts,
modified=opentype_ts,
)
font = fb.font
# Step 7a: Build COLR v0 / CPAL tables
if colr_layer_data:
print("Step 7a: Building COLR v0/CPAL tables...")
from fontTools.colorLib.builder import buildCOLR, buildCPAL
# CPAL: single palette normalised to 0..1
palette = [(0, 0, 0, 1.0)] * len(palette_colours)
for (r, g, b), idx in palette_colours.items():
palette[idx] = (r / 255, g / 255, b / 255, 1.0)
font["CPAL"] = buildCPAL([palette])
# COLR v0: list of (layer_glyph_name, palette_index) per base glyph
colr_v0 = {}
for base_name, layers in colr_layer_data.items():
colr_v0[base_name] = [
(layer_name, palette_colours[rgb])
for layer_name, rgb in layers
]
font["COLR"] = buildCOLR(colr_v0, version=0)
print(f" COLR v0: {len(colr_v0)} base glyphs, {len(palette)} palette entries")
# Step 8: Generate and compile OpenType features
if not no_features:
print("Step 8: Generating OpenType features...")
kern_pairs = generate_kerning_pairs(glyphs)
print(f" {len(kern_pairs)} kerning pairs")
jamo_data = get_jamo_gsub_data()
fea_code = generate_features(glyphs, kern_pairs, glyph_set,
replacewith_subs=replacewith_subs,
jamo_data=jamo_data)
if fea_code.strip():
print(" Compiling features with feaLib...")
try:
# Obtain raw .fea text for debugging
with open("debugout_features.fea", "w") as text_file:
text_file.write(fea_code)
fea_stream = io.StringIO(fea_code)
addOpenTypeFeatures(font, fea_stream)
print(" Features compiled successfully")
except Exception as e:
print(f" [WARNING] Feature compilation failed: {e}")
print(" Continuing without OpenType features")
else:
print(" No features to compile")
else:
print("Step 8: Skipping OpenType features (--no-features)")
# Step 9: Add bitmap strike (EBDT/EBLC)
if not no_bitmap:
print("Step 9: Adding bitmap strike...")
_add_bitmap_strike(font, glyphs, glyph_order, glyph_set)
else:
print("Step 9: Skipping bitmap strike (--no-bitmap)")
# Save
print(f"Saving to {output_path}...")
font.save(output_path)
elapsed = time.time() - t0
print(f"Done! Built {len(glyph_order)} glyphs in {elapsed:.1f}s")
print(f"Output: {output_path}")
def _add_bitmap_strike(font, glyphs, glyph_order, glyph_set):
"""Add EBDT/EBLC embedded bitmap strike at ppem=20 via TTX roundtrip."""
import tempfile
import os as _os
ppem = 20
name_to_id = {name: idx for idx, name in enumerate(glyph_order)}
bitmap_entries = []
for name in glyph_order:
if name == ".notdef":
continue
cp = _name_to_cp(name)
if cp is None or cp not in glyphs:
continue
g = glyphs[cp]
if g.props.is_illegal or g.props.width == 0:
continue
bitmap = g.bitmap
h = len(bitmap)
w = len(bitmap[0]) if h > 0 else 0
if w == 0 or h == 0:
continue
hex_rows = []
for row in bitmap:
row_bytes = bytearray()
for col_start in range(0, w, 8):
byte_val = 0
for bit in range(8):
col = col_start + bit
if col < w and row[col]:
byte_val |= (0x80 >> bit)
row_bytes.append(byte_val)
hex_rows.append(row_bytes.hex())
bitmap_entries.append({
'name': name,
'gid': name_to_id.get(name, 0),
'height': h,
'width': w,
'advance': g.props.width,
'hex_rows': hex_rows,
})
if not bitmap_entries:
print(" No bitmap data to embed")
return
gid_sorted = sorted(bitmap_entries, key=lambda e: e['gid'])
runs = []
current_run = [gid_sorted[0]]
for i in range(1, len(gid_sorted)):
if gid_sorted[i]['gid'] == gid_sorted[i-1]['gid'] + 1:
current_run.append(gid_sorted[i])
else:
runs.append(current_run)
current_run = [gid_sorted[i]]
runs.append(current_run)
ebdt_xml = ['<EBDT>', '<header version="2.0"/>', '<strikedata index="0">']
for entry in gid_sorted:
ebdt_xml.append(f' <cbdt_bitmap_format_1 name="{entry["name"]}">')
ebdt_xml.append(f' <SmallGlyphMetrics>')
ebdt_xml.append(f' <height value="{entry["height"]}"/>')
ebdt_xml.append(f' <width value="{entry["width"]}"/>')
ebdt_xml.append(f' <BearingX value="0"/>')
ebdt_xml.append(f' <BearingY value="{BASELINE_ROW}"/>')
ebdt_xml.append(f' <Advance value="{entry["advance"]}"/>')
ebdt_xml.append(f' </SmallGlyphMetrics>')
ebdt_xml.append(f' <rawimagedata>')
for hr in entry['hex_rows']:
ebdt_xml.append(f' {hr}')
ebdt_xml.append(f' </rawimagedata>')
ebdt_xml.append(f' </cbdt_bitmap_format_1>')
ebdt_xml.append('</strikedata>')
ebdt_xml.append('</EBDT>')
all_gids = [e['gid'] for e in gid_sorted]
desc = -(SC.H - BASELINE_ROW)
def _line_metrics_xml(direction, caret_num=1):
return [
f' <sbitLineMetrics direction="{direction}">',
f' <ascender value="{BASELINE_ROW}"/>',
f' <descender value="{desc}"/>',
f' <widthMax value="{SC.W_WIDEVAR_INIT}"/>',
f' <caretSlopeNumerator value="{caret_num}"/>',
' <caretSlopeDenominator value="0"/>',
' <caretOffset value="0"/>',
' <minOriginSB value="0"/>',
' <minAdvanceSB value="0"/>',
f' <maxBeforeBL value="{BASELINE_ROW}"/>',
f' <minAfterBL value="{desc}"/>',
' <pad1 value="0"/>',
' <pad2 value="0"/>',
f' </sbitLineMetrics>',
]
eblc_xml = [
'<EBLC>', '<header version="2.0"/>',
'<strike index="0">', ' <bitmapSizeTable>',
' <colorRef value="0"/>',
]
eblc_xml.extend(_line_metrics_xml("hori", 1))
eblc_xml.extend(_line_metrics_xml("vert", 0))
eblc_xml.extend([
f' <startGlyphIndex value="{all_gids[0]}"/>',
f' <endGlyphIndex value="{all_gids[-1]}"/>',
f' <ppemX value="{ppem}"/>',
f' <ppemY value="{ppem}"/>',
' <bitDepth value="1"/>',
' <flags value="1"/>',
' </bitmapSizeTable>',
])
for run in runs:
first_gid = run[0]['gid']
last_gid = run[-1]['gid']
eblc_xml.append(f' <eblc_index_sub_table_1 imageFormat="1" firstGlyphIndex="{first_gid}" lastGlyphIndex="{last_gid}">')
for entry in run:
eblc_xml.append(f' <glyphLoc name="{entry["name"]}"/>')
eblc_xml.append(' </eblc_index_sub_table_1>')
eblc_xml.append('</strike>')
eblc_xml.append('</EBLC>')
try:
ttx_content = '<?xml version="1.0" encoding="UTF-8"?>\n<ttFont>\n'
ttx_content += '\n'.join(ebdt_xml) + '\n'
ttx_content += '\n'.join(eblc_xml) + '\n'
ttx_content += '</ttFont>\n'
with tempfile.NamedTemporaryFile(mode='w', suffix='.ttx', delete=False) as f:
f.write(ttx_content)
ttx_path = f.name
font.importXML(ttx_path)
_os.unlink(ttx_path)
print(f" Added bitmap strike at {ppem}ppem with {len(bitmap_entries)} glyphs ({len(runs)} index subtables)")
except Exception as e:
print(f" [WARNING] Bitmap strike failed: {e}")
print(" Continuing without bitmap strike")
def _name_to_cp(name):
"""Convert glyph name back to codepoint."""
if name == ".notdef":
return None
if name == "space":
return 0x20
if name.startswith("uni"):
try:
return int(name[3:], 16)
except ValueError:
return None
if name.startswith("u"):
try:
return int(name[1:], 16)
except ValueError:
return None
return None

505
OTFbuild/glyph_parser.py Normal file
View File

@@ -0,0 +1,505 @@
"""
Extract glyph bitmaps and tag-column properties from TGA sprite sheets.
Ported from TerrarumSansBitmap.kt:buildWidthTable() and GlyphSheetParser.kt.
Enhancement over v1: extracts all 6 diacritics anchors for GPOS mark feature.
"""
import os
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Tuple
from tga_reader import TgaImage, read_tga
import sheet_config as SC
@dataclass
class DiacriticsAnchor:
type: int
x: int
y: int
x_used: bool
y_used: bool
@dataclass
class GlyphProps:
width: int
is_low_height: bool = False
nudge_x: int = 0
nudge_y: int = 0
diacritics_anchors: List[DiacriticsAnchor] = field(default_factory=lambda: [
DiacriticsAnchor(i, 0, 0, False, False) for i in range(6)
])
align_where: int = 0
write_on_top: int = -1
stack_where: int = 0
ext_info: List[int] = field(default_factory=lambda: [0] * 15)
has_kern_data: bool = False
is_kern_y_type: bool = False
kerning_mask: int = 255
directive_opcode: int = 0
directive_arg1: int = 0
directive_arg2: int = 0
@property
def is_illegal(self):
return self.directive_opcode == 255
def required_ext_info_count(self):
if self.stack_where == SC.STACK_BEFORE_N_AFTER:
return 2
if 0b10000_000 <= self.directive_opcode <= 0b10000_111:
return 7
return 0
def is_pragma(self, pragma):
if pragma == "replacewith":
return 0b10000_000 <= self.directive_opcode <= 0b10000_111
return False
@dataclass
class ExtractedGlyph:
codepoint: int
props: GlyphProps
bitmap: List[List[int]] # [row][col], 0 or 1
color_bitmap: Optional[List[List[int]]] = None # [row][col], RGBA8888 values
def _is_coloured_pixel(px):
"""Return True if the pixel is visible (A > 0) and non-white (R+G+B < 765)."""
a = px & 0xFF
if a == 0:
return False
r = (px >> 24) & 0xFF
g = (px >> 16) & 0xFF
b = (px >> 8) & 0xFF
return (r + g + b) < 765
def _tagify(pixel):
"""Return 0 if alpha channel is zero, else return the original value."""
return 0 if (pixel & 0xFF) == 0 else pixel
def _signed_byte(val):
"""Convert unsigned byte to signed."""
return val - 256 if val >= 128 else val
def _parse_diacritics_anchors(image, code_start_x, code_start_y):
"""Parse 6 diacritics anchors from tag column rows 11-14."""
anchors = []
for i in range(6):
y_pos = 13 - (i // 3) * 2
shift = (3 - (i % 3)) * 8
y_pixel = _tagify(image.get_pixel(code_start_x, code_start_y + y_pos))
x_pixel = _tagify(image.get_pixel(code_start_x, code_start_y + y_pos + 1))
y_used = ((y_pixel >> shift) & 128) != 0
x_used = ((x_pixel >> shift) & 128) != 0
y_val = (y_pixel >> shift) & 127 if y_used else 0
x_val = (x_pixel >> shift) & 127 if x_used else 0
anchors.append(DiacriticsAnchor(i, x_val, y_val, x_used, y_used))
return anchors
def parse_variable_sheet(image, sheet_index, cell_w, cell_h, cols, is_xy_swapped):
"""Parse a variable-width sheet: extract tag column for properties, bitmap for glyph."""
code_range = SC.CODE_RANGE[sheet_index]
binary_code_offset = cell_w - 1 # tag column is last pixel column of cell
result = {}
for index, code in enumerate(code_range):
if is_xy_swapped:
cell_x = (index // cols) * cell_w
cell_y = (index % cols) * cell_h
else:
cell_x = (index % cols) * cell_w
cell_y = (index // cols) * cell_h
code_start_x = cell_x + binary_code_offset
code_start_y = cell_y
# Width (5 bits)
width = 0
for y in range(5):
if image.get_pixel(code_start_x, code_start_y + y) & 0xFF:
width |= (1 << y)
is_low_height = (image.get_pixel(code_start_x, code_start_y + 5) & 0xFF) != 0
# Kerning data
kerning_bit1 = _tagify(image.get_pixel(code_start_x, code_start_y + 6))
# kerning_bit2 and kerning_bit3 are reserved
is_kern_y_type = (kerning_bit1 & 0x80000000) != 0
kerning_mask = (kerning_bit1 >> 8) & 0xFFFFFF
has_kern_data = (kerning_bit1 & 0xFF) != 0
if not has_kern_data:
is_kern_y_type = False
kerning_mask = 255
# Compiler directives
compiler_directives = _tagify(image.get_pixel(code_start_x, code_start_y + 9))
directive_opcode = (compiler_directives >> 24) & 255
directive_arg1 = (compiler_directives >> 16) & 255
directive_arg2 = (compiler_directives >> 8) & 255
# Nudge
nudging_bits = _tagify(image.get_pixel(code_start_x, code_start_y + 10))
nudge_x = _signed_byte((nudging_bits >> 24) & 0xFF)
nudge_y = _signed_byte((nudging_bits >> 16) & 0xFF)
# Diacritics anchors
diacritics_anchors = _parse_diacritics_anchors(image, code_start_x, code_start_y)
# Alignment
align_where = 0
for y in range(2):
if image.get_pixel(code_start_x, code_start_y + y + 15) & 0xFF:
align_where |= (1 << y)
# Write on top
write_on_top_raw = image.get_pixel(code_start_x, code_start_y + 17) # NO tagify
if (write_on_top_raw & 0xFF) == 0:
write_on_top = -1
else:
if (write_on_top_raw >> 8) == 0xFFFFFF:
write_on_top = 0
else:
write_on_top = (write_on_top_raw >> 28) & 15
# Stack where
stack_where0 = _tagify(image.get_pixel(code_start_x, code_start_y + 18))
stack_where1 = _tagify(image.get_pixel(code_start_x, code_start_y + 19))
if stack_where0 == 0x00FF00FF and stack_where1 == 0x00FF00FF:
stack_where = SC.STACK_DONT
else:
stack_where = 0
for y in range(2):
if image.get_pixel(code_start_x, code_start_y + y + 18) & 0xFF:
stack_where |= (1 << y)
ext_info = [0] * 15
props = GlyphProps(
width=width, is_low_height=is_low_height,
nudge_x=nudge_x, nudge_y=nudge_y,
diacritics_anchors=diacritics_anchors,
align_where=align_where, write_on_top=write_on_top,
stack_where=stack_where, ext_info=ext_info,
has_kern_data=has_kern_data, is_kern_y_type=is_kern_y_type,
kerning_mask=kerning_mask,
directive_opcode=directive_opcode, directive_arg1=directive_arg1,
directive_arg2=directive_arg2,
)
# Parse extInfo if needed
ext_count = props.required_ext_info_count()
if ext_count > 0:
for x in range(ext_count):
info = 0
for y in range(20):
if image.get_pixel(cell_x + x, cell_y + y) & 0xFF:
info |= (1 << y)
ext_info[x] = info
# Extract glyph bitmap: full cell minus the tag column.
# The Kotlin code draws the ENTIRE cell at a computed position;
# the tag column is the only thing excluded.
# Alignment and width only affect advance/positioning, not the bitmap.
max_w = cell_w - 1 # exclude tag column
bitmap = []
for row in range(cell_h):
row_data = []
for col in range(max_w):
px = image.get_pixel(cell_x + col, cell_y + row)
row_data.append(1 if (px & 0xFF) != 0 else 0)
bitmap.append(row_data)
# Now strip the tag column pixels that may have leaked into
# the glyph area. Tag data lives at column (cell_w - 1) which
# we already excluded, but extInfo columns 0..6 at the LEFT
# edge of the cell also contain tag data for replacewith glyphs.
# Clean those columns if they were used for extInfo.
if ext_count > 0:
for col_idx in range(min(ext_count, max_w)):
for row in range(cell_h):
bitmap[row][col_idx] = 0
# Colour extraction: check if any visible pixel is non-white
has_colour = False
color_bitmap = []
for row in range(cell_h):
row_data = []
for col in range(max_w):
px = image.get_pixel(cell_x + col, cell_y + row)
row_data.append(px)
if not has_colour and _is_coloured_pixel(px):
has_colour = True
color_bitmap.append(row_data)
if has_colour:
# Strip extInfo columns from color_bitmap too
if ext_count > 0:
for col_idx in range(min(ext_count, max_w)):
for row in range(cell_h):
color_bitmap[row][col_idx] = 0
else:
color_bitmap = None
result[code] = ExtractedGlyph(code, props, bitmap, color_bitmap)
return result
def _read_hangul_cell(image, column, row, cell_w=SC.W_HANGUL_BASE, cell_h=SC.H):
"""Read a single cell from the Hangul johab sheet at (column, row)."""
cell_x = column * cell_w
cell_y = row * cell_h
bitmap = []
for r in range(cell_h):
row_data = []
for c in range(cell_w):
px = image.get_pixel(cell_x + c, cell_y + r)
row_data.append(1 if (px & 0xFF) != 0 else 0)
bitmap.append(row_data)
return bitmap
def parse_hangul_jamo_sheet(image, cell_w, cell_h):
"""
Parse the Hangul Jamo sheet with correct row/column mapping.
Layout in hangul_johab.tga:
- Choseong (U+1100-U+115E): column = choseongIndex, row = 1
- Jungseong (U+1161-U+11A7): column = jungseongIndex+1, row = 15
(column 0 is filler U+1160, stored at row 15 col 0)
- Jongseong (U+11A8-U+11FF): column = jongseongIndex, row = 17
(index starts at 1 for 11A8)
- Extended Choseong (U+A960-U+A97F): column = 96+offset, row = 1
- Extended Jungseong (U+D7B0-U+D7C6): column = 72+offset, row = 15
- Extended Jongseong (U+D7CB-U+D7FB): column = 89+offset, row = 17
Each jamo gets a default-row bitmap. Multiple variant rows exist for
syllable composition (handled separately by hangul.py / GSUB).
"""
result = {}
# U+1160 (Hangul Jungseong Filler) — column 0, row 15
bm = _read_hangul_cell(image, 0, 15, cell_w, cell_h)
result[0x1160] = ExtractedGlyph(0x1160, GlyphProps(width=cell_w), bm)
# Choseong: U+1100-U+115E → column = cp - 0x1100, row = 1
for cp in range(0x1100, 0x115F):
col = cp - 0x1100
bm = _read_hangul_cell(image, col, 1, cell_w, cell_h)
result[cp] = ExtractedGlyph(cp, GlyphProps(width=cell_w), bm)
# U+115F (Hangul Choseong Filler)
col = 0x115F - 0x1100
bm = _read_hangul_cell(image, col, 1, cell_w, cell_h)
result[0x115F] = ExtractedGlyph(0x115F, GlyphProps(width=cell_w), bm)
# Jungseong: U+1161-U+11A7 → column = (cp - 0x1160), row = 15
for cp in range(0x1161, 0x11A8):
col = cp - 0x1160
bm = _read_hangul_cell(image, col, 15, cell_w, cell_h)
result[cp] = ExtractedGlyph(cp, GlyphProps(width=cell_w), bm)
# Jongseong: U+11A8-U+11FF → column = (cp - 0x11A8 + 1), row = 17
for cp in range(0x11A8, 0x1200):
col = cp - 0x11A8 + 1
bm = _read_hangul_cell(image, col, 17, cell_w, cell_h)
result[cp] = ExtractedGlyph(cp, GlyphProps(width=cell_w), bm)
# Extended Choseong: U+A960-U+A97F → column = (cp - 0xA960 + 96), row = 1
for cp in range(0xA960, 0xA980):
col = cp - 0xA960 + 96
bm = _read_hangul_cell(image, col, 1, cell_w, cell_h)
result[cp] = ExtractedGlyph(cp, GlyphProps(width=cell_w), bm)
# Extended Jungseong: U+D7B0-U+D7C6 → column = (cp - 0xD7B0 + 72), row = 15
for cp in range(0xD7B0, 0xD7C7):
col = cp - 0xD7B0 + 72
bm = _read_hangul_cell(image, col, 15, cell_w, cell_h)
result[cp] = ExtractedGlyph(cp, GlyphProps(width=cell_w), bm)
# Extended Jongseong: U+D7CB-U+D7FB → column = (cp - 0xD7CB + 88 + 1), row = 17
for cp in range(0xD7CB, 0xD7FC):
col = cp - 0xD7CB + 88 + 1
bm = _read_hangul_cell(image, col, 17, cell_w, cell_h)
result[cp] = ExtractedGlyph(cp, GlyphProps(width=cell_w), bm)
return result
def parse_fixed_sheet(image, sheet_index, cell_w, cell_h, cols):
"""Parse a fixed-width sheet (Hangul, Unihan, Runic, Custom Sym)."""
# Hangul Jamo sheet has special layout — handled separately
if sheet_index == SC.SHEET_HANGUL:
return parse_hangul_jamo_sheet(image, cell_w, cell_h)
code_range = SC.CODE_RANGE[sheet_index]
result = {}
fixed_width = {
SC.SHEET_CUSTOM_SYM: 20,
SC.SHEET_RUNIC: 9,
SC.SHEET_UNIHAN: SC.W_UNIHAN,
}.get(sheet_index, cell_w)
for index, code in enumerate(code_range):
cell_x = (index % cols) * cell_w
cell_y = (index // cols) * cell_h
bitmap = []
has_colour = False
color_bitmap = []
for row in range(cell_h):
row_data = []
color_row = []
for col in range(cell_w):
px = image.get_pixel(cell_x + col, cell_y + row)
row_data.append(1 if (px & 0xFF) != 0 else 0)
color_row.append(px)
if not has_colour and _is_coloured_pixel(px):
has_colour = True
bitmap.append(row_data)
color_bitmap.append(color_row)
props = GlyphProps(width=fixed_width)
result[code] = ExtractedGlyph(code, props, bitmap,
color_bitmap if has_colour else None)
return result
def _empty_bitmap(w=SC.W_VAR_INIT, h=SC.H):
return [[0] * w for _ in range(h)]
def parse_all_sheets(assets_dir):
"""Parse all sheets and return a map of codepoint -> ExtractedGlyph."""
result = {}
for sheet_index, filename in enumerate(SC.FILE_LIST):
filepath = os.path.join(assets_dir, filename)
if not os.path.exists(filepath):
print(f" [SKIP] {filename} not found")
continue
is_var = SC.is_variable(filename)
is_xy = SC.is_xy_swapped(filename)
is_ew = SC.is_extra_wide(filename)
cell_w = SC.get_cell_width(sheet_index)
cell_h = SC.get_cell_height(sheet_index)
cols = SC.get_columns(sheet_index)
tags = []
if is_var: tags.append("VARIABLE")
if is_xy: tags.append("XYSWAP")
if is_ew: tags.append("EXTRAWIDE")
if not tags: tags.append("STATIC")
print(f" Loading [{','.join(tags)}] {filename}")
image = read_tga(filepath)
if is_var:
sheet_glyphs = parse_variable_sheet(image, sheet_index, cell_w, cell_h, cols, is_xy)
else:
sheet_glyphs = parse_fixed_sheet(image, sheet_index, cell_w, cell_h, cols)
result.update(sheet_glyphs)
# Fixed-width overrides
_add_fixed_width_overrides(result)
return result
def _add_fixed_width_overrides(result):
"""Apply fixed-width overrides."""
# Hangul compat jamo
for code in SC.CODE_RANGE_HANGUL_COMPAT:
if code not in result:
result[code] = ExtractedGlyph(code, GlyphProps(width=SC.W_HANGUL_BASE), _empty_bitmap(SC.W_HANGUL_BASE))
# Zero-width ranges (only internal/PUA control ranges, not surrogates or full Plane 16)
for code in range(0xFFFA0, 0x100000):
result[code] = ExtractedGlyph(code, GlyphProps(width=0), _empty_bitmap(1, 1))
# Null char
result[0] = ExtractedGlyph(0, GlyphProps(width=0), _empty_bitmap(1, 1))
# Replacement character at U+007F
if 0x7F in result:
result[0x7F].props.width = 15
def get_hangul_jamo_bitmaps(assets_dir):
"""
Extract raw Hangul jamo bitmaps from the Hangul sheet for composition.
Returns a function: (column_index, row) -> bitmap (list of list of int)
"""
filename = SC.FILE_LIST[SC.SHEET_HANGUL]
filepath = os.path.join(assets_dir, filename)
if not os.path.exists(filepath):
print(" [WARNING] Hangul sheet not found")
return lambda idx, row: _empty_bitmap(SC.W_HANGUL_BASE)
image = read_tga(filepath)
cell_w = SC.W_HANGUL_BASE
cell_h = SC.H
def get_bitmap(index, row):
cell_x = index * cell_w
cell_y = row * cell_h
bitmap = []
for r in range(cell_h):
row_data = []
for c in range(cell_w):
px = image.get_pixel(cell_x + c, cell_y + r)
row_data.append(1 if (px & 0xFF) != 0 else 0)
bitmap.append(row_data)
return bitmap
return get_bitmap
def extract_hangul_jamo_variants(assets_dir):
"""
Extract ALL Hangul jamo variant bitmaps from hangul_johab.tga.
Returns dict of (column, row) -> bitmap for every non-empty cell.
Used by hangul.py to store variants in PUA for GSUB assembly.
Layout:
Row 0: Hangul Compatibility Jamo (U+3130-U+318F)
Rows 1-14: Choseong variants (row depends on jungseong context)
Rows 15-16: Jungseong variants (15=no final, 16=with final)
Rows 17-18: Jongseong variants (17=normal, 18=rightie jungseong)
Rows 19-24: Additional choseong variants (giyeok remapping)
"""
filename = SC.FILE_LIST[SC.SHEET_HANGUL]
filepath = os.path.join(assets_dir, filename)
if not os.path.exists(filepath):
return {}
image = read_tga(filepath)
cell_w = SC.W_HANGUL_BASE
cell_h = SC.H
variants = {}
# Scan all rows that contain jamo data
# Rows 0-24 at minimum, checking up to image height
max_row = image.height // cell_h
max_col = image.width // cell_w
for row in range(max_row):
for col in range(max_col):
bm = _read_hangul_cell(image, col, row, cell_w, cell_h)
# Check if non-empty
if any(px for r in bm for px in r):
variants[(col, row)] = bm
return variants

160
OTFbuild/hangul.py Normal file
View File

@@ -0,0 +1,160 @@
"""
Compose 11,172 Hangul syllables (U+AC00-U+D7A3) from jamo sprite pieces.
Also composes Hangul Compatibility Jamo (U+3130-U+318F).
Also stores all jamo variant bitmaps in PUA for GSUB-based jamo assembly.
Ported from HangulCompositor.kt and TerrarumSansBitmap.kt.
"""
from typing import Dict, List, Tuple
from glyph_parser import (
ExtractedGlyph, GlyphProps, get_hangul_jamo_bitmaps,
extract_hangul_jamo_variants, _read_hangul_cell, _empty_bitmap,
)
import sheet_config as SC
# PUA range for Hangul jamo variant storage.
# We need space for: max_col * max_row variants.
# Using 0xF0600-0xF1E7F
HANGUL_PUA_BASE = 0xF0600
def _compose_bitmaps(a, b, w, h):
"""OR two bitmaps together."""
result = []
for row in range(h):
row_data = []
for col in range(w):
av = a[row][col] if row < len(a) and col < len(a[row]) else 0
bv = b[row][col] if row < len(b) and col < len(b[row]) else 0
row_data.append(1 if av or bv else 0)
result.append(row_data)
return result
def _compose_bitmap_into(target, source, w, h):
"""OR source bitmap into target (mutates target)."""
for row in range(min(h, len(target), len(source))):
for col in range(min(w, len(target[row]), len(source[row]))):
if source[row][col]:
target[row][col] = 1
def _pua_for_jamo_variant(col, row):
"""Get PUA codepoint for a jamo variant at (column, row) in the sheet."""
# Encode as base + row * 256 + col (supports up to 256 columns per row)
return HANGUL_PUA_BASE + row * 256 + col
def compose_hangul(assets_dir) -> Dict[int, ExtractedGlyph]:
"""
Compose all Hangul syllables, compatibility jamo, and jamo variants.
Returns a dict of codepoint -> ExtractedGlyph.
"""
get_jamo = get_hangul_jamo_bitmaps(assets_dir)
cell_w = SC.W_HANGUL_BASE
cell_h = SC.H
result = {}
# Compose Hangul Compatibility Jamo (U+3130-U+318F)
for c in range(0x3130, 0x3190):
index = c - 0x3130
bitmap = get_jamo(index, 0)
props = GlyphProps(width=cell_w)
result[c] = ExtractedGlyph(c, props, bitmap)
# Compose 11,172 Hangul syllables (U+AC00-U+D7A3)
print(" Composing 11,172 Hangul syllables...")
for c in range(0xAC00, 0xD7A4):
c_int = c - 0xAC00
index_cho = c_int // (SC.JUNG_COUNT * SC.JONG_COUNT)
index_jung = c_int // SC.JONG_COUNT % SC.JUNG_COUNT
index_jong = c_int % SC.JONG_COUNT # 0 = no jongseong
# Map to jamo codepoints
cho_cp = 0x1100 + index_cho
jung_cp = 0x1161 + index_jung
jong_cp = 0x11A8 + index_jong - 1 if index_jong > 0 else 0
# Get sheet indices
i_cho = SC.to_hangul_choseong_index(cho_cp)
i_jung = SC.to_hangul_jungseong_index(jung_cp)
if i_jung is None:
i_jung = 0
i_jong = 0
if jong_cp != 0:
idx = SC.to_hangul_jongseong_index(jong_cp)
if idx is not None:
i_jong = idx
# Get row positions
cho_row = SC.get_han_initial_row(i_cho, i_jung, i_jong)
jung_row = SC.get_han_medial_row(i_cho, i_jung, i_jong)
jong_row = SC.get_han_final_row(i_cho, i_jung, i_jong)
# Get jamo bitmaps
cho_bitmap = get_jamo(i_cho, cho_row)
jung_bitmap = get_jamo(i_jung, jung_row)
# Compose
composed = _compose_bitmaps(cho_bitmap, jung_bitmap, cell_w, cell_h)
if index_jong > 0:
jong_bitmap = get_jamo(i_jong, jong_row)
_compose_bitmap_into(composed, jong_bitmap, cell_w, cell_h)
# Determine advance width
advance_width = cell_w + 1 if i_jung in SC.HANGUL_PEAKS_WITH_EXTRA_WIDTH else cell_w
props = GlyphProps(width=advance_width)
result[c] = ExtractedGlyph(c, props, composed)
print(f" Hangul syllable composition done: {len(result)} glyphs")
# Store jamo variant bitmaps in PUA for GSUB assembly
print(" Extracting jamo variants for GSUB...")
variants = extract_hangul_jamo_variants(assets_dir)
variant_count = 0
for (col, row), bm in variants.items():
pua = _pua_for_jamo_variant(col, row)
if pua not in result:
# Jungseong (rows 15-16) and jongseong (rows 17-18) overlay the
# choseong, so they need zero advance width.
w = 0 if 15 <= row <= 18 else cell_w
result[pua] = ExtractedGlyph(pua, GlyphProps(width=w), bm)
variant_count += 1
# Ensure jungseong filler PUA variants exist (col=0, rows 15-16).
# The filler has an empty bitmap so extract_hangul_jamo_variants skips
# it, but the vjmo GSUB lookup needs a PUA target to substitute to.
empty_bm = [[0] * cell_w for _ in range(cell_h)]
for row in [15, 16]:
pua = _pua_for_jamo_variant(0, row)
if pua not in result:
result[pua] = ExtractedGlyph(pua, GlyphProps(width=0), empty_bm)
variant_count += 1
print(f" Stored {variant_count} jamo variant glyphs in PUA (0x{HANGUL_PUA_BASE:05X}+)")
print(f" Total Hangul glyphs: {len(result)}")
return result
def get_jamo_gsub_data():
"""
Generate the data needed for Hangul jamo GSUB lookups.
Returns a dict with:
- 'cho_rows': dict mapping (i_jung, has_jong) -> row for choseong
- 'jung_rows': dict mapping has_jong -> row for jungseong
- 'jong_rows': dict mapping is_rightie -> row for jongseong
- 'pua_fn': function(col, row) -> PUA codepoint
These are the row-selection rules from the Kotlin code:
Choseong row = getHanInitialRow(i_cho, i_jung, i_jong)
Jungseong row = 15 if no final, else 16
Jongseong row = 17 if jungseong is not rightie, else 18
"""
return {
'pua_fn': _pua_for_jamo_variant,
'pua_base': HANGUL_PUA_BASE,
}

126
OTFbuild/keming_machine.py Normal file
View File

@@ -0,0 +1,126 @@
"""
Generate kerning pairs from shape rules.
Ported from TerrarumSansBitmap.kt "The Keming Machine" section.
6 base rules + 6 mirrored (auto-generated) = 12 rules total.
Also includes r+dot special pairs.
Output kern values scaled by SCALE (50 units/pixel):
-1px -> -50 units, -2px -> -100 units
"""
from typing import Dict, Tuple
from glyph_parser import ExtractedGlyph
import sheet_config as SC
SCALE = SC.SCALE
class _Ing:
"""Pattern matcher for kerning shape bits."""
def __init__(self, s):
self.s = s
self.care_bits = 0
self.rule_bits = 0
for index, char in enumerate(s):
if char == '@':
self.care_bits |= SC.KEMING_BIT_MASK[index]
self.rule_bits |= SC.KEMING_BIT_MASK[index]
elif char == '`':
self.care_bits |= SC.KEMING_BIT_MASK[index]
def matches(self, shape_bits):
return (shape_bits & self.care_bits) == self.rule_bits
class _Kem:
def __init__(self, first, second, bb=2, yy=1):
self.first = first
self.second = second
self.bb = bb
self.yy = yy
def _build_kerning_rules():
"""Build the 12 kerning rules (6 base + 6 mirrored)."""
base_rules = [
_Kem(_Ing("_`_@___`__"), _Ing("`_`___@___")),
_Kem(_Ing("_@_`___`__"), _Ing("`_________")),
_Kem(_Ing("_@_@___`__"), _Ing("`___@_@___"), 1, 1),
_Kem(_Ing("_@_@_`_`__"), _Ing("`_____@___")),
_Kem(_Ing("___`_`____"), _Ing("`___@_`___")),
_Kem(_Ing("___`_`____"), _Ing("`_@___`___")),
]
mirrored = []
for rule in base_rules:
left = rule.first.s
right = rule.second.s
new_left = []
new_right = []
for c in range(0, len(left), 2):
new_left.append(right[c + 1])
new_left.append(right[c])
new_right.append(left[c + 1])
new_right.append(left[c])
mirrored.append(_Kem(
_Ing(''.join(new_left)),
_Ing(''.join(new_right)),
rule.bb, rule.yy
))
return base_rules + mirrored
_KERNING_RULES = _build_kerning_rules()
def generate_kerning_pairs(glyphs: Dict[int, ExtractedGlyph]) -> Dict[Tuple[int, int], int]:
"""
Generate kerning pairs from all glyphs that have kerning data.
Returns dict of (left_codepoint, right_codepoint) -> kern_offset_in_font_units.
Negative values = tighter spacing.
"""
result = {}
# Collect all codepoints with kerning data
kernable = {cp: g for cp, g in glyphs.items() if g.props.has_kern_data}
if not kernable:
print(" [KemingMachine] No glyphs with kern data found")
return result
print(f" [KemingMachine] {len(kernable)} glyphs with kern data")
# Special rule: lowercase r + dot
r_dot_count = 0
for r in SC.LOWERCASE_RS:
for d in SC.DOTS:
if r in glyphs and d in glyphs:
result[(r, d)] = -1 * SCALE
r_dot_count += 1
# Apply kerning rules to all pairs
kern_codes = list(kernable.keys())
pairs_found = 0
for left_code in kern_codes:
left_props = kernable[left_code].props
mask_l = left_props.kerning_mask
for right_code in kern_codes:
right_props = kernable[right_code].props
mask_r = right_props.kerning_mask
for rule in _KERNING_RULES:
if rule.first.matches(mask_l) and rule.second.matches(mask_r):
contraction = rule.yy if (left_props.is_kern_y_type or right_props.is_kern_y_type) else rule.bb
if contraction > 0:
result[(left_code, right_code)] = -contraction * SCALE
pairs_found += 1
break # first matching rule wins
print(f" [KemingMachine] Generated {pairs_found} kerning pairs (+ {r_dot_count} r-dot pairs)")
return result

File diff suppressed because it is too large Load Diff

10
OTFbuild/otf2woff2.py Executable file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env python3
"""Convert an OTF/TTF font to WOFF2 format."""
import sys
from fontTools.ttLib import TTFont
src, dst = sys.argv[1], sys.argv[2]
font = TTFont(src)
font.flavor = 'woff2'
font.save(dst)
print(f" Written {dst}")

View File

@@ -0,0 +1,3 @@
fonttools>=4.47.0
brotli>=1.1.0
opentype-sanitizer>=9.2.0

543
OTFbuild/sheet_config.py Normal file
View File

@@ -0,0 +1,543 @@
"""
Sheet definitions, code ranges, index functions, and font metric constants.
Ported from TerrarumSansBitmap.kt companion object and SheetConfig.kt.
"""
# Font metrics
H = 20
H_UNIHAN = 16
W_HANGUL_BASE = 13
W_UNIHAN = 16
W_LATIN_WIDE = 9
W_VAR_INIT = 15
W_WIDEVAR_INIT = 31
HGAP_VAR = 1
SIZE_CUSTOM_SYM = 20
H_DIACRITICS = 3
H_STACKUP_LOWERCASE_SHIFTDOWN = 4
H_OVERLAY_LOWERCASE_SHIFTDOWN = 2
LINE_HEIGHT = 24
# OTF metrics (1000 UPM, scale = 50 units/pixel)
UNITS_PER_EM = 1000
SCALE = 50 # units per pixel
ASCENT = 16 * SCALE # 800
DESCENT = 4 * SCALE # 200
X_HEIGHT = 8 * SCALE # 400
CAP_HEIGHT = 12 * SCALE # 600
LINE_GAP = (LINE_HEIGHT - H) * SCALE # 200
# Sheet indices
SHEET_ASCII_VARW = 0
SHEET_HANGUL = 1
SHEET_EXTA_VARW = 2
SHEET_EXTB_VARW = 3
SHEET_KANA = 4
SHEET_CJK_PUNCT = 5
SHEET_UNIHAN = 6
SHEET_CYRILIC_VARW = 7
SHEET_HALFWIDTH_FULLWIDTH_VARW = 8
SHEET_UNI_PUNCT_VARW = 9
SHEET_GREEK_VARW = 10
SHEET_THAI_VARW = 11
SHEET_HAYEREN_VARW = 12
SHEET_KARTULI_VARW = 13
SHEET_IPA_VARW = 14
SHEET_RUNIC = 15
SHEET_LATIN_EXT_ADD_VARW = 16
SHEET_CUSTOM_SYM = 17
SHEET_BULGARIAN_VARW = 18
SHEET_SERBIAN_VARW = 19
SHEET_TSALAGI_VARW = 20
SHEET_PHONETIC_EXT_VARW = 21
SHEET_DEVANAGARI_VARW = 22
SHEET_KARTULI_CAPS_VARW = 23
SHEET_DIACRITICAL_MARKS_VARW = 24
SHEET_GREEK_POLY_VARW = 25
SHEET_EXTC_VARW = 26
SHEET_EXTD_VARW = 27
SHEET_CURRENCIES_VARW = 28
SHEET_INTERNAL_VARW = 29
SHEET_LETTERLIKE_MATHS_VARW = 30
SHEET_ENCLOSED_ALPHNUM_SUPL_VARW = 31
SHEET_TAMIL_VARW = 32
SHEET_BENGALI_VARW = 33
SHEET_BRAILLE_VARW = 34
SHEET_SUNDANESE_VARW = 35
SHEET_DEVANAGARI2_INTERNAL_VARW = 36
SHEET_CODESTYLE_ASCII_VARW = 37
SHEET_ALPHABETIC_PRESENTATION_FORMS = 38
SHEET_HENTAIGANA_VARW = 39
SHEET_CONTROL_PICTURES_VARW = 40
SHEET_LEGACY_COMPUTING_VARW = 41
SHEET_UNKNOWN = 254
FILE_LIST = [
"ascii_variable.tga",
"hangul_johab.tga",
"latinExtA_variable.tga",
"latinExtB_variable.tga",
"kana_variable.tga",
"cjkpunct_variable.tga",
"wenquanyi.tga",
"cyrilic_variable.tga",
"halfwidth_fullwidth_variable.tga",
"unipunct_variable.tga",
"greek_variable.tga",
"thai_variable.tga",
"hayeren_variable.tga",
"kartuli_variable.tga",
"ipa_ext_variable.tga",
"futhark.tga",
"latinExt_additional_variable.tga",
"puae000-e0ff.tga",
"cyrilic_bulgarian_variable.tga",
"cyrilic_serbian_variable.tga",
"tsalagi_variable.tga",
"phonetic_extensions_variable.tga",
"devanagari_variable.tga",
"kartuli_allcaps_variable.tga",
"diacritical_marks_variable.tga",
"greek_polytonic_xyswap_variable.tga",
"latinExtC_variable.tga",
"latinExtD_variable.tga",
"currencies_variable.tga",
"internal_variable.tga",
"letterlike_symbols_variable.tga",
"enclosed_alphanumeric_supplement_variable.tga",
"tamil_extrawide_variable.tga",
"bengali_variable.tga",
"braille_variable.tga",
"sundanese_variable.tga",
"devanagari_internal_extrawide_variable.tga",
"pua_codestyle_ascii_variable.tga",
"alphabetic_presentation_forms_extrawide_variable.tga",
"hentaigana_variable.tga",
"control_pictures_variable.tga",
"symbols_for_legacy_computing_variable.tga",
]
CODE_RANGE = [
list(range(0x00, 0x100)), # 0: ASCII
list(range(0x1100, 0x1200)) + list(range(0xA960, 0xA980)) + list(range(0xD7B0, 0xD800)), # 1: Hangul Jamo
list(range(0x100, 0x180)), # 2: Latin Ext A
list(range(0x180, 0x250)), # 3: Latin Ext B
list(range(0x3040, 0x3100)) + list(range(0x31F0, 0x3200)), # 4: Kana
list(range(0x3000, 0x3040)), # 5: CJK Punct
list(range(0x3400, 0xA000)), # 6: Unihan
list(range(0x400, 0x530)), # 7: Cyrillic
list(range(0xFF00, 0x10000)), # 8: Halfwidth/Fullwidth
list(range(0x2000, 0x20A0)), # 9: Uni Punct
list(range(0x370, 0x3CF)), # 10: Greek
list(range(0xE00, 0xE60)), # 11: Thai
list(range(0x530, 0x590)), # 12: Armenian
list(range(0x10D0, 0x1100)), # 13: Georgian
list(range(0x250, 0x300)), # 14: IPA
list(range(0x16A0, 0x1700)), # 15: Runic
list(range(0x1E00, 0x1F00)), # 16: Latin Ext Additional
list(range(0xE000, 0xE100)), # 17: Custom Sym (PUA)
list(range(0xF0000, 0xF0060)), # 18: Bulgarian
list(range(0xF0060, 0xF00C0)), # 19: Serbian
list(range(0x13A0, 0x13F6)), # 20: Cherokee
list(range(0x1D00, 0x1DC0)), # 21: Phonetic Ext
list(range(0x900, 0x980)) + list(range(0xF0100, 0xF0500)), # 22: Devanagari
list(range(0x1C90, 0x1CC0)), # 23: Georgian Caps
list(range(0x300, 0x370)), # 24: Diacritical Marks
list(range(0x1F00, 0x2000)), # 25: Greek Polytonic
list(range(0x2C60, 0x2C80)), # 26: Latin Ext C
list(range(0xA720, 0xA800)), # 27: Latin Ext D
list(range(0x20A0, 0x20D0)), # 28: Currencies
list(range(0xFFE00, 0xFFFA0)), # 29: Internal
list(range(0x2100, 0x2150)), # 30: Letterlike
list(range(0x1F100, 0x1F200)), # 31: Enclosed Alphanum Supl
list(range(0x0B80, 0x0C00)) + list(range(0xF00C0, 0xF0100)), # 32: Tamil
list(range(0x980, 0xA00)), # 33: Bengali
list(range(0x2800, 0x2900)), # 34: Braille
list(range(0x1B80, 0x1BC0)) + list(range(0x1CC0, 0x1CD0)) + list(range(0xF0500, 0xF0510)), # 35: Sundanese
list(range(0xF0110, 0xF0130)), # 36: Devanagari2 Internal
list(range(0xF0520, 0xF0580)), # 37: Codestyle ASCII
list(range(0xFB00, 0xFB18)), # 38: Alphabetic Presentation
list(range(0x1B000, 0x1B170)), # 39: Hentaigana
list(range(0x2400, 0x2440)), # 40: Control Pictures
list(range(0x1FB00, 0x1FC00)), # 41: Legacy Computing
]
CODE_RANGE_HANGUL_COMPAT = range(0x3130, 0x3190)
ALT_CHARSET_CODEPOINT_OFFSETS = [
0,
0xF0000 - 0x400, # Bulgarian
0xF0060 - 0x400, # Serbian
0xF0520 - 0x20, # Codestyle
]
ALT_CHARSET_CODEPOINT_DOMAINS = [
range(0, 0x10FFFF + 1),
range(0x400, 0x460),
range(0x400, 0x460),
range(0x20, 0x80),
]
# Unicode spacing characters
NQSP = 0x2000
MQSP = 0x2001
ENSP = 0x2002
EMSP = 0x2003
THREE_PER_EMSP = 0x2004
QUARTER_EMSP = 0x2005
SIX_PER_EMSP = 0x2006
FSP = 0x2007
PSP = 0x2008
THSP = 0x2009
HSP = 0x200A
ZWSP = 0x200B
ZWNJ = 0x200C
ZWJ = 0x200D
SHY = 0xAD
NBSP = 0xA0
OBJ = 0xFFFC
FIXED_BLOCK_1 = 0xFFFD0
MOVABLE_BLOCK_M1 = 0xFFFE0
MOVABLE_BLOCK_1 = 0xFFFF0
CHARSET_OVERRIDE_DEFAULT = 0xFFFC0
CHARSET_OVERRIDE_BG_BG = 0xFFFC1
CHARSET_OVERRIDE_SR_SR = 0xFFFC2
CHARSET_OVERRIDE_CODESTYLE = 0xFFFC3
# Alignment constants
ALIGN_LEFT = 0
ALIGN_RIGHT = 1
ALIGN_CENTRE = 2
ALIGN_BEFORE = 3
# Stack constants
STACK_UP = 0
STACK_DOWN = 1
STACK_BEFORE_N_AFTER = 2
STACK_UP_N_DOWN = 3
STACK_DONT = 4
def is_variable(filename):
return filename.endswith("_variable.tga")
def is_xy_swapped(filename):
return "xyswap" in filename.lower()
def is_extra_wide(filename):
return "extrawide" in filename.lower()
def get_cell_width(sheet_index):
"""Returns the cell pitch in the sprite sheet (includes HGAP_VAR for variable sheets)."""
fn = FILE_LIST[sheet_index]
if is_extra_wide(fn):
return W_WIDEVAR_INIT + HGAP_VAR # 32
if is_variable(fn):
return W_VAR_INIT + HGAP_VAR # 16
if sheet_index == SHEET_UNIHAN:
return W_UNIHAN
if sheet_index == SHEET_HANGUL:
return W_HANGUL_BASE
if sheet_index == SHEET_CUSTOM_SYM:
return SIZE_CUSTOM_SYM
if sheet_index == SHEET_RUNIC:
return W_LATIN_WIDE
return W_VAR_INIT + HGAP_VAR
def get_cell_height(sheet_index):
if sheet_index == SHEET_UNIHAN:
return H_UNIHAN
if sheet_index == SHEET_CUSTOM_SYM:
return SIZE_CUSTOM_SYM
return H
def get_columns(sheet_index):
if sheet_index == SHEET_UNIHAN:
return 256
return 16
# Hangul constants
JUNG_COUNT = 21
JONG_COUNT = 28
# Hangul shape arrays (sorted sets)
JUNGSEONG_I = frozenset([21, 61])
JUNGSEONG_OU = frozenset([9, 13, 14, 18, 34, 35, 39, 45, 51, 53, 54, 64, 73, 80, 83])
JUNGSEONG_OU_COMPLEX = frozenset(
[10, 11, 16] + list(range(22, 34)) + [36, 37, 38] + list(range(41, 45)) +
list(range(46, 51)) + list(range(56, 60)) + [63] + list(range(67, 73)) +
list(range(74, 80)) + list(range(81, 84)) + list(range(85, 92)) + [93, 94]
)
JUNGSEONG_RIGHTIE = frozenset([2, 4, 6, 8, 11, 16, 32, 33, 37, 42, 44, 48, 50, 71, 72, 75, 78, 79, 83, 86, 87, 88, 94])
JUNGSEONG_OEWI = frozenset([12, 15, 17, 40, 52, 55, 89, 90, 91])
JUNGSEONG_EU = frozenset([19, 62, 66])
JUNGSEONG_YI = frozenset([20, 60, 65])
JUNGSEONG_UU = frozenset([14, 15, 16, 17, 18, 27, 30, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 59, 67, 68, 73, 77, 78, 79, 80, 81, 82, 83, 84, 91])
JUNGSEONG_WIDE = frozenset(list(JUNGSEONG_OU) + list(JUNGSEONG_EU))
CHOSEONG_GIYEOKS = frozenset([0, 1, 15, 23, 30, 34, 45, 51, 56, 65, 82, 90, 100, 101, 110, 111, 115])
HANGUL_PEAKS_WITH_EXTRA_WIDTH = frozenset([2, 4, 6, 8, 11, 16, 32, 33, 37, 42, 44, 48, 50, 71, 75, 78, 79, 83, 86, 87, 88, 94])
GIYEOK_REMAPPING = {5: 19, 6: 20, 7: 21, 8: 22, 11: 23, 12: 24}
def is_hangul_choseong(c):
return 0x1100 <= c <= 0x115F or 0xA960 <= c <= 0xA97F
def is_hangul_jungseong(c):
return 0x1160 <= c <= 0x11A7 or 0xD7B0 <= c <= 0xD7C6
def is_hangul_jongseong(c):
return 0x11A8 <= c <= 0x11FF or 0xD7CB <= c <= 0xD7FB
def is_hangul_compat(c):
return 0x3130 <= c <= 0x318F
def to_hangul_choseong_index(c):
if 0x1100 <= c <= 0x115F:
return c - 0x1100
if 0xA960 <= c <= 0xA97F:
return c - 0xA960 + 96
raise ValueError(f"Not a choseong: U+{c:04X}")
def to_hangul_jungseong_index(c):
if 0x1160 <= c <= 0x11A7:
return c - 0x1160
if 0xD7B0 <= c <= 0xD7C6:
return c - 0xD7B0 + 72
return None
def to_hangul_jongseong_index(c):
if 0x11A8 <= c <= 0x11FF:
return c - 0x11A8 + 1
if 0xD7CB <= c <= 0xD7FB:
return c - 0xD7CB + 88 + 1
return None
def get_han_initial_row(i, p, f):
if p in JUNGSEONG_I:
ret = 3
elif p in JUNGSEONG_OEWI:
ret = 11
elif p in JUNGSEONG_OU_COMPLEX:
ret = 7
elif p in JUNGSEONG_OU:
ret = 5
elif p in JUNGSEONG_EU:
ret = 9
elif p in JUNGSEONG_YI:
ret = 13
else:
ret = 1
if f != 0:
ret += 1
if p in JUNGSEONG_UU and i in CHOSEONG_GIYEOKS:
mapped = GIYEOK_REMAPPING.get(ret)
if mapped is None:
raise ValueError(f"Giyeok remapping failed: i={i} p={p} f={f} ret={ret}")
return mapped
return ret
def get_han_medial_row(i, p, f):
return 15 if f == 0 else 16
def get_han_final_row(i, p, f):
return 17 if p not in JUNGSEONG_RIGHTIE else 18
# Kerning constants
KEMING_BIT_MASK = [1 << b for b in [7, 6, 5, 4, 3, 2, 1, 0, 15, 14]]
# Special characters for r+dot kerning
LOWERCASE_RS = frozenset([0x72, 0x155, 0x157, 0x159, 0x211, 0x213, 0x27c, 0x1e59, 0x1e58, 0x1e5f])
DOTS = frozenset([0x2c, 0x2e])
# Devanagari internal encoding
DEVANAGARI_UNICODE_NUQTA_TABLE = [0xF0170, 0xF0171, 0xF0172, 0xF0177, 0xF017C, 0xF017D, 0xF0186, 0xF018A]
def to_deva_internal(c):
if 0x0915 <= c <= 0x0939:
return c - 0x0915 + 0xF0140
if 0x0958 <= c <= 0x095F:
return DEVANAGARI_UNICODE_NUQTA_TABLE[c - 0x0958]
raise ValueError(f"No internal form for U+{c:04X}")
DEVANAGARI_CONSONANTS = frozenset(
list(range(0x0915, 0x093A)) + list(range(0x0958, 0x0960)) +
list(range(0x0978, 0x0980)) + list(range(0xF0140, 0xF0500)) +
list(range(0xF0106, 0xF010A))
)
# Sundanese internal forms
SUNDANESE_ING = 0xF0500
SUNDANESE_ENG = 0xF0501
SUNDANESE_EUNG = 0xF0502
SUNDANESE_IR = 0xF0503
SUNDANESE_ER = 0xF0504
SUNDANESE_EUR = 0xF0505
SUNDANESE_LU = 0xF0506
# Tamil constants
TAMIL_KSSA = 0xF00ED
TAMIL_SHRII = 0xF00EE
TAMIL_I = 0xBBF
TAMIL_LIGATING_CONSONANTS = [
0x0B95, 0x0B99, 0x0B9A, 0x0B9E, 0x0B9F, 0x0BA3, 0x0BA4, 0x0BA8,
0x0BA9, 0x0BAA, 0x0BAE, 0x0BAF, 0x0BB0, 0x0BB1, 0x0BB2, 0x0BB3,
0x0BB4, 0x0BB5,
]
# Devanagari special codepoints
DEVANAGARI_VIRAMA = 0x94D
DEVANAGARI_NUQTA = 0x93C
DEVANAGARI_RA = to_deva_internal(0x930)
DEVANAGARI_YA = to_deva_internal(0x92F)
DEVANAGARI_RRA = to_deva_internal(0x931)
DEVANAGARI_VA = to_deva_internal(0x935)
DEVANAGARI_HA = to_deva_internal(0x939)
DEVANAGARI_U = 0x941
DEVANAGARI_UU = 0x942
DEVANAGARI_I_VOWEL = 0x093F
DEVANAGARI_II_VOWEL = 0x0940
DEVANAGARI_RYA = 0xF0106
DEVANAGARI_HALF_RYA = 0xF0107
DEVANAGARI_OPEN_YA = 0xF0108
DEVANAGARI_OPEN_HALF_YA = 0xF0109
DEVANAGARI_ALT_HALF_SHA = 0xF010F
DEVANAGARI_RA_SUB = 0xF010A # below-base RA (rakaar); transient glyph for blwf/cjct
DEVANAGARI_EYELASH_RA = 0xF010B
DEVANAGARI_RA_SUPER = 0xF010C
DEVANAGARI_RA_SUPER_COMPLEX = 0xF010D
MARWARI_DD = 0x978
MARWARI_LIG_DD_R = 0xF010E
DEVANAGARI_SYLL_RU = 0xF0100
DEVANAGARI_SYLL_RUU = 0xF0101
DEVANAGARI_SYLL_RRU = 0xF0102
DEVANAGARI_SYLL_RRUU = 0xF0103
DEVANAGARI_SYLL_HU = 0xF0104
DEVANAGARI_SYLL_HUU = 0xF0105
# Devanagari ligature codepoints
DEVANAGARI_LIG_K_T = 0xF01BC
DEVANAGARI_LIG_K_SS = 0xF01A1
DEVANAGARI_LIG_J_NY = 0xF01A2
DEVANAGARI_LIG_T_T = 0xF01A3
DEVANAGARI_LIG_N_T = 0xF01A4
DEVANAGARI_LIG_N_N = 0xF01A5
DEVANAGARI_LIG_S_V = 0xF01A6
DEVANAGARI_LIG_SS_P = 0xF01A7
DEVANAGARI_LIG_SH_C = 0xF01A8
DEVANAGARI_LIG_SH_N = 0xF01A9
DEVANAGARI_LIG_SH_V = 0xF01AA
DEVANAGARI_LIG_J_Y = 0xF01AB
DEVANAGARI_LIG_J_J_Y = 0xF01AC
MARWARI_LIG_DD_DD = 0xF01BA
MARWARI_LIG_DD_DDH = 0xF01BB
DEVANAGARI_ANUSVARA_UPPER = 0xF016C
MARWARI_LIG_DD_Y = 0xF016E
MARWARI_HALFLIG_DD_Y = 0xF016F
# Devanagari range sets for feature generation
DEVANAGARI_PRESENTATION_CONSONANTS = range(0xF0140, 0xF0230)
DEVANAGARI_PRESENTATION_CONSONANTS_HALF = range(0xF0230, 0xF0320)
DEVANAGARI_PRESENTATION_CONSONANTS_WITH_RA = range(0xF0320, 0xF0410)
DEVANAGARI_PRESENTATION_CONSONANTS_WITH_RA_HALF = range(0xF0410, 0xF0500)
# Index functions
def _kana_index_y(c):
return 12 if 0x31F0 <= c <= 0x31FF else (c - 0x3040) // 16
def _unihan_index_y(c):
return (c - 0x3400) // 256
def _devanagari_index_y(c):
return ((c - 0x0900) if c < 0xF0000 else (c - 0xF0080)) // 16
def _tamil_index_y(c):
return ((c - 0x0B80) if c < 0xF0000 else (c - 0xF0040)) // 16
def _sundanese_index_y(c):
if c >= 0xF0500:
return (c - 0xF04B0) // 16
if c < 0x1BC0:
return (c - 0x1B80) // 16
return (c - 0x1C80) // 16
def index_x(c):
return c % 16
def unihan_index_x(c):
return (c - 0x3400) % 256
def index_y(sheet_index, c):
"""Y-index (row) for codepoint c in the given sheet."""
return {
SHEET_ASCII_VARW: lambda: c // 16,
SHEET_UNIHAN: lambda: _unihan_index_y(c),
SHEET_EXTA_VARW: lambda: (c - 0x100) // 16,
SHEET_EXTB_VARW: lambda: (c - 0x180) // 16,
SHEET_KANA: lambda: _kana_index_y(c),
SHEET_CJK_PUNCT: lambda: (c - 0x3000) // 16,
SHEET_CYRILIC_VARW: lambda: (c - 0x400) // 16,
SHEET_HALFWIDTH_FULLWIDTH_VARW: lambda: (c - 0xFF00) // 16,
SHEET_UNI_PUNCT_VARW: lambda: (c - 0x2000) // 16,
SHEET_GREEK_VARW: lambda: (c - 0x370) // 16,
SHEET_THAI_VARW: lambda: (c - 0xE00) // 16,
SHEET_CUSTOM_SYM: lambda: (c - 0xE000) // 16,
SHEET_HAYEREN_VARW: lambda: (c - 0x530) // 16,
SHEET_KARTULI_VARW: lambda: (c - 0x10D0) // 16,
SHEET_IPA_VARW: lambda: (c - 0x250) // 16,
SHEET_RUNIC: lambda: (c - 0x16A0) // 16,
SHEET_LATIN_EXT_ADD_VARW: lambda: (c - 0x1E00) // 16,
SHEET_BULGARIAN_VARW: lambda: (c - 0xF0000) // 16,
SHEET_SERBIAN_VARW: lambda: (c - 0xF0060) // 16,
SHEET_TSALAGI_VARW: lambda: (c - 0x13A0) // 16,
SHEET_PHONETIC_EXT_VARW: lambda: (c - 0x1D00) // 16,
SHEET_DEVANAGARI_VARW: lambda: _devanagari_index_y(c),
SHEET_KARTULI_CAPS_VARW: lambda: (c - 0x1C90) // 16,
SHEET_DIACRITICAL_MARKS_VARW: lambda: (c - 0x300) // 16,
SHEET_GREEK_POLY_VARW: lambda: (c - 0x1F00) // 16,
SHEET_EXTC_VARW: lambda: (c - 0x2C60) // 16,
SHEET_EXTD_VARW: lambda: (c - 0xA720) // 16,
SHEET_CURRENCIES_VARW: lambda: (c - 0x20A0) // 16,
SHEET_INTERNAL_VARW: lambda: (c - 0xFFE00) // 16,
SHEET_LETTERLIKE_MATHS_VARW: lambda: (c - 0x2100) // 16,
SHEET_ENCLOSED_ALPHNUM_SUPL_VARW: lambda: (c - 0x1F100) // 16,
SHEET_TAMIL_VARW: lambda: _tamil_index_y(c),
SHEET_BENGALI_VARW: lambda: (c - 0x980) // 16,
SHEET_BRAILLE_VARW: lambda: (c - 0x2800) // 16,
SHEET_SUNDANESE_VARW: lambda: _sundanese_index_y(c),
SHEET_DEVANAGARI2_INTERNAL_VARW: lambda: (c - 0xF0110) // 16,
SHEET_CODESTYLE_ASCII_VARW: lambda: (c - 0xF0520) // 16,
SHEET_ALPHABETIC_PRESENTATION_FORMS: lambda: (c - 0xFB00) // 16,
SHEET_HENTAIGANA_VARW: lambda: (c - 0x1B000) // 16,
SHEET_CONTROL_PICTURES_VARW: lambda: (c - 0x2400) // 16,
SHEET_LEGACY_COMPUTING_VARW: lambda: (c - 0x1FB00) // 16,
SHEET_HANGUL: lambda: 0,
}.get(sheet_index, lambda: c // 16)()

90
OTFbuild/tga_reader.py Normal file
View File

@@ -0,0 +1,90 @@
"""
TGA reader for uncompressed true-colour images (Type 2).
Stores pixels as RGBA8888: (R<<24 | G<<16 | B<<8 | A).
Matches the convention in TerrarumSansBitmap.kt where .and(255) checks
the alpha channel (lowest byte).
"""
import struct
from typing import List
class TgaImage:
__slots__ = ('width', 'height', 'pixels')
def __init__(self, width: int, height: int, pixels: List[int]):
self.width = width
self.height = height
self.pixels = pixels # flat array, row-major
def get_pixel(self, x: int, y: int) -> int:
"""Get pixel at (x, y) as RGBA8888 (R in bits 31-24, A in bits 7-0)."""
if x < 0 or x >= self.width or y < 0 or y >= self.height:
return 0
return self.pixels[y * self.width + x]
def read_tga(path: str) -> TgaImage:
"""Read an uncompressed true-colour TGA file."""
with open(path, 'rb') as f:
data = f.read()
pos = 0
def u8():
nonlocal pos
val = data[pos]
pos += 1
return val
def u16():
nonlocal pos
val = struct.unpack_from('<H', data, pos)[0]
pos += 2
return val
id_length = u8()
colour_map_type = u8()
image_type = u8()
# colour map spec (5 bytes)
u16(); u16(); u8()
# image spec
x_origin = u16()
y_origin = u16()
width = u16()
height = u16()
bits_per_pixel = u8()
descriptor = u8()
top_to_bottom = (descriptor & 0x20) != 0
bytes_per_pixel = bits_per_pixel // 8
# skip ID
pos += id_length
if colour_map_type != 0:
raise ValueError("Colour-mapped TGA not supported")
if image_type != 2:
raise ValueError(f"Only uncompressed true-colour TGA supported (type 2), got type {image_type}")
if bytes_per_pixel not in (3, 4):
raise ValueError(f"Only 24-bit or 32-bit TGA supported, got {bits_per_pixel}-bit")
pixels = [0] * (width * height)
for row in range(height):
y = row if top_to_bottom else (height - 1 - row)
for x in range(width):
b = data[pos]; pos += 1
g = data[pos]; pos += 1
r = data[pos]; pos += 1
a = data[pos] if bytes_per_pixel == 4 else 0xFF
if bytes_per_pixel == 4:
pos += 1
# Store as RGBA8888: R in high byte, A in low byte
pixels[y * width + x] = (r << 24) | (g << 16) | (b << 8) | a
return TgaImage(width, height, pixels)

BIN
PUA_allocation_chart.xlsx Executable file

Binary file not shown.

173
README.md Normal file → Executable file
View File

@@ -1,38 +1,46 @@
# Terrarum Sans Bitmap
![Font sample](https://github.com/minjaesong/Terrarum-sans-bitmap/blob/master/font_test_3.PNG)
![Font sample — necessary information in this image is also provided below.](demo.PNG)
This font is a bitmap font used in [my game project called Terrarum](https://gitlab.com/minjaesong/terrarum) (hence the name). The font supports more than 90 % of european languages, as well as Chinese, Japanese and Korean. More technical side, it supports Latin-1 Supplement, Latin Ext-A, Latin Ext-B, IPA Extension (required by some languages), Greek, Cyrillic (+ Bulgarian, Serbian variants) and the supplement, Armenian, Thai (beta version), Georgian, Unicode Punctuations, CJK Punctuations, Kana, Chinese (limited to Unihan and Ext-A), Hangul (all 11 172 possible syllables) and Fullwidth forms.
This font is a bitmap font used in [my game project called Terrarum](https://github.com/minjaesong/Terrarum) (hence the name). The font supports more than 90 % of european languages, as well as Chinese, Japanese, and Korean.
The JAR package is meant to be used with Slick2d (extends ```Font``` class) and LibGDX (extends ```BitmapFont``` class). If you are not using the framework, please refer to the __Font metrics__ section to implement the font metrics correctly on your system.
The JAR package is meant to be used with LibGDX (extends ```BitmapFont``` class). If you are not using the framework, please refer to the __Font metrics__ section to implement the font metrics correctly on your system.
The issue page is open. If you have some issues to submit, or have a question, please leave it on the page.
#### Little notes
- You can't display Bulgarian, Russian and Serbian variants at the same time without some more codes; ```reload()```ing them or creating multiple instances of the font would be the easiest solutions.
- All Han characters are in Chinese variant, no other variants are to be supported as most Chinese, Japanese and Korean can understand other's variant and to be honest, we don't bother anyway.
#### Notes and Limitations
- Displaying Bulgarian/Serbian variants of Cyrillic requires special Control Characters. (`GameFontBase.charsetOverrideBulgarian` -- U+FFFC1; `GameFontBase.charsetOverrideSerbian` -- U+FFFC2)
- All Han characters are in Mainland Chinese variant. There is no plan to support the other variants unless there is someone willing to do the drawing of the characters
- Only the Devanagari and Tamil has full (as much as I can) ligature support for Indic scripts -- Bengali script does not have any ligature support
- Slick2d versions are now unsupported. I couldn't extend myself to work on both versions, but I'm still welcome to merge your pull requests.
## Contribution guidelines
### Design Goals
You can contribute to the font by fixing wrong glyphs, suggesting better ones, extending character set (letters for other writing systems or filling in the blanks on the existing ones), or code for other game frameworks (not limited to Java). Please leave pull request for that.
- Sans-serif
- Realise (some of) handwritten forms
- Combininig with the sans-serif, this stands for **no over-simplification**
- Condensed capitals for efficient space usage
Font Spritesheets are stored in ```assets/graphics/fonts``` directory. Image format must be TGA with Alpha — no PNG. If someone needs PNG, they can batch-convert the font using utils like ImageMagick.
## Download
- Go ahead to the [release tab](https://github.com/minjaesong/Terrarum-sans-bitmap/releases), and download the most recent version. It is **not** advised to use the .jar found within the repository, they're experimental builds I use during the development, and may contain bugs like leaking memory.
## Using on your game
- Firstly, place the .jar to your library path and unzip spritesheets, then:
- Firstly, place the .jar to your library path and assets folder to the main directory of the app, then:
### Using on LibGDX
On your code (Kotlin):
import net.torvald.terrarumsansbitmap.gdx.TerrarumSansBitmap
class YourGame : Game() {
lateinit var fontGame: Font
override fun create() {
fontGame = GameFontBase(path_to_assets)
fontGame = TerrarumSansBitmap(path_to_assets, ...)
...
}
@@ -47,12 +55,14 @@ On your code (Kotlin):
On your code (Java):
import net.torvald.terrarumsansbitmap.gdx.TerrarumSansBitmap;
class YourGame extends BasicGame {
Font fontGame;
@Override void create() {
fontGame = new GameFontBase(path_to_assets);
fontGame = new TerrarumSansBitmap(path_to_assets, ...);
...
}
@@ -65,149 +75,26 @@ On your code (Java):
}
}
### Using on Slick2d
On your code (Kotlin):
class YourGame : BasicGame("YourGameName") {
lateinit var fontGame: Font
override fun init(gc: GameContainer) {
fontGame = GameFontBase(path_to_assets)
...
}
override fun render(gc: GameContainer, g: Graphics) {
g.font = fontGame
g.drawString(...)
}
}
On your code (Java):
class YourGame extends BasicGame {
Font fontGame;
@Override void init(GameContainer gc) {
fontGame = new GameFontBase(path_to_assets);
...
}
@Override void render(GameContainer gc, Graphics g) {
g.setFont(fontGame);
g.drawString(...);
}
}
### How to Use Color Code
Color codes are individual unicode characters. While you can somehow make a raw character and paste in on your code, it's certainly not desirable. Fortunately, we're also providing utility functions for the color codes.
GameFontBase.toColorCode(rgba4444: Int) -- returns String
GameFontBase.toColorCode(argb4444: Int) -- returns String
GameFontBase.toColorCode(r: Int, g: Int, b: Int) -- returns String
GameFontBase.toColorCode(r: Int, g: Int, b: Int, a: Int) -- returns String
```rgba4444``` takes whole RGBA as input, that is, from 0x0000 to 0xFFFF. Most significant bits represents Red, and least significant bits represents Alpha (which should be fixed as F for the most time)
```r, g, b(, a)``` takes RGB and A separately, in the range of 0..F. Any value exceeding the range **are unchecked and may wreak havoc**, so be careful.
```argb4444``` takes whole ARGB (in that order) as input, that is, from 0x0000 to 0xFFFF.
```r, g, b(, a)``` takes RGB and A separately, in the range of 0x0..0xF. Any value exceeding the range **are unchecked and may wreak havoc**, so be careful.
U+100000 is used to disable previously-applied color codes (going back to original colour), even if it looks like ARGB of all zero.
## Font metrics
## Contribution guidelines
Although the font is basically a Spritesheet, some of the sheet expects variable widths to be supported. Any sheets with ```_variable``` means it expects variable widths. Anything else expects fixed width (regular Spritesheet behaviour). ```cjkpunct``` has width of 10, ```kana``` and ```hangul_johab``` has width of 12, ```wenquanyi``` has width of 16.
### Parsing glyph widths for variable font sheets
![Sample of Font Spritesheet with annotation](https://github.com/minjaesong/Terrarum-sans-bitmap/blob/master/width_bit_encoding_annotated.png)
Width is encoded in binary bits, on pixels. On the font spritesheet, every glyph has vertical dots on their top-right side (to be exact, every (16k - 1)th pixel on x axis). Above image is a sample of the font, with width information coloured in magenta. From top to bottom, each dot represents 1, 2, 4 and 8. For example, in the above image, ! (exclamation mark) has width of 5, " (double quote) has width of 6, # (octothorp) has width of 8, $ (dollar sign) has width of 9.
### Implementing the Korean writing system
On this font, Hangul letters are printed by assemblying two or three letter pieces. There are 10 sets of Hangul letter pieces on the font. Top 6 are initials, middle 2 are medials, and bottom 2 are finals. On the rightmost side, there's eight assembled glyphs to help you with (assuming you have basic knowledge on the writing system). Top 6 tells you how to use 6 initials, and bottom 2 tells you how to use 2 finals.
This is a Kotlin-like pseudocode for assembling the glyph:
function getHanChosung(hanIndex: Int) = hanIndex / (21 * 28)
function getHanJungseong(hanIndex: Int) = hanIndex / 28 % 21
function getHanJongseong(hanIndex: Int) = hanIndex % 28
jungseongWide = arrayOf(8, 12, 13, 17, 18, 21)
jungseongComplex = arrayOf(9, 10, 11, 14, 15, 16, 22)
function getHanInitialRow(hanIndex: Int): Int {
val ret: Int
if (isJungseongWide(hanIndex))
ret = 2
else if (isJungseongComplex(hanIndex))
ret = 4
else
ret = 0
return if (getHanJongseong(hanIndex) == 0) ret else ret + 1
}
function isJungseongWide(hanIndex: Int) = jungseongWide.contains(getHanJungseong(hanIndex))
function isJungseongComplex(hanIndex: Int) = jungseongComplex.contains(getHanJungseong(hanIndex))
function getHanInitialRow(hanIndex: Int): Int {
val ret: Int
if (isJungseongWide(hanIndex))
ret = 2
else if (isJungseongComplex(hanIndex))
ret = 4
else
ret = 0
return if (getHanJongseong(hanIndex) == 0) ret else ret + 1
}
function getHanMedialRow(hanIndex: Int) = if (getHanJongseong(hanIndex) == 0) 6 else 7
function getHanFinalRow(hanIndex: Int): Int {
val jungseongIndex = getHanJungseong(hanIndex)
return if (jungseongWide.contains(jungseongIndex))
8
else
9
}
function isHangul(c: Char) = c.toInt() >= 0xAC00 && c.toInt() < 0xD7A4
...
for (each Char on the string) {
if (isHangul(Char)) {
val hIndex = Char.toInt() - 0xAC00
val indexCho = getHanChosung(hIndex)
val indexJung = getHanJungseong(hIndex)
val indexJong = getHanJongseong(hIndex)
val choRow = getHanInitialRow(hIndex)
val jungRow = getHanMedialRow(hIndex)
val jongRow = getHanFinalRow(hIndex)
// get sub image from sprite sheet
val choseongImage = hangulSheet.getSubImage(indexCho, choRow)
val jungseongImage = hangulSheet.getSubImage(indexJung, jungRow)
val jongseongImage = hangulSheet.getSubImage(indexJong, jongRow)
// actual drawing part
draw choseongImage to somewhere you want
draw jungseongImage on top of choseongImage
draw jongseongImage on top of choseongImage
}
...
}
Please refer to [CONTRIBUTING.md](https://github.com/minjaesong/Terrarum-sans-bitmap/blob/master/CONTRIBUTING.md)
## Acknowledgement
Thanks to kind people of [/r/Typography](https://www.reddit.com/r/typography/) for amazing feedbacks.
CJK Ideographs are powered by [WenQuanYi Font](http://wenq.org/wqy2/index.cgi?BitmapSong). The font is distributed under the GNU GPL version 2. Although the glyphs themselves are not copyrightable (the program codes—e.g. TTF—do), we would like to give a credit for the font and the people behind it.
CJK Ideographs are powered by [WenQuanYi Font](http://wenq.org/wqy2/index.cgi?BitmapSong). The font is distributed under the GNU GPL version 2. Although the shapes of typefaces are not copyrightable (the program codes—e.g. TTF—do), we would like to give a credit to the font and the people behind it.

Binary file not shown.

BIN
alignment_illustration.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

BIN
demo.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

1
demo/.idea/.name generated
View File

@@ -1 +0,0 @@
TerrarumSansDemo

View File

@@ -1,15 +0,0 @@
<component name="ArtifactManager">
<artifact type="jar" name="unnamed">
<output-path>$PROJECT_DIR$/out/artifacts/unnamed</output-path>
<root id="archive" name="TerrarumSansDemo.jar">
<element id="directory" name="META-INF">
<element id="file-copy" path="$PROJECT_DIR$/META-INF/MANIFEST.MF" />
</element>
<element id="file-copy" path="$PROJECT_DIR$/../LICENSE.md" />
<element id="file-copy" path="$PROJECT_DIR$/config.properties" />
<element id="file-copy" path="$PROJECT_DIR$/text.txt" />
<element id="dir-copy" path="$PROJECT_DIR$/lib" />
<element id="module-output" name="TerrarumSansDemo" />
</root>
</artifact>
</component>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="1.8" />
</component>
</project>

View File

@@ -1,17 +0,0 @@
<component name="libraryTable">
<library name="KotlinJavaRuntime">
<CLASSES>
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-runtime.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" />
<root url="file://$PROJECT_DIR$/lib" />
</CLASSES>
<JAVADOC />
<NATIVE>
<root url="file://$PROJECT_DIR$/lib" />
</NATIVE>
<SOURCES>
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-runtime-sources.jar!/" />
</SOURCES>
<jarDirectory url="file://$PROJECT_DIR$/lib" recursive="false" />
</library>
</component>

6
demo/.idea/misc.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/TerrarumSansDemo.iml" filepath="$PROJECT_DIR$/TerrarumSansDemo.iml" />
</modules>
</component>
</project>

828
demo/.idea/workspace.xml generated
View File

@@ -1,828 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ArtifactsWorkspaceSettings">
<artifacts-to-build>
<artifact name="unnamed" />
</artifacts-to-build>
</component>
<component name="ChangeListManager">
<list default="true" id="fcc8c48a-4440-438f-9e38-d42425b5efd4" name="Default" comment="" />
<ignored path="$PROJECT_DIR$/out/" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="CreatePatchCommitExecutor">
<option name="PATCH_PATH" value="" />
</component>
<component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file leaf-file-name="GameFontDemo.kt" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontDemo.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="434">
<caret line="43" column="0" lean-forward="false" selection-start-line="43" selection-start-column="0" selection-end-line="43" selection-end-column="0" />
<folding>
<element signature="e#40#255#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="GameFontBase.kt" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="405">
<caret line="561" column="32" lean-forward="true" selection-start-line="561" selection-start-column="32" selection-end-line="561" selection-end-column="32" />
<folding>
<element signature="e#48#391#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Kotlin File" />
</list>
</option>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>&quot;|</find>
<find>,\n</find>
<find>main</find>
<find>E7F</find>
</findStrings>
<replaceStrings>
<replace>\n</replace>
</replaceStrings>
</component>
<component name="GradleLocalSettings">
<option name="modificationStamps">
<map>
<entry key="$PROJECT_DIR$/../../terrarum_renewed" value="2962360935090" />
</map>
</option>
<option name="externalProjectsViewState">
<projects_view />
</option>
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/text.properties" />
<option value="$PROJECT_DIR$/config.properties" />
<option value="$PROJECT_DIR$/META-INF/MANIFEST.MF" />
<option value="$PROJECT_DIR$/text.txt" />
<option value="$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontImpl.kt" />
<option value="$PROJECT_DIR$/lib/slick.jar!/org/newdawn/slick/opengl/Texture.class" />
<option value="$PROJECT_DIR$/lib/slick.jar!/org/newdawn/slick/SpriteSheet.class" />
<option value="$PROJECT_DIR$/lib/slick.jar!/org/newdawn/slick/Image.class" />
<option value="$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontDemo.kt" />
<option value="$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontBase.kt" />
</list>
</option>
</component>
<component name="ProjectFrameBounds">
<option name="x" value="-8" />
<option name="y" value="-8" />
<option name="width" value="1936" />
<option name="height" value="1176" />
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1">
<flattenPackages />
<showMembers />
<showModules />
<showLibraryContents />
<hideEmptyPackages />
<abbreviatePackageNames />
<autoscrollToSource />
<autoscrollFromSource />
<sortByType />
<manualOrder />
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="PackagesPane" />
<pane id="Scope" />
<pane id="ProjectPane">
<subPane>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="TerrarumSansDemo" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="demo" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="TerrarumSansDemo" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="demo" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="src" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="imagefont" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="TerrarumSansDemo" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="demo" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="META-INF" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
</subPane>
</pane>
<pane id="Scratches" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="settings.editor.selected.configurable" value="project.kotlinCompiler" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="project.structure.last.edited" value="Modules" />
<property name="project.structure.proportion" value="0.15" />
<property name="project.structure.side.proportion" value="0.32068965" />
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager" selected="Kotlin.net.torvald.terrarum.imagefont.GameFontDemoKt">
<configuration default="false" name="net.torvald.terrarum.imagefont.GameFontDemoKt" type="JetRunConfigurationType" factoryName="Kotlin" temporary="true" nameIsGenerated="true">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="net.torvald.terrarum.imagefont.GameFontDemoKt" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="TerrarumSansDemo" />
<envs />
<method />
</configuration>
<configuration default="true" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType" factoryName="Plugin">
<module name="" />
<option name="VM_PARAMETERS" value="-Xmx512m -Xms256m -XX:MaxPermSize=250m -ea" />
<option name="PROGRAM_PARAMETERS" />
<predefined_log_file id="idea.log" enabled="true" />
<method />
</configuration>
<configuration default="true" type="Applet" factoryName="Applet">
<option name="HTML_USED" value="false" />
<option name="WIDTH" value="400" />
<option name="HEIGHT" value="300" />
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
<module />
<method />
</configuration>
<configuration default="true" type="Application" factoryName="Application">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="ENABLE_SWING_INSPECTOR" value="false" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="" />
<envs />
<method />
</configuration>
<configuration default="true" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list />
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<method />
</configuration>
<configuration default="true" type="JUnit" factoryName="JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" />
<option name="MAIN_CLASS_NAME" />
<option name="METHOD_NAME" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method />
</configuration>
<configuration default="true" type="JarApplication" factoryName="JAR Application">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<envs />
<method />
</configuration>
<configuration default="true" type="Java Scratch" factoryName="Java Scratch">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="SCRATCH_FILE_ID" value="0" />
<option name="MAIN_CLASS_NAME" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="ENABLE_SWING_INSPECTOR" value="false" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="" />
<envs />
<method />
</configuration>
<configuration default="true" type="JetRunConfigurationType" factoryName="Kotlin">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="TerrarumSansDemo" />
<envs />
<method />
</configuration>
<configuration default="true" type="KotlinStandaloneScriptRunConfigurationType" factoryName="Kotlin script">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="filePath" />
<option name="vmParameters" />
<option name="alternativeJrePath" />
<option name="programParameters" />
<option name="passParentEnvs" value="true" />
<option name="workingDirectory" />
<option name="isAlternativeJrePathEnabled" value="false" />
<envs />
<method />
</configuration>
<configuration default="true" type="Remote" factoryName="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true" />
<option name="SERVER_MODE" value="false" />
<option name="SHMEM_ADDRESS" value="javadebug" />
<option name="HOST" value="localhost" />
<option name="PORT" value="5005" />
<method />
</configuration>
<configuration default="true" type="TestNG" factoryName="TestNG">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="SUITE_NAME" />
<option name="PACKAGE_NAME" />
<option name="MAIN_CLASS_NAME" />
<option name="METHOD_NAME" />
<option name="GROUP_NAME" />
<option name="TEST_OBJECT" value="CLASS" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
<option name="OUTPUT_DIRECTORY" />
<option name="ANNOTATION_TYPE" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<option name="USE_DEFAULT_REPORTERS" value="false" />
<option name="PROPERTIES_FILE" />
<envs />
<properties />
<listeners />
<method />
</configuration>
<configuration default="true" type="executeSpecs" factoryName="Gauge Execution">
<setting name="environment" value="" />
<setting name="specsToExecute" value="" />
<setting name="tags" value="" />
<setting name="parallelNodes" value="" />
<setting name="execInParallel" value="false" />
<setting name="programParameters" value="" />
<setting name="workingDirectory" value="" />
<setting name="moduleName" value="" />
<envMap />
<setting name="rowsRange" value="" />
<method />
</configuration>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="Kotlin.net.torvald.terrarum.imagefont.GameFontDemoKt" />
</list>
<recent_temporary>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="Kotlin.net.torvald.terrarum.imagefont.GameFontDemoKt" />
</list>
</recent_temporary>
</component>
<component name="ShelveChangesManager" show_recycled="false">
<option name="remove_strategy" value="false" />
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="fcc8c48a-4440-438f-9e38-d42425b5efd4" name="Default" comment="" />
<created>1487243351068</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1487243351068</updated>
</task>
<servers />
</component>
<component name="ToolWindowManager">
<frame x="-8" y="-8" width="1936" height="1176" extended-state="6" />
<editor active="true" />
<layout>
<window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32898468" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" />
<window_info id="Palette&#9;" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="true" content_ui="tabs" />
<window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32960597" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32898468" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" />
<window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.17430703" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="Structure" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3054371" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25790986" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="4" side_tool="true" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Capture Tool" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="9" side_tool="false" content_ui="combo" />
<window_info id="LuaJ" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32960597" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Code Iris" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="false" weight="0.32960597" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Capture Analysis" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />
<window_info id="Documentation" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.1544196" sideWeight="0.4651163" order="10" side_tool="false" content_ui="tabs" x="1380" y="-157" width="1720" height="857" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
</layout>
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<option name="time" value="1" />
</breakpoint-manager>
<watches-manager />
</component>
<component name="antWorkspaceConfiguration">
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
<option name="FILTER_TARGETS" value="false" />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontDemo.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="518">
<caret line="43" column="0" lean-forward="false" selection-start-line="43" selection-start-column="0" selection-end-line="43" selection-end-column="0" />
<folding>
<element signature="e#40#255#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="8442">
<caret line="613" column="0" lean-forward="false" selection-start-line="613" selection-start-column="0" selection-end-line="613" selection-end-column="0" />
<folding>
<element signature="e#48#391#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontDemo.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#40#255#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="8442">
<caret line="613" column="0" lean-forward="false" selection-start-line="613" selection-start-column="0" selection-end-line="613" selection-end-column="0" />
<folding>
<element signature="e#48#391#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontDemo.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#40#255#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="8582">
<caret line="613" column="0" lean-forward="false" selection-start-line="613" selection-start-column="0" selection-end-line="613" selection-end-column="0" />
<folding>
<element signature="e#48#391#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontDemo.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="924">
<caret line="72" column="55" lean-forward="false" selection-start-line="72" selection-start-column="55" selection-end-line="72" selection-end-column="55" />
<folding>
<element signature="e#40#255#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/text.txt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="252">
<caret line="18" column="0" lean-forward="false" selection-start-line="18" selection-start-column="0" selection-end-line="18" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontImpl.kt" />
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#48#391#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontDemo.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="924">
<caret line="72" column="55" lean-forward="false" selection-start-line="72" selection-start-column="55" selection-end-line="72" selection-end-column="55" />
<folding>
<element signature="e#40#255#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/text.txt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="252">
<caret line="18" column="0" lean-forward="false" selection-start-line="18" selection-start-column="0" selection-end-line="18" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontImpl.kt" />
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#48#391#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontDemo.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1008">
<caret line="72" column="55" lean-forward="false" selection-start-line="72" selection-start-column="55" selection-end-line="72" selection-end-column="55" />
<folding>
<element signature="e#40#255#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/text.txt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="252">
<caret line="18" column="0" lean-forward="false" selection-start-line="18" selection-start-column="0" selection-end-line="18" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontImpl.kt" />
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#48#391#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontDemo.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="924">
<caret line="72" column="55" lean-forward="false" selection-start-line="72" selection-start-column="55" selection-end-line="72" selection-end-column="55" />
<folding>
<element signature="e#40#255#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontImpl.kt" />
<entry file="file://$PROJECT_DIR$/text.txt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="252">
<caret line="18" column="0" lean-forward="true" selection-start-line="18" selection-start-column="0" selection-end-line="18" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2800">
<caret line="204" column="48" lean-forward="false" selection-start-line="204" selection-start-column="48" selection-end-line="204" selection-end-column="48" />
<folding>
<element signature="e#48#391#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontDemo.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="924">
<caret line="72" column="55" lean-forward="false" selection-start-line="72" selection-start-column="55" selection-end-line="72" selection-end-column="55" />
<folding>
<element signature="e#40#255#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="6846">
<caret line="493" column="0" lean-forward="false" selection-start-line="493" selection-start-column="0" selection-end-line="493" selection-end-column="0" />
<folding>
<element signature="e#48#391#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontImpl.kt" />
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontDemo.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="e#40#255#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="7168">
<caret line="516" column="36" lean-forward="false" selection-start-line="516" selection-start-column="36" selection-end-line="516" selection-end-column="36" />
<folding>
<element signature="e#48#391#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/config.properties">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="10" lean-forward="true" selection-start-line="0" selection-start-column="10" selection-end-line="0" selection-end-column="10" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/text.txt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="95" lean-forward="false" selection-start-line="0" selection-start-column="95" selection-end-line="0" selection-end-column="95" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontImpl.kt" />
<entry file="file://$PROJECT_DIR$/META-INF/MANIFEST.MF">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="28">
<caret line="2" column="57" lean-forward="true" selection-start-line="2" selection-start-column="57" selection-end-line="2" selection-end-column="57" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/../../Ba-AA/src/net/torvald/aa/demoplayer/BaAA.java" />
<entry file="file://$PROJECT_DIR$/config.properties">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="10" lean-forward="false" selection-start-line="0" selection-start-column="10" selection-end-line="0" selection-end-column="10" />
</state>
</provider>
</entry>
<entry file="jar://$KOTLIN_BUNDLED$/lib/kotlin-runtime-sources.jar!/kotlin/Primitives.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="338">
<caret line="491" column="24" lean-forward="false" selection-start-line="491" selection-start-column="24" selection-end-line="491" selection-end-column="24" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontImpl.kt" />
<entry file="file://$PROJECT_DIR$/text.txt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="252">
<caret line="18" column="0" lean-forward="false" selection-start-line="18" selection-start-column="0" selection-end-line="18" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="jar://$PROJECT_DIR$/lib/slick.jar!/org/newdawn/slick/opengl/Texture.class">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="378">
<caret line="30" column="11" lean-forward="false" selection-start-line="30" selection-start-column="11" selection-end-line="30" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="jar://$PROJECT_DIR$/lib/slick.jar!/org/newdawn/slick/Image.class">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="504">
<caret line="48" column="14" lean-forward="false" selection-start-line="48" selection-start-column="14" selection-end-line="48" selection-end-column="14" />
</state>
</provider>
</entry>
<entry file="jar://$PROJECT_DIR$/lib/slick.jar!/org/newdawn/slick/SpriteSheet.class">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="392">
<caret line="34" column="11" lean-forward="false" selection-start-line="34" selection-start-column="11" selection-end-line="34" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontBase.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="405">
<caret line="561" column="32" lean-forward="true" selection-start-line="561" selection-start-column="32" selection-end-line="561" selection-end-column="32" />
<folding>
<element signature="e#48#391#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/net/torvald/terrarum/imagefont/GameFontDemo.kt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="434">
<caret line="43" column="0" lean-forward="false" selection-start-line="43" selection-start-column="0" selection-end-line="43" selection-end-column="0" />
<folding>
<element signature="e#40#255#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</component>
<component name="masterDetails">
<states>
<state key="ArtifactsStructureConfigurable.UI">
<settings>
<artifact-editor>
<show-content>
<type id="artifact" />
<type id="library" />
</show-content>
</artifact-editor>
<last-edited>unnamed</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.32068965" />
<option value="0.5" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="FacetStructureConfigurable.UI">
<settings>
<last-edited>Detection</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.32068965" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="GlobalLibrariesConfigurable.UI">
<settings>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="JdkListConfigurable.UI">
<settings>
<last-edited>1.8</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="ModuleStructureConfigurable.UI">
<settings>
<last-edited>Kotlin|TerrarumSansDemo</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.32068965" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="ProjectJDKs.UI">
<settings>
<last-edited>1.8</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="ProjectLibrariesConfigurable.UI">
<settings>
<last-edited>KotlinJavaRuntime</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.32068965" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>

View File

@@ -1,4 +0,0 @@
Manifest-Version: 1.0
Class-Path: lib/lwjgl.jar lib/lwjgl_util.jar lib/slick.jar lib/kotlin-reflect.jar lib/kotlin-runtime.jar
Main-Class: net.torvald.terrarum.imagefont.GameFontDemoKt

View File

@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="2" platform="JVM 1.6" useProjectSettings="false">
<compilerSettings />
<compilerArguments>
<option name="jvmTarget" value="1.6" />
<option name="languageVersion" value="1.1" />
<option name="apiVersion" value="1.1" />
<option name="coroutinesWarn" value="true" />
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component>
</module>

View File

@@ -1 +0,0 @@
locale=enUS

View File

@@ -1,700 +0,0 @@
package net.torvald.terrarumsansbitmap.slick2d
import org.newdawn.slick.Color
import org.newdawn.slick.Font
import org.newdawn.slick.Image
import org.newdawn.slick.SpriteSheet
import org.newdawn.slick.opengl.Texture
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.*
import java.util.zip.GZIPInputStream
/**
* LibGDX port of Terrarum Sans Bitmap implementation
*
* Filename and Extension for the spritesheet is hard-coded, which are:
*
* - ascii_variable.tga
* - hangul_johab.tga
* - LatinExtA_variable.tga
* - LatinExtB_variable.tga
* - kana.tga
* - cjkpunct.tga
* - wenquanyi.tga.gz
* - cyrillic_variable.tga
* - fullwidth_forms.tga
* - unipunct_variable.tga
* - greek_variable.tga
* - thai_variable.tga
* - puae000-e0ff.tga
*
*
* Glyphs are drawn lazily (calculated on-the-fly, rather than load up all), which is inevitable as we just can't load
* up 40k+ characters on the machine, which will certainly make loading time painfully long.
*
* @param noShadow Self-explanatory
* @param flipY If you have Y-down coord system implemented on your GDX (e.g. legacy codebase), set this to ```true``` so that the shadow won't be upside-down. For glyph getting upside-down, set ```TextureRegionPack.globalFlipY = true```.
*
* Created by minjaesong on 2017-06-15.
*/
class GameFontBase(fontDir: String, val noShadow: Boolean = false) : Font {
private fun getHanChosung(hanIndex: Int) = hanIndex / (JUNG_COUNT * JONG_COUNT)
private fun getHanJungseong(hanIndex: Int) = hanIndex / JONG_COUNT % JUNG_COUNT
private fun getHanJongseong(hanIndex: Int) = hanIndex % JONG_COUNT
private val jungseongWide = arrayOf(8, 12, 13, 17, 18, 21)
private val jungseongComplex = arrayOf(9, 10, 11, 14, 15, 16, 22)
private fun isJungseongWide(hanIndex: Int) = jungseongWide.contains(getHanJungseong(hanIndex))
private fun isJungseongComplex(hanIndex: Int) = jungseongComplex.contains(getHanJungseong(hanIndex))
private fun getHanInitialRow(hanIndex: Int): Int {
val ret: Int
if (isJungseongWide(hanIndex))
ret = 2
else if (isJungseongComplex(hanIndex))
ret = 4
else
ret = 0
return if (getHanJongseong(hanIndex) == 0) ret else ret + 1
}
private fun getHanMedialRow(hanIndex: Int) = if (getHanJongseong(hanIndex) == 0) 6 else 7
private fun getHanFinalRow(hanIndex: Int): Int {
val jungseongIndex = getHanJungseong(hanIndex)
return if (jungseongWide.contains(jungseongIndex))
8
else
9
}
private fun isHangul(c: Char) = c.toInt() in codeRange[SHEET_HANGUL]
private fun isAscii(c: Char) = c.toInt() in codeRange[SHEET_ASCII_VARW]
//private fun isRunic(c: Char) = runicList.contains(c)
private fun isExtA(c: Char) = c.toInt() in codeRange[SHEET_EXTA_VARW]
private fun isExtB(c: Char) = c.toInt() in codeRange[SHEET_EXTB_VARW]
private fun isKana(c: Char) = c.toInt() in codeRange[SHEET_KANA]
private fun isCJKPunct(c: Char) = c.toInt() in codeRange[SHEET_CJK_PUNCT]
private fun isUniHan(c: Char) = c.toInt() in codeRange[SHEET_UNIHAN]
private fun isCyrilic(c: Char) = c.toInt() in codeRange[SHEET_CYRILIC_VARW]
private fun isFullwidthUni(c: Char) = c.toInt() in codeRange[SHEET_FW_UNI]
private fun isUniPunct(c: Char) = c.toInt() in codeRange[SHEET_UNI_PUNCT]
private fun isGreek(c: Char) = c.toInt() in codeRange[SHEET_GREEK_VARW]
private fun isThai(c: Char) = c.toInt() in codeRange[SHEET_THAI_VARW]
private fun isDiacritics(c: Char) = c.toInt() in 0xE34..0xE3A
|| c.toInt() in 0xE47..0xE4E
|| c.toInt() == 0xE31
private fun isCustomSym(c: Char) = c.toInt() in codeRange[SHEET_CUSTOM_SYM]
private fun isArmenian(c: Char) = c.toInt() in codeRange[SHEET_HAYEREN_VARW]
private fun isKartvelian(c: Char) = c.toInt() in codeRange[SHEET_KARTULI_VARW]
private fun isIPA(c: Char) = c.toInt() in codeRange[SHEET_IPA_VARW]
private fun extAindexX(c: Char) = (c.toInt() - 0x100) % 16
private fun extAindexY(c: Char) = (c.toInt() - 0x100) / 16
private fun extBindexX(c: Char) = (c.toInt() - 0x180) % 16
private fun extBindexY(c: Char) = (c.toInt() - 0x180) / 16
//private fun runicIndexX(c: Char) = runicList.indexOf(c) % 16
//private fun runicIndexY(c: Char) = runicList.indexOf(c) / 16
private fun kanaIndexX(c: Char) = (c.toInt() - 0x3040) % 16
private fun kanaIndexY(c: Char) = (c.toInt() - 0x3040) / 16
private fun cjkPunctIndexX(c: Char) = (c.toInt() - 0x3000) % 16
private fun cjkPunctIndexY(c: Char) = (c.toInt() - 0x3000) / 16
private fun cyrilicIndexX(c: Char) = (c.toInt() - 0x400) % 16
private fun cyrilicIndexY(c: Char) = (c.toInt() - 0x400) / 16
private fun fullwidthUniIndexX(c: Char) = (c.toInt() - 0xFF00) % 16
private fun fullwidthUniIndexY(c: Char) = (c.toInt() - 0xFF00) / 16
private fun uniPunctIndexX(c: Char) = (c.toInt() - 0x2000) % 16
private fun uniPunctIndexY(c: Char) = (c.toInt() - 0x2000) / 16
private fun unihanIndexX(c: Char) = (c.toInt() - 0x3400) % 256
private fun unihanIndexY(c: Char) = (c.toInt() - 0x3400) / 256
private fun greekIndexX(c: Char) = (c.toInt() - 0x370) % 16
private fun greekIndexY(c: Char) = (c.toInt() - 0x370) / 16
private fun thaiIndexX(c: Char) = (c.toInt() - 0xE00) % 16
private fun thaiIndexY(c: Char) = (c.toInt() - 0xE00) / 16
private fun symbolIndexX(c: Char) = (c.toInt() - 0xE000) % 16
private fun symbolIndexY(c: Char) = (c.toInt() - 0xE000) / 16
private fun armenianIndexX(c: Char) = (c.toInt() - 0x530) % 16
private fun armenianIndexY(c: Char) = (c.toInt() - 0x530) / 16
private fun kartvelianIndexX(c: Char) = (c.toInt() - 0x10D0) % 16
private fun kartvelianIndexY(c: Char) = (c.toInt() - 0x10D0) / 16
private fun ipaIndexX(c: Char) = (c.toInt() - 0x250) % 16
private fun ipaIndexY(c: Char) = (c.toInt() - 0x250) / 16
private val unihanWidthSheets = arrayOf(
SHEET_UNIHAN,
SHEET_FW_UNI
)
private val variableWidthSheets = arrayOf(
SHEET_ASCII_VARW,
SHEET_EXTA_VARW,
SHEET_EXTB_VARW,
SHEET_CYRILIC_VARW,
SHEET_UNI_PUNCT,
SHEET_GREEK_VARW,
SHEET_THAI_VARW,
SHEET_HAYEREN_VARW,
SHEET_KARTULI_VARW,
SHEET_IPA_VARW
)
private val fontParentDir = if (fontDir.endsWith('/') || fontDir.endsWith('\\')) fontDir else "$fontDir/"
private val fileList = arrayOf( // MUST BE MATCHING WITH SHEET INDICES!!
"ascii_variable.tga",
"hangul_johab.tga",
"LatinExtA_variable.tga",
"LatinExtB_variable.tga",
"kana.tga",
"cjkpunct.tga",
"wenquanyi.tga.gz",
"cyrilic_variable.tga",
"fullwidth_forms.tga",
"unipunct_variable.tga",
"greek_variable.tga",
"thai_variable.tga",
"hayeren_variable.tga",
"kartuli_variable.tga",
"ipa_ext_variable.tga",
"puae000-e0ff.tga"
)
private val cyrilic_bg = "cyrilic_bulgarian_variable.tga"
private val cyrilic_sr = "cyrilic_serbian_variable.tga"
private val codeRange = arrayOf( // MUST BE MATCHING WITH SHEET INDICES!!
0..0xFF,
0xAC00..0xD7A3,
0x100..0x17F,
0x180..0x24F,
0x3040..0x30FF,
0x3000..0x303F,
0x3400..0x9FFF,
0x400..0x52F,
0xFF00..0xFF1F,
0x2000..0x205F,
0x370..0x3CE,
0xE00..0xE5F,
0x530..0x58F,
0x10D0..0x10FF,
0x250..0x2AF,
0xE000..0xE0FF
)
private val glyphWidths: HashMap<Int, Int> = HashMap() // if the value is negative, it's diacritics
private val sheets: Array<SpriteSheet>
init {
val sheetsPack = ArrayList<SpriteSheet>()
// first we create pixmap to read pixels, then make texture using pixmap
fileList.forEachIndexed { index, it ->
val isVariable1 = it.endsWith("_variable.tga")
val isVariable2 = variableWidthSheets.contains(index)
val isVariable = isVariable1 && isVariable2
// idiocity check
if (isVariable1 && !isVariable2)
throw Error("[TerrarumSansBitmap] font is named as variable on the name but not enlisted as")
else if (!isVariable1 && isVariable2)
throw Error("[TerrarumSansBitmap] font is enlisted as variable on the name but not named as")
val image: Image
// unpack gz if applicable
if (it.endsWith(".gz")) {
val gzi = GZIPInputStream(FileInputStream(fontParentDir + it))
val wholeFile = gzi.readBytes()
gzi.close()
val fos = BufferedOutputStream(FileOutputStream("tmp_wenquanyi.tga"))
fos.write(wholeFile)
fos.flush()
fos.close()
image = Image("tmp_wenquanyi.tga")
File("tmp_wenquanyi.tga").delete()
}
else {
image = Image(fontParentDir + it)
}
val texture = image.texture
if (isVariable) {
println("[TerrarumSansBitmap] loading texture $it [VARIABLE]")
buildWidthTable(texture, codeRange[index], 16)
}
else {
println("[TerrarumSansBitmap] loading texture $it")
}
val texRegPack = if (isVariable) {
SpriteSheet(image, W_VAR_INIT, H - 1, HGAP_VAR)
}
else if (index == SHEET_UNIHAN) {
SpriteSheet(image, W_UNIHAN, H_UNIHAN) // the only exception that is height is 16
}
// below they all have height of 20 'H'
else if (index == SHEET_FW_UNI) {
SpriteSheet(image, W_UNIHAN, H)
}
else if (index == SHEET_CJK_PUNCT) {
SpriteSheet(image, W_ASIAN_PUNCT, H)
}
else if (index == SHEET_KANA) {
SpriteSheet(image, W_KANA, H)
}
else if (index == SHEET_HANGUL) {
SpriteSheet(image, W_HANGUL, H)
}
else if (index == SHEET_CUSTOM_SYM) {
SpriteSheet(image, SIZE_CUSTOM_SYM, SIZE_CUSTOM_SYM) // TODO variable
}
else throw IllegalArgumentException("[TerrarumSansBitmap] Unknown sheet index: $index")
sheetsPack.add(texRegPack)
}
sheets = sheetsPack.toTypedArray()
}
private var localeBuffer = ""
fun reload(locale: String) {
if (!localeBuffer.startsWith("ru") && locale.startsWith("ru")) {
val image = Image(fontParentDir + fileList[SHEET_CYRILIC_VARW])
sheets[SHEET_CYRILIC_VARW].destroy()
sheets[SHEET_CYRILIC_VARW] = SpriteSheet(image, W_VAR_INIT, H, HGAP_VAR, 0)
}
else if (!localeBuffer.startsWith("bg") && locale.startsWith("bg")) {
val image = Image(fontParentDir + cyrilic_bg)
sheets[SHEET_CYRILIC_VARW].destroy()
sheets[SHEET_CYRILIC_VARW] = SpriteSheet(image, W_VAR_INIT, H, HGAP_VAR, 0)
}
else if (!localeBuffer.startsWith("sr") && locale.startsWith("sr")) {
val image = Image(fontParentDir + cyrilic_sr)
sheets[SHEET_CYRILIC_VARW].destroy()
sheets[SHEET_CYRILIC_VARW] = SpriteSheet(image, W_VAR_INIT, H, HGAP_VAR, 0)
}
localeBuffer = locale
}
override fun getLineHeight(): Int = H
override fun getHeight(p0: String) = lineHeight
private val offsetUnihan = (H - H_UNIHAN) / 2
private val offsetCustomSym = (H - SIZE_CUSTOM_SYM) / 2
private var textBuffer: CharSequence = ""
private var textBWidth = intArrayOf() // absolute posX of glyphs from print-origin
private var textBGSize = intArrayOf() // width of each glyph
override fun drawString(x: Float, y: Float, str: String) {
drawString(x, y, str, Color.white)
}
override fun drawString(p0: Float, p1: Float, p2: String?, p3: Color?, p4: Int, p5: Int) {
throw UnsupportedOperationException()
}
override fun drawString(x: Float, y: Float, str: String, color: Color) {
if (textBuffer != str) {
textBuffer = str
val widths = getWidthOfCharSeq(str)
textBGSize = widths
textBWidth = IntArray(str.length, { charIndex ->
if (charIndex == 0)
0
else {
var acc = 0
(0..charIndex - 1).forEach { acc += maxOf(0, widths[it]) } // don't accumulate diacrtics (which has negative value)
/*return*/acc
}
})
}
//print("[TerrarumSansBitmap] widthTable for $textBuffer: ")
//textBWidth.forEach { print("$it ") }; println()
val mainCol = color
val shadowCol = color.darker(0.5f)
textBuffer.forEachIndexed { index, c ->
val sheetID = getSheetType(c)
val sheetXY = getSheetwisePosition(c)
//println("[TerrarumSansBitmap] sprite: $sheetID:${sheetXY[0]}x${sheetXY[1]}")
if (sheetID == SHEET_HANGUL) {
val hangulSheet = sheets[SHEET_HANGUL]
val hIndex = c.toInt() - 0xAC00
val indexCho = getHanChosung(hIndex)
val indexJung = getHanJungseong(hIndex)
val indexJong = getHanJongseong(hIndex)
val choRow = getHanInitialRow(hIndex)
val jungRow = getHanMedialRow(hIndex)
val jongRow = getHanFinalRow(hIndex)
if (!noShadow) {
hangulSheet.getSubImage(indexCho, choRow ).draw(x + textBWidth[index] + 1, y, shadowCol)
hangulSheet.getSubImage(indexCho, choRow ).draw(x + textBWidth[index] , y, shadowCol)
hangulSheet.getSubImage(indexCho, choRow ).draw(x + textBWidth[index] + 1, y, shadowCol)
hangulSheet.getSubImage(indexJung, jungRow).draw(x + textBWidth[index] + 1, y, shadowCol)
hangulSheet.getSubImage(indexJung, jungRow).draw(x + textBWidth[index] , y, shadowCol)
hangulSheet.getSubImage(indexJung, jungRow).draw(x + textBWidth[index] + 1, y, shadowCol)
hangulSheet.getSubImage(indexJong, jongRow).draw(x + textBWidth[index] + 1, y, shadowCol)
hangulSheet.getSubImage(indexJong, jongRow).draw(x + textBWidth[index] , y, shadowCol)
hangulSheet.getSubImage(indexJong, jongRow).draw(x + textBWidth[index] + 1, y, shadowCol)
}
hangulSheet.getSubImage(indexCho, choRow ).draw(x + textBWidth[index], y, mainCol)
hangulSheet.getSubImage(indexJung, jungRow).draw(x + textBWidth[index], y, mainCol)
hangulSheet.getSubImage(indexJong, jongRow).draw(x + textBWidth[index], y, mainCol)
}
else {
try {
val offset = if (!isDiacritics(c)) 0 else {
if (index > 0) // LIMITATION: does not support double (or more) diacritics properly
(textBGSize[index] - textBGSize[index - 1]) / 2
else
textBGSize[index]
}
if (!noShadow) {
sheets[sheetID].getSubImage(sheetXY[0], sheetXY[1]).draw(
x + textBWidth[index] + 1 + offset,
y + (if (sheetID == SHEET_UNIHAN) // evil exceptions
offsetUnihan
else if (sheetID == SHEET_CUSTOM_SYM)
offsetCustomSym
else
0),
shadowCol
)
sheets[sheetID].getSubImage(sheetXY[0], sheetXY[1]).draw(
x + textBWidth[index] + offset,
y + (if (sheetID == SHEET_UNIHAN) // evil exceptions
offsetUnihan + 1
else if (sheetID == SHEET_CUSTOM_SYM)
offsetCustomSym + 1
else
1),
shadowCol
)
sheets[sheetID].getSubImage(sheetXY[0], sheetXY[1]).draw(
x + textBWidth[index] + 1 + offset,
y + (if (sheetID == SHEET_UNIHAN) // evil exceptions
offsetUnihan + 1
else if (sheetID == SHEET_CUSTOM_SYM)
offsetCustomSym + 1
else
1),
shadowCol
)
}
sheets[sheetID].getSubImage(sheetXY[0], sheetXY[1]).draw(
x + textBWidth[index] + offset,
y + if (sheetID == SHEET_UNIHAN) // evil exceptions
offsetUnihan
else if (sheetID == SHEET_CUSTOM_SYM)
offsetCustomSym
else 0,
mainCol
)
}
catch (noSuchGlyph: ArrayIndexOutOfBoundsException) {
}
}
}
}
fun dispose() {
sheets.forEach { it.destroy() }
}
private fun getWidthOfCharSeq(s: CharSequence): IntArray {
val len = IntArray(s.length)
for (i in 0..s.lastIndex) {
val chr = s[i]
val ctype = getSheetType(s[i])
if (variableWidthSheets.contains(ctype)) {
if (!glyphWidths.containsKey(chr.toInt())) {
println("[TerrarumSansBitmap] no width data for glyph number ${Integer.toHexString(chr.toInt()).toUpperCase()}")
len[i] = W_LATIN_WIDE
}
len[i] = glyphWidths[chr.toInt()]!!
}
else if (ctype == SHEET_CJK_PUNCT)
len[i] = W_ASIAN_PUNCT
else if (ctype == SHEET_HANGUL)
len[i] = W_HANGUL
else if (ctype == SHEET_KANA)
len[i] = W_KANA
else if (unihanWidthSheets.contains(ctype))
len[i] = W_UNIHAN
else if (ctype == SHEET_CUSTOM_SYM)
len[i] = SIZE_CUSTOM_SYM
else
len[i] = W_LATIN_WIDE
if (scale > 1) len[i] *= scale
if (i < s.lastIndex) len[i] += interchar
}
return len
}
private fun getSheetType(c: Char): Int {
if (isHangul(c))
return SHEET_HANGUL
else if (isKana(c))
return SHEET_KANA
else if (isUniHan(c))
return SHEET_UNIHAN
else if (isAscii(c))
return SHEET_ASCII_VARW
else if (isExtA(c))
return SHEET_EXTA_VARW
else if (isExtB(c))
return SHEET_EXTB_VARW
else if (isCyrilic(c))
return SHEET_CYRILIC_VARW
else if (isUniPunct(c))
return SHEET_UNI_PUNCT
else if (isCJKPunct(c))
return SHEET_CJK_PUNCT
else if (isFullwidthUni(c))
return SHEET_FW_UNI
else if (isGreek(c))
return SHEET_GREEK_VARW
else if (isThai(c))
return SHEET_THAI_VARW
else if (isCustomSym(c))
return SHEET_CUSTOM_SYM
else if (isArmenian(c))
return SHEET_HAYEREN_VARW
else if (isKartvelian(c))
return SHEET_KARTULI_VARW
else if (isIPA(c))
return SHEET_IPA_VARW
else
return SHEET_UNKNOWN
// fixed width
// fallback
}
private fun getSheetwisePosition(ch: Char): IntArray {
val sheetX: Int; val sheetY: Int
when (getSheetType(ch)) {
SHEET_UNIHAN -> {
sheetX = unihanIndexX(ch)
sheetY = unihanIndexY(ch)
}
SHEET_EXTA_VARW -> {
sheetX = extAindexX(ch)
sheetY = extAindexY(ch)
}
SHEET_EXTB_VARW -> {
sheetX = extBindexX(ch)
sheetY = extBindexY(ch)
}
SHEET_KANA -> {
sheetX = kanaIndexX(ch)
sheetY = kanaIndexY(ch)
}
SHEET_CJK_PUNCT -> {
sheetX = cjkPunctIndexX(ch)
sheetY = cjkPunctIndexY(ch)
}
SHEET_CYRILIC_VARW -> {
sheetX = cyrilicIndexX(ch)
sheetY = cyrilicIndexY(ch)
}
SHEET_FW_UNI -> {
sheetX = fullwidthUniIndexX(ch)
sheetY = fullwidthUniIndexY(ch)
}
SHEET_UNI_PUNCT -> {
sheetX = uniPunctIndexX(ch)
sheetY = uniPunctIndexY(ch)
}
SHEET_GREEK_VARW -> {
sheetX = greekIndexX(ch)
sheetY = greekIndexY(ch)
}
SHEET_THAI_VARW -> {
sheetX = thaiIndexX(ch)
sheetY = thaiIndexY(ch)
}
SHEET_CUSTOM_SYM -> {
sheetX = symbolIndexX(ch)
sheetY = symbolIndexY(ch)
}
SHEET_HAYEREN_VARW -> {
sheetX = armenianIndexX(ch)
sheetY = armenianIndexY(ch)
}
SHEET_KARTULI_VARW -> {
sheetX = kartvelianIndexX(ch)
sheetY = kartvelianIndexY(ch)
}
SHEET_IPA_VARW -> {
sheetX = ipaIndexX(ch)
sheetY = ipaIndexY(ch)
}
else -> {
sheetX = ch.toInt() % 16
sheetY = ch.toInt() / 16
}
}
return intArrayOf(sheetX, sheetY)
}
fun buildWidthTable(texture: Texture, codeRange: IntRange, cols: Int = 16) {
val binaryCodeOffset = W_VAR_INIT
val cellW = W_VAR_INIT + 1
val cellH = H
for (code in codeRange) {
val cellX = ((code - codeRange.start) % cols) * cellW
val cellY = ((code - codeRange.start) / cols) * cellH
val codeStartX = cellX + binaryCodeOffset
val codeStartY = cellY
var glyphWidth = 0
for (downCtr in 0..3) {
// if ALPHA is not zero, assume it's 1
if (texture.textureData[4 * (codeStartX + (codeStartY + downCtr) * texture.textureWidth) + 3] != 0.toByte()) {
glyphWidth = glyphWidth or (1 shl downCtr)
}
}
val isDiacritics = texture.textureData[4 * (codeStartX + (codeStartY + H - 1) * texture.textureWidth) + 3] != 0.toByte()
if (isDiacritics)
glyphWidth = -glyphWidth
glyphWidths[code] = glyphWidth
}
}
override fun getWidth(text: String): Int {
return getWidthOfCharSeq(text).sum()
}
companion object {
internal val JUNG_COUNT = 21
internal val JONG_COUNT = 28
internal val W_ASIAN_PUNCT = 10
internal val W_HANGUL = 12
internal val W_KANA = 12
internal val W_UNIHAN = 16
internal val W_LATIN_WIDE = 9 // width of regular letters
internal val W_VAR_INIT = 15
internal val HGAP_VAR = 1
internal val H = 20
internal val H_UNIHAN = 16
internal val SIZE_CUSTOM_SYM = 18
internal val SHEET_ASCII_VARW = 0
internal val SHEET_HANGUL = 1
internal val SHEET_EXTA_VARW = 2
internal val SHEET_EXTB_VARW = 3
internal val SHEET_KANA = 4
internal val SHEET_CJK_PUNCT = 5
internal val SHEET_UNIHAN = 6
internal val SHEET_CYRILIC_VARW = 7
internal val SHEET_FW_UNI = 8
internal val SHEET_UNI_PUNCT = 9
internal val SHEET_GREEK_VARW = 10
internal val SHEET_THAI_VARW = 11
internal val SHEET_HAYEREN_VARW = 12
internal val SHEET_KARTULI_VARW = 13
internal val SHEET_IPA_VARW = 14
internal val SHEET_CUSTOM_SYM = 15
internal val SHEET_UNKNOWN = 254
/**
* Runic letters list used for game. The set is
* Younger Futhark + Medieval rune 'e' + Punct + Runic Almanac
* BEWARE OF SIMILAR-LOOKING RUNES, especially:
* * Algiz ᛉ instead of Maðr ᛘ
* * Short-Twig Hagall ᚽ instead of Runic Letter E ᛂ
* * Runic Letter OE ᚯ instead of Óss ᚬ
* Examples:
* ᛭ᛋᛁᚴᚱᛁᚦᛦ᛭
* ᛭ᛂᛚᛋᛅ᛭ᛏᚱᚢᛏᚾᛁᚾᚴᚢᚾᛅ᛬ᛅᚱᚾᛅᛏᛅᛚᛋ
*/
//internal val runicList = arrayOf('ᚠ', 'ᚢ', 'ᚦ', 'ᚬ', 'ᚱ', 'ᚴ', 'ᚼ', 'ᚾ', '', 'ᛅ', 'ᛋ', 'ᛏ', 'ᛒ', 'ᛘ', 'ᛚ', 'ᛦ', 'ᛂ', '', '᛫', '', 'ᛮ', 'ᛯ', 'ᛰ')
// TODO expand to full Unicode runes
var interchar = 0
var scale = 1
set(value) {
if (value > 0) field = value
else throw IllegalArgumentException("Font scale cannot be zero or negative (input: $value)")
}
}
}

View File

@@ -1,97 +0,0 @@
package net.torvald.terrarum.imagefont
import net.torvald.terrarumsansbitmap.slick2d.GameFontBase
import org.newdawn.slick.*
import java.io.File
import java.io.FileInputStream
import java.nio.file.FileSystems
import java.nio.file.Files
import java.util.*
/**
* Created by SKYHi14 on 2017-02-16.
*/
class GameFontDemo : BasicGame("Terrarum Sans Bitmap Demo") {
lateinit var gameFont: Font
override fun init(p0: GameContainer?) {
gameFont = GameFontBase("assets/")
}
override fun update(gc: GameContainer, delta: Int) {
appgc!!.setTitle("Terrarum Sans Bitmap — F: ${gc.fps}")
}
override fun render(gc: GameContainer, g: Graphics) {
g.font = gameFont
g.background = Color(0x282828)
text.forEachIndexed { i, s ->
g.drawString(s, 10f, 10f + i * gameFont.lineHeight)
}
}
companion object {
lateinit var gameLocale: String
var hasError: Boolean
val text = ArrayList<String>()
init {
try {
val prop = Properties()
prop.load(FileInputStream("./config.properties"))
gameLocale = prop.getProperty("locale")
if (gameLocale.length < 2)
throw IllegalArgumentException("Bad locale setting: “$gameLocale")
Files.lines(FileSystems.getDefault().getPath("./text.txt")).forEach(
{ text.add(it) }
)
hasError = false
}
catch (e: Exception) {
gameLocale = "enUS"
hasError = true
text.add("There was some problem loading the demo :(")
text.add("This is what JVM says:")
text.add("")
e.message!!.split('\n').forEach {
text.add(it)
}
}
}
}
}
var appgc: AppGameContainer? = null
fun main(args: Array<String>) {
System.setProperty("java.library.path", "lib")
System.setProperty("org.lwjgl.librarypath", File("lib").absolutePath)
val WIDTH = 1000
val HEIGHT = 800
try {
appgc = AppGameContainer(GameFontDemo())
appgc!!.setDisplayMode(WIDTH, HEIGHT, false)
appgc!!.setMultiSample(0)
appgc!!.setShowFPS(false)
// game will run normally even if it is not focused
appgc!!.setUpdateOnlyWhenVisible(false)
appgc!!.alwaysRender = true
appgc!!.start()
}
catch (ex: Exception) {
ex.printStackTrace()
}
}

View File

@@ -1,43 +0,0 @@
x86 се нарича общата микроархитектура на фамилия микропроцесори, основана на Intel 8086. Първите процесори от тази
фамилия, разработени и произвеждани от Intel, са обозначавани с номера, завършващи на 86: 8086, 80186, 80286, 80386
(впоследствие i386) и 486 (i486).
x86 — Intel 8086 prosessorunun arxitekturasının təlimatına əsasən yaradılan prosessor arxitekturası ailəsi.
x86 (= 80x86) IBM PC-uyumlu kompüterlərdə istifadə olunan Intel mikroprosessorları ailəsi (x = 1, ..., 5 və 8086
üçün olmaya bilər). Bu ailəyə 086, 186, 286, 386, 486, Pentium, Pentium Pro, Pentium II aiddir.
Ο όρος x86 αναφέρεται σε μια οικογένεια από αρχιτεκτονικές συνόλου εντολών που βασίζονται στην CPU Intel 8086.
Ο επεξεργαστής 8086 κυκλοφόρησε το 1978 σαν μια πλήρως 16-bit επέκταση του 8080 της Intel που βασιζόταν σε τεχνολογία
8-bit, και εισήγαγε το segmentation για να ξεπεράσει το φράγμα των 16-bit διευθύνσεων της προηγούμενης σχεδίασης.
x86 또는 80x86은 인텔이 개발한 마이크로프로세서 계열을 부르는 말이자, 이들과 호환되는 프로세서들에서 사용한 명령어 집합 구조들을 통칭하는
말이다. x86 아키텍처는 데스크톱 컴퓨터 시장에서 매우 널리 쓰이며, PowerPC 같이 좀 더 근대적인 아키텍처를 사용한 프로세서들이 x86과 경쟁했으나
그다지 많은 시장 점유율을 확보하지는 못했다.
x86(Intel 80x86)-ն ապարատային պլատֆորմ է, որը հանդիսանում է միկրոպրոցեսորի ճարտարապետություն և հրամանների
համապատասխան հավաքածու։ Այն մշակված է և թողարկված, ինչպես Intel-ի, այնպես էլ նրա հետ համատեղելի պրոցեսորների
համար, որոնք ստեղծվել են այլ արտադրողների կողմից։
Pentium არის კომპანია ინტელის მიერ 1993 წელს გამოშვებული პროცესორი. იგი იყო წარმატებული i486-CPU სერიის
ჩიპების მომდევნო თაობა. Pente (πέντε) ბერძნული სიტყვაა და ხუთს ნიშნავს. იგი ხომ ინტელის სახლის x86 სერიის
ჩიპების მეხუთე თაობას წარმოადგენდა.
x86エックスはちろくは、Intel 8086、およびその後方互換性を持つマイクロプロセッサの命令セットアーキテクチャの総称。
16ビットの8086で登場し、32ビット拡張の80386後にIA-32と命名、64ビット拡張のx64、広義には更にAMDなどの互換プロセッサを含む。
x86 (англ. Intel 80x86) — архитектура процессора c одноимённым набором команд, впервые реализованная в процессорах
компании Intel. Название образовано от двух цифр, которыми заканчивались названия процессоров Intel ранних моделей —
8086, 80186, 80286 (i286), 80386 (i386), 80486 (i486).
x86 on Intelin kehittämä ja valmistama suoritinarkkitehtuuri. Intel ei kuitenkaan ole arkkitehtuurin ainoa valmistaja
vaan myös AMD, Cyrix, NEC, Transmeta, VIA ja Zilog ovat valmistaneet x86-yhteensopivia suorittimia.
เอกซ์86 (x86) เป็นชื่อทั่วไปของสถาปัตยกรรมคอมพิวเตอร์สำหรับไมโครโพรเซสเซอร์ที่สร้างโดยบริษัทอินเทล ปัจจุบันสถาปัตยกรรมแบบ x86
เป็นสถาปัตยกรรมที่ได้รับความนิยมมากที่สุดในตลาดคอมพิวเตอร์เดสก์ท็อป, โน้ตบุ๊คและเซิร์ฟเวอร์ขนาดเล็ก นับตั้งแต่เริ่มใช้ในไอบีเอ็มพีซี ช่วงทศวรรษที่ 80
x86 (англ. Intel 80x86) — архітектура процесорів та ядра мікроконтролерів з однойменною системою команд. x86 або 80x86
є загальною назвою мікропроцесорної архітектури та архітектури розробленої на її базі обчислювальної техніки.
x86是一個指令集架構家族最早由英特爾在1978年面市的「Intel 8086」CPU上開發出來。
該系列較早期的處理器名稱是以數字來表示80x86。由於以“86”作為結尾包括Intel 8086、80186、80286、80386以及80486因此其架
構被稱為“x86”。由於數字並不能作為註冊商標因此Intel及其競爭者均在新一代處理器使用可註冊的名稱如Pentium。

130
demotext.txt Executable file
View File

@@ -0,0 +1,130 @@
􏿆The bitmap font for game developers who seek good font that has real multilingual support,􀀀
􏿆for free (as in freedom and without cost)􀀀
There are many bitmap fonts on the internet. You care for the multilingual support, but alas!
most of them do not support your language, vector fonts take too much time to load, and even
then their legibility suffers because screw built-in antialias.
You somehow found a multilingual one, and it makes your text as if they came straight from an old
computer terminal, and you say:
“Well, better than nothing… no, its ugly.”
You speak Japanese and wish to support it, but then このクソなfont only good for displaying
Japanese, its not even truly multilingual, and their English look uncanny and inconsistent.
Eventually you just mix different fonts together, and the results were always mildly infuriating.
No more suffering. This font has everything you need.
􏾐while􀀀 (isVisible(􏺟BAD_FONTS􀀀)) {
ripAndTear(􏺟BAD_FONTS􀀀).where { 􏺟GUTS􀀀 };
}
How multilingual? Real multilingual!
􏻬Ианҵоуп ақьаад, нусхур аҩырала, ҩ ҽшьаҟакла, иҧшӡоу анапҩырала􀀀
􏻬գրիչս վայր դրի, վեր կացա և պատրաստվում էի, որ քնեմ, երբ հանկարծ դռանս զանգակը հնչեց􀀀
􏻬ՄՇԱԿԻՉ ԿԱՄ ԿԵՆՏՐՈՆԱԿԱՆ ՄՇԱԿԻՉ ՀԱՆԳՈՒՅՑԸ ՀԱՆԴԻՍԱՆՈՒՄ Է ՀԱՄԱԿԱՐԳՉԻ ՍԱՐՔԱՎՈՐՈՒՄՆԵՐԻՑ􀀀
􏻬Zəfər, jaketini də papağını da götür, bu axşam hava çox soyuq olacaq􀀀
􏻬আমি কাঁচ খেতে পারি, তাতে আমার কোনো ক্ষতি হয় না। 􀀀
􏻬󿿁Под южно дърво, цъфтящо в синьо, бягаше малко пухкаво зайче󿿀􀀀
􏻬ᎠᏍᎦᏯᎡᎦᎢᎾᎨᎢᎣᏍᏓᎤᎩᏍᏗᎥᎴᏓᎯᎲᎢᏔᎵᏕᎦᏟᏗᏖᎸᎳᏗᏗᎧᎵᎢᏘᎴᎩ ᏙᏱᏗᏜᏫᏗᏣᏚᎦᏫᏛᏄᏓᎦᏝᏃᎠᎾᏗᎭᏞᎦᎯᎦᏘᏓᏠᎨᏏᏕᏡᎬᏢᏓᏥᏩᏝᎡᎢᎪᎢ􀀀
􏻬ᎠᎦᏂᏗᎮᎢᎫᎩᎬᏩᎴᎢᎠᏆᏅᏛᎫᏊᎾᎥᎠᏁᏙᎲᏐᏈᎵᎤᎩᎸᏓᏭᎷᏤᎢᏏᏉᏯᏌᏊ ᎤᏂᏋᎢᏡᎬᎢᎰᏩᎬᏤᎵᏍᏗᏱᎩᎱᎱᎤᎩᎴᎢᏦᎢᎠᏂᏧᏣᏨᎦᏥᎪᎥᏌᏊᎤᎶᏒᎢᎢᏡᎬᎢ􀀀
􏻬ᎹᎦᎺᎵᏥᎻᎼᏏᎽᏗᏩᏂᎦᏘᎾᎿᎠᏁᎬᎢᏅᎩᎾᏂᎡᎢᏌᎶᎵᏎᎷᎠᏑᏍᏗᏪᎩ ᎠᎴ ᏬᏗᏲᏭᎾᏓᏍᏓᏴᏁᎢᎤᎦᏅᏮᏰᎵᏳᏂᎨᎢ􀀀
􏻬Příliš žluťoučký kůň úpěl ďábelské ódy􀀀
􏻬Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Walther spillede på xylofon􀀀
􏻬PACK MY BOX WITH FIVE DOZEN LIQUOR JUGS􀀀
􏻬hƿæt ƿe ᵹardena inᵹear ꝺaᵹum þeoꝺ cynninᵹa þꞃym ᵹeꝼꞃumon􀀀
􏻬Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich GROẞEN GROẞE􀀀
􏻬ζαφείρι δέξου πάγκαλο, βαθῶν ψυχῆς τὸ σῆμα􀀀
􏻬ΔΙΑΦΥΛΆΞΤΕ ΓΕΝΙΚΆ ΤΗ ΖΩΉ ΣΑΣ ΑΠΌ ΒΑΘΕΙΆ ΨΥΧΙΚΆ ΤΡΑΎΜΑΤΑ􀀀
􏻬სწრაფი ყავისფერი მელა გადაახტა ზარმაც ძაღლს ᲘᲜᲢᲔᲚ ᲞᲔᲜᲢᲘᲣᲛᲘ ᲛᲘᲙᲠᲝᲞᲠᲝᲪᲔᲡᲝᲠᲘ􀀀
􏻬ऋषियों को सताने वाले दुष्ट राक्षसों के राजा रावण का सर्वनाश करने वाले विष्णुवतार भगवान श्रीराम अयोध्या के महाराज दशरथ के􀀀
􏻬Kæmi ný öxi hér, ykist þjófum nú bæði víl og ádrepa􀀀
􏻬Ċuaiġ bé ṁórṡáċ le dlúṫspád fíoꝛḟinn trí hata mo ḋea-ṗoꝛcáin ḃig􀀀
􏻬あめつちほしそら やまかはみねたに くもきりむろこけ ひといぬうへすゑ ゆわさるおふせよ えの𛀁をなれゐて􀀀
􏻬トリナクコヱス ユメサマセ ミヨアケワタル ヒンカシヲ ソライロハエテ オキツヘニ ホフネムレヰヌ モヤノウチ􀀀
􏻬田居に出で 菜摘むわれをぞ 君召すと 求食り追ひゆく 山城の 打酔へる子ら 藻葉干せよ え舟繋けぬ􀀀
􏻬정 참판 양반댁 규수 큰 교자 타고 혼례 치른 날 하얬다 도럄직한 퀡봹퉪헰ꥸᅦퟗꥸᅦퟗᄋힳᆫㅗㅜㅑㄷ􀀀
􏻬Četri psihi faķīri vēlu vakarā zāģēja guļbūvei durvis, fonā šņācot mežam􀀀
􏻬Įlinkdama fechtuotojo špaga sublykčiojusi pragręžė apvalų arbūzą􀀀
􏻬Ѕидарски пејзаж: шугав билмез со чудење џвака ќофте и кељ на туѓ цех􀀀
􏻬Щётканы фермд пийшин цувъя. Бөгж зогсч хэльюү􀀀
􏻬Pchnąć w tę łódź jeża lub ośm skrzyń fig􀀀
􏻬Înjurând pițigăiat, zoofobul comandă vexat whisky și tequila􀀀
􏻬Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства􀀀
􏻬अद्वैतामृवर्षिणीं भगवतीमष्टादशाध्यायिनीम् अम्ब त्वामनुसन्दधामि भगवद्गीते भवद्वेषिणीम् ॥􀀀
􏻬󿿂Ајшо, лепото и чежњо, за љубав срца мога дођи у Хаџиће на кафу󿿀􀀀
􏻬ᮙᮛᮔᮨᮂᮔ ᮓᮤᮘᮨᮛᮨ ᮃᮊᮜ᮪ ᮏᮩᮀ ᮠᮒᮨ ᮔᮥᮛᮔᮤ, ᮎᮙ᮪ᮕᮥᮁ-ᮌᮅᮜ᮪ ᮏᮩᮀ ᮞᮞᮙᮔ ᮃᮚ ᮓᮤᮔ ᮞᮥᮙᮔᮨᮒ᮪ ᮓᮥᮓᮥᮜᮥᮛᮔ᮪􀀀
􏻬Jovencillo emponzoñado de whisky: ¡qué figurota exhibe!􀀀
􏻬எழுத்து வடிவங்களுக்கு வார்த்தைகள் மற்றும் வாக்கியங்கள் போலவே தொனி, ஒலி பண்பு, தன்மை உண்டு􀀀
􏻬นายสังฆภัณฑ์ เฮงพิทักษ์ฝั่ง ผู้เฒ่าซึ่งมีอาชีพเป็นฅนขายฃวด ถูกตำรวจปฏิบัติการจับฟ้องศาล ฐานลักนาฬิกาคุณหญิงฉัตรชฎา ฌานสมาธิ􀀀
􏻬Pijamalı hasta yağız şoföre çabucak güvendi􀀀
􏻬Жебракують філософи при ґанку церкви в Гадячі, ще й шатро їхнє п’яне знаємо􀀀
􏻬Do bạch kim rất quý nên sẽ dùng để lắp vô xương􀀀
􏻬日堀油告観観藤村抄海評業庁経賃室弁市。太撮収改売週法所何都慣次現。価紙一無三洋日話転手治稿載末替付致治。􀀀
􏻬[pʰnɣɬɥi.m͡ŋχɫʍɨnaɸ.cθʊɫɯ.ɹɨɫʏ͡ɛx.ɯ͡ɣaxɲaɣɫ.ɸtʰɑɣɴ]􀀀
􏻬⠑⠥⠊⠵⠀⠟⠫⠒⠵⠀⠓⠗⠎⠉⠂⠀⠠⠊⠗⠘⠍⠓⠎⠀⠨⠣⠩⠐⠥⠍⠑⠱⠀⠈⠪⠀⠨⠷⠎⠢⠈⠧⠀⠈⠏⠒⠐⠕⠝⠀⠕⠌⠎⠀⠊⠿⠊⠪⠶⠚⠊􀀀
􏃯Features:􀀀
􏿿Ever heard of « Guillemets »? You speak „Nederlands” or „Deutsch“? ”suomi”? 「日本語」しゃべる?或《中文》?􀀀
􎳌Multilingual brackets; these are the most basic things, right?􀀀
􏋶99406 91902 59186 29182 45814 57862 34368 08240􀀀
􏋶31485 65379 04348 28952 42927 26157 42615 39526􀀀
􎳌Fixed-width numbers, because number-crunching matters􀀀
􏼀ᚱ􏼐ᛂ􏼠ᚴ􏼰ᛋ􏽀ᛂ􏽐ᛋ􏽠ᛏ􏽰ᛋ􏾀ᚮ􏾐ᚾ􏾠ᛔ􏾰ᚢ􏿀ᛏ􏿐ᛚ􏿠ᚮ􏿰ᛋ􏻰᛬􏷰ᚱ􏳰ᛂ􏯰ᚴ􏫰ᛋ􏧰ᛋ􏣰ᚢ􏟰ᚼ􏛰ᚾ􏗰ᚢ􏓰ᛘ􏏰ᚢ􏋰ᛚ􏇰ᚾ􏃰ᛏ􏃱ᚮ􏃲᛬􏃳ᛏ􏃴ᚮ􏃵ᛋ􏃶ᛁ􏃷ᚮ􏃸ᚵ􏃹ᛂ􏃺ᚢ􏃻ᛏ􏃼ᚮ􏃽ᚱ􏃾ᛘ􏃿ᛔ􏃯ᚱ􏃟ᛂ􏃏ᚴ􏂿ᛋ􏂯ᛏ􏂟᛭􏂏ᛋ􏁿ᚢ􏁯ᚼ􏁟ᚾ􏁏ᚢ􏀿ᛋ􏀯ᛘ􏀟ᚮ􏀏ᛁ􏄏ᚵ􏈏ᚾ􏌏ᛁ􏐏ᛂ􏔏ᛏ􏘏ᚮ􏜏ᛑ􏠏᛭􏤏ᚵ􏨏ᛂ􏬏ᚢ􏰏ᛏ􏴏ᚮ􏸏ᚱ􀀀
􎳌Colour-code that supports 4096 colours via 4 bit RGB􀀀
􏿿󿿁Щиглецът се яде само пържен в юфка без чушки и хвойна󿿀 (Bulgarian)􀀀
􏿿󿿂Љубазни фењерџија чађавог лица хоће да ми покаже штос󿿀 (Serbian)􀀀
􏿿Разъяренный чтец эгоистично бьёт пятью жердями шустрого фехтовальщика (Russian)􀀀
􎳌Control characters to support Bulgarian and Serbian letter shapes on the fly􀀀
􏿿Press  to pay respects—or is it , or …􀀀
􎳌Custom symbols for video games􀀀
􏿿Can you distinguish following dashes: - — - - ――――――――――― 4863 48-63􀀀
􎳌For all those dash-pedants, we have en-dash, em-dash, and even horizontal bars!􀀀
􏿿5¹⁹₃₂ inch is 142.1 mm · (C₂F₄)ₙ is godly · Error = MoreCode²􀀀
􏿿NOTE: we dont do fractions. 142¹₁₀ mm is illegal!􀀀
􎳌Did you know Unicode supports arbitrary fractions? Actually I didnt… as they abused super/subscripts!􀀀
􏿿ᄋᆡ급 일 ᄭᅡᄃᆞᆰ에 영국 ᄒᆞ고 불난셔 ᄒᆞ고 ᄃᆡ단이 시비가 잇슬 모양 인ᄃᆡ􀀀
􏿿일본 농샹공부에셔 젼긔학 학ᄉᆞ 셋ᄉᆞᆯ 미국과 구라파로 보내셔 젼화 쓰ᄂᆞᆫ 법을 더ᄇᆡ호게 ᄒᆞ더라􀀀
􏿿일쳔 구ᄇᆡᆨ년에 불난셔 셔울 파리스에 만국 박남회를 버릴터 인ᄃᆡ 각국이 다 물화와 졔조품을 거긔 보낼터이더라􀀀
􎳌Rejoice, now we can render Old Korean in a correct way!􀀀
􏃯Supported Unicode Blocks:􀀀
Basic Latin Latin-1 Supplement Latin Extended-A Latin Extended-B IPA Extensions Spacing Modifier Letters
Combining Diacritical Marks Greek and Coptic􏿆ᴱ􀀀 Cyrillic􏿆ᴭ􀀀 Cyrillic Supplement􏿆ᴭ􀀀 Armenian Devanagari
Bengali􏿆ᶠⁱ􀀀 Tamil Thai Georgian􏿆ჼ􀀀 Hangul Jamo Cherokee􏿆⁷􀀀 Runic Sundanese Georgian Extended
Sundanese Supplement Phonetic Extensions Phonetic Extensions Supplement Latin Extended Additional
Greek Extended General Punctuations Superscripts and Subscripts Currency Symbols Letterlike Symbols
Braile Patterns Latin Extended-C CJK Symbols and Punctuation Hiragana Katakana
Hangul Compatibility Jamo Katakana Phonetic Extensions CJK Unified Ideographs Extension A􏿆¹²·¹􀀀
CJK Unified Ideographs􏿆⁶􀀀 Latin Extended-D Hangul Jamo Extended-A Hangul Syllables
Hangul Jamo Extended-B Halfwidth and Fullwidth Forms Kana Supplement􏿆⁹􀀀 Enclosed Alphanumeric Supplement
􏿆ᴭ􀀀 No support for archæic letters 􏿆ᴱ􀀀 No support for Coptic
􏿆ᶠⁱ􀀀 No support for ligatures 􏿆ჼ􀀀 Mkhedruli only
􏿆⁶􀀀 􏿆⁷􀀀 􏿆⁹􀀀 􏿆¹²·¹􀀀 Up to the specified Unicode version
GitHubs issue page is open! You can report any 􏽕errors􀀀, or leave 􏽕suggestions􀀀.
You can help this font to be more versatile. (for more languages, more frameworks) 􏽕Clone􀀀 this repo, make
changes, and make a 􏽕pull request􀀀! I appreciate any and all supports.
<EFBFBD> 􎳌文字化け!􀀀 <20>

160
demotext_unaligned.txt Executable file
View File

@@ -0,0 +1,160 @@
􏿆The bitmap font for game developers who seek good font that has real multilingual support, for free (as in freedom and without cost)􀀀
There are many bitmap fonts on the internet. You care for the multilingual support, but alas! most of them do not support your language, vector fonts take too much time to load, and even then their legibility suffers because screw built-in antialias.
You somehow found a multilingual one, and it makes your text as if they came straight from an old computer terminal, and you say:
“Well, better than nothing… no, its ugly.”
You speak Japanese and wish to support it, but then このクソなfont only good for displaying Japanese, its not even truly multilingual, and their English look uncanny and inconsistent.
Eventually you just mix different fonts together, and the results were always mildly infuriating.
No more suffering. This font has everything you need.
􏾐while􀀀 (isVisible(􏺟BAD_FONTS􀀀)) {
ripAndTear(􏺟BAD_FONTS􀀀).where { 􏺟GUTS􀀀 };
}
How multilingual? Real multilingual!
􏻬Ианҵоуп ақьаад, нусхур аҩырала, ҩ ҽшьаҟакла, иҧшӡоу анапҩырала􀀀
􏻬գրիչս վայր դրի, վեր կացա և պատրաստվում էի, որ քնեմ, երբ հանկարծ դռանս զանգակը հնչեց􀀀
􏻬ՄՇԱԿԻՉ ԿԱՄ ԿԵՆՏՐՈՆԱԿԱՆ ՄՇԱԿԻՉ ՀԱՆԳՈՒՅՑԸ ՀԱՆԴԻՍԱՆՈՒՄ Է ՀԱՄԱԿԱՐԳՉԻ ՍԱՐՔԱՎՈՐՈՒՄՆԵՐԻՑ􀀀
􏻬Zəfər, jaketini də papağını da götür, bu axşam hava çox soyuq olacaq􀀀
􏻬আমি কাঁচ খেতে পারি, তাতে আমার কোনো ক্ষতি হয় না। 􀀀
􏻬󿿁Под южно дърво, цъфтящо в синьо, бягаше малко пухкаво зайче󿿀􀀀
􏻬ᎠᏍᎦᏯᎡᎦᎢᎾᎨᎢᎣᏍᏓᎤᎩᏍᏗᎥᎴᏓᎯᎲᎢᏔᎵᏕᎦᏟᏗᏖᎸᎳᏗᏗᎧᎵᎢᏘᎴᎩ ᏙᏱᏗᏜᏫᏗᏣᏚᎦᏫᏛᏄᏓᎦᏝᏃᎠᎾᏗᎭᏞᎦᎯᎦᏘᏓᏠᎨᏏᏕᏡᎬᏢᏓᏥᏩᏝᎡᎢᎪᎢ ᎠᎦᏂᏗᎮᎢᎫᎩᎬᏩᎴᎢᎠᏆᏅᏛᎫᏊᎾᎥᎠᏁᏙᎲᏐᏈᎵᎤᎩᎸᏓᏭᎷᏤᎢᏏᏉᏯᏌᏊ ᎤᏂᏋᎢᏡᎬᎢᎰᏩᎬᏤᎵᏍᏗᏱᎩᎱᎱᎤᎩᎴᎢᏦᎢᎠᏂᏧᏣᏨᎦᏥᎪᎥᏌᏊᎤᎶᏒᎢᎢᏡᎬᎢ ᎹᎦᎺᎵᏥᎻᎼᏏᎽᏗᏩᏂᎦᏘᎾᎿᎠᏁᎬᎢᏅᎩᎾᏂᎡᎢᏌᎶᎵᏎᎷᎠᏑᏍᏗᏪᎩ ᎠᎴ ᏬᏗᏲᏭᎾᏓᏍᏓᏴᏁᎢᎤᎦᏅᏮᏰᎵᏳᏂᎨᎢ􀀀
􏻬Příliš žluťoučký kůň úpěl ďábelské ódy􀀀
􏻬Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Walther spillede på xylofon􀀀
􏻬PACK MY BOX WITH FIVE DOZEN LIQUOR JUGS􀀀
􏻬hƿæt ƿe ᵹardena inᵹear ꝺaᵹum þeoꝺ cynninᵹa þꞃym ᵹeꝼꞃumon􀀀
􏻬Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich GROẞEN GROẞE􀀀
􏻬ζαφείρι δέξου πάγκαλο, βαθῶν ψυχῆς τὸ σῆμα􀀀
􏻬ΔΙΑΦΥΛΆΞΤΕ ΓΕΝΙΚΆ ΤΗ ΖΩΉ ΣΑΣ ΑΠΌ ΒΑΘΕΙΆ ΨΥΧΙΚΆ ΤΡΑΎΜΑΤΑ􀀀
􏻬სწრაფი ყავისფერი მელა გადაახტა ზარმაც ძაღლს ᲘᲜᲢᲔᲚ ᲞᲔᲜᲢᲘᲣᲛᲘ ᲛᲘᲙᲠᲝᲞᲠᲝᲪᲔᲡᲝᲠᲘ􀀀
􏻬ऋषियों को सताने वाले दुष्ट राक्षसों के राजा रावण का सर्वनाश करने वाले विष्णुवतार भगवान श्रीराम अयोध्या के महाराज दशरथ के􀀀
􏻬Kæmi ný öxi hér, ykist þjófum nú bæði víl og ádrepa􀀀
􏻬Ċuaiġ bé ṁórṡáċ le dlúṫspád fíoꝛḟinn trí hata mo ḋea-ṗoꝛcáin ḃig􀀀
􏻬あめつちほしそら やまかはみねたに くもきりむろこけ ひといぬうへすゑ ゆわさるおふせよ えの𛀁をなれゐて􀀀
􏻬トリナクコヱス ユメサマセ ミヨアケワタル ヒンカシヲ ソライロハエテ オキツヘニ ホフネムレヰヌ モヤノウチ􀀀
􏻬田居に出で 菜摘むわれをぞ 君召すと 求食り追ひゆく 山城の 打酔へる子ら 藻葉干せよ え舟繋けぬ􀀀
􏻬정 참판 양반댁 규수 큰 교자 타고 혼례 치른 날  찦차를 타고 온 펲시맨과 쑛다리 똠방각하􀀀
􏻬쾅 ᄒᆞ는 소리 헨 “아이구 베락 털어져ᇝ인가?” 영 걷어진 쥥은 몰르곡 경헨 ᄇᆞᆰ도록 ᄌᆞᆷ ᄒᆞᆫᄌᆞᆷ들 안 잣수다􀀀
􏻬Četri psihi faķīri vēlu vakarā zāģēja guļbūvei durvis, fonā šņācot mežam􀀀
􏻬Įlinkdama fechtuotojo špaga sublykčiojusi pragręžė apvalų arbūzą􀀀
􏻬Ѕидарски пејзаж: шугав билмез со чудење џвака ќофте и кељ на туѓ цех􀀀
􏻬Щётканы фермд пийшин цувъя. Бөгж зогсч хэльюү􀀀
􏻬Pchnąć w tę łódź jeża lub ośm skrzyń fig􀀀
􏻬Înjurând pițigăiat, zoofobul comandă vexat whisky și tequila􀀀
􏻬Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства􀀀
􏻬अद्वैतामृवर्षिणीं भगवतीमष्टादशाध्यायिनीम् अम्ब त्वामनुसन्दधामि भगवद्गीते भवद्वेषिणीम् ॥􀀀
􏻬󿿂Ајшо, лепото и чежњо, за љубав срца мога дођи у Хаџиће на кафу󿿀􀀀
􏻬ᮙᮛᮔᮨᮂᮔ ᮓᮤᮘᮨᮛᮨ ᮃᮊᮜ᮪ ᮏᮩᮀ ᮠᮒᮨ ᮔᮥᮛᮔᮤ, ᮎᮙ᮪ᮕᮥᮁ-ᮌᮅᮜ᮪ ᮏᮩᮀ ᮞᮞᮙᮔ ᮃᮚ ᮓᮤᮔ ᮞᮥᮙᮔᮨᮒ᮪ ᮓᮥᮓᮥᮜᮥᮛᮔ᮪􀀀
􏻬Jovencillo emponzoñado de whisky: ¡qué figurota exhibe!􀀀
􏻬எழுத்து வடிவங்களுக்கு வார்த்தைகள் மற்றும் வாக்கியங்கள் போலவே தொனி, ஒலி பண்பு, தன்மை உண்டு􀀀
􏻬นายสังฆภัณฑ์ เฮงพิทักษ์ฝั่ง ผู้เฒ่าซึ่งมีอาชีพเป็นฅนขายฃวด ถูกตำรวจปฏิบัติการจับฟ้องศาล ฐานลักนาฬิกาคุณหญิงฉัตรชฎา ฌานสมาธิ􀀀
􏻬Pijamalı hasta yağız şoföre çabucak güvendi􀀀
􏻬Жебракують філософи при ґанку церкви в Гадячі, ще й шатро їхнє п’яне знаємо􀀀
􏻬Do bạch kim rất quý nên sẽ dùng để lắp vô xương􀀀
􏻬日堀油告観観藤村抄海評業庁経賃室弁市。太撮収改売週法所何都慣次現。価紙一無三洋日話転手治稿載末替付致治。􀀀
􏻬[pʰnɣɬɥi.m͡ŋχɫʍɨnaɸ.cθʊɫɯ.ɹɨɫʏ͡ɛx.ɯ͡ɣaxɲaɣɫ.ɸtʰɑɣɴ]􀀀
􏻬⠑⠥⠊⠵⠀⠟⠫⠒⠵⠀⠓⠗⠎⠉⠂⠀⠠⠊⠗⠘⠍⠓⠎⠀⠨⠣⠩⠐⠥⠍⠑⠱⠀⠈⠪⠀⠨⠷⠎⠢⠈⠧⠀⠈⠏⠒⠐⠕⠝⠀⠕⠌⠎⠀⠊⠿⠊⠪⠶⠚⠊􀀀
􏃯Features:􀀀
􏻬Heard of « Guillemets »? You speak „Nederlands” or „Deutsch“? ”suomi”? 「日本語」しゃべる?或《中文》?􀀀
􎳌‣ Multilingual brackets; these are the most basic things, right?􀀀
􏋶99406 91902 59186 29182 45814 57862 34368 08240􀀀
􏋶31485 65379 04348 28952 42927 26157 42615 39526􀀀
􎳌‣ Fixed-width (tabulated) numbers, because number-crunching matters􀀀
􏼀ᚱ􏼐ᛂ􏼠ᚴ􏼰ᛋ􏽀ᛂ􏽐ᛋ􏽠ᛏ􏽰ᛋ􏾀ᚮ􏾐ᚾ􏾠ᛔ􏾰ᚢ􏿀ᛏ􏿐ᛚ􏿠ᚮ􏿰ᛋ􏻰᛬􏷰ᚱ􏳰ᛂ􏯰ᚴ􏫰ᛋ􏧰ᛋ􏣰ᚢ􏟰ᚼ􏛰ᚾ􏗰ᚢ􏓰ᛘ􏏰ᚢ􏋰ᛚ􏇰ᚾ􏃰ᛏ􏃱ᚮ􏃲᛬􏃳ᛏ􏃴ᚮ􏃵ᛋ􏃶ᛁ􏃷ᚮ􏃸ᚵ􏃹ᛂ􏃺ᚢ􏃻ᛏ􏃼ᚮ􏃽ᚱ􏃾ᛘ􏃿ᛔ􏃯ᚱ􏃟ᛂ􏃏ᚴ􏂿ᛋ􏂯ᛏ􏂟᛭􏂏ᛋ􏁿ᚢ􏁯ᚼ􏁟ᚾ􏁏ᚢ􏀿ᛋ􏀯ᛘ􏀟ᚮ􏀏ᛁ􏄏ᚵ􏈏ᚾ􏌏ᛁ􏐏ᛂ􏔏ᛏ􏘏ᚮ􏜏ᛑ􏠏᛭􏤏ᚵ􏨏ᛂ􏬏ᚢ􏰏ᛏ􏴏ᚮ􏸏ᚱ􀀀􀀀
􎳌‣ Colour-code that supports 4096 colours via 4 bit RGB􀀀
􏻬󿿁Щиглецът се яде само пържен в юфка без чушки и хвойна󿿀 (Bulgarian)󿿀􀀀
􏻬󿿂Љубазни фењерџија чађавог лица хоће да ми покаже штос󿿀 (Serbian)󿿀􀀀
􏻬Разъяренный чтец эгоистично бьёт пятью жердями шустрого фехтовальщика (Russian)􀀀
􎳌‣ Control characters to support Bulgarian and Serbian letter shapes on the fly􀀀
􏻬Press  to pay respects—or is it , or …􀀀
􎳌‣ Custom symbols for video games􀀀
􏻬Can you distinguish following dashes: - — - - ――――――――――― 4863 48-63􀀀
􎳌‣ For all those dash-pedants, we have en-dash, em-dash, and even horizontal bars!􀀀
􏻬5¹⁹₃₂ inch is 142.1 mm · (C₂F₄)ₙ is godly · Error = MoreCode²􀀀
􏻬NOTE: we dont do fractions. 142¹₁₀ mm is illegal!􀀀
􎳌‣ Unicode fractions, also known as super/subscripts􀀀
􏻬ᄀᆞᄅᆞᇝ ᄀᆞᅀᅢ 자거늘 밀므리 사ᄋᆞ리로ᄃᆡ 나거ᅀᅡ ᄌᆞᄆᆞ니ᅌᅵ다 셤 안해 자시ᇙ 제 한비 사ᄋᆞ리로ᄃᆡ 뷔어ᅀᅡ ᄌᆞᄆᆞ니ᅌᅵ다􀀀
􏻬쾅 ᄒᆞ는 소리 헨 “아이구, 베락 털어져ᇝ인가?” 영 걷어진 쥥은 몰르곡 경헨 나왕 보고들랑 영헤연 ᄇᆞᆰ도록 ᄌᆞᆷ ᄒᆞᆫᄌᆞᆷ들 안 잣수다 이 시간 동네 사람들.􀀀
􎳌‣ Full support for Old Korean/Jeju dialect orthography􀀀
􏻬𛀆𛄆𛂦𛂌𛂻𛂶𛁻𛁦𛃶𛂑𛃺𛄚𛄋𛀙𛃫𛁟𛄀𛁛𛁫𛂗𛂁𛃭𛃑𛀋𛂜𛀕𛀬𛃟𛃅𛀴𛂱𛀸𛀁𛁲𛀄𛀿𛀣𛃥𛃔𛃎𛁈ゑ𛂯𛃙𛁕𛁏𛄝􀀀
􎳌‣ Full support for Archaic Kana/Hentaigana􀀀
􏃯Supported Unicode Blocks:􀀀
Basic Latin
Latin-1 Supplement
Latin Extended Additional
Latin Extended-A/B/C/D
Armenian
Bengali􏿆ᶠⁱ􀀀
Braille Patterns
Cherokee􏿆⁷􀀀
CJK Symbols and Punctuation
CJK Unified Ideographs􏿆⁶􀀀
CJK Unified Ideographs Extension A􏿆¹²·¹􀀀
Combining Diacritical Marks
Control Pictures
Currency Symbols
Cyrillic􏿆ᴭ􀀀
Cyrillic Supplement􏿆ᴭ􀀀
Devanagari
Enclosed Alphanumeric Supplement
General Punctuations
Georgian􏿆ჼ􀀀
Georgian Extended
Greek and Coptic􏿆ᴱ􀀀
Greek Extended
Halfwidth and Fullwidth Forms
Hangul Compatibility Jamo
Hangul Jamo
Hangul Jamo Extended-A/B
Hangul Syllables
IPA Extensions
Hiragana
Katakana
Katakana Phonetic Extensions
Kana Supplement
Kana Extended-A
Small Kana Extension
Letterlike Symbols
Phonetic Extensions
Phonetic Extensions Supplement
Runic
Spacing Modifier Letters
Sundanese
Sundanese Supplement
Superscripts and Subscripts
Symbols for Legacy Computing
Tamil
Thai
􏿆ᴭ􀀀 No support for archæic letters  􏿆ᴱ􀀀 No support for Coptic
􏿆ᶠⁱ􀀀 No support for ligatures  􏿆ჼ􀀀 Mkhedruli only
􏿆⁶􀀀 􏿆⁷􀀀 􏿆⁹􀀀 􏿆¹²·¹􀀀 Up to the specified Unicode version
GitHubs issue page is open! You can report any 􏽕errors􀀀, or leave 􏽕suggestions􀀀. You can help this font to be more versatile. (for more languages, more frameworks) 􏽕Clone􀀀 this repo, make changes, and make a 􏽕pull request􀀀! I appreciate any and all supports.

BIN
font_drawing_template.png LFS Executable file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

BIN
glyph_height_pos_annotation.png LFS Executable file

Binary file not shown.

550
keming_calculator.html Normal file
View File

@@ -0,0 +1,550 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Keming Machine Tag Calculator</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Segoe UI', system-ui, sans-serif; background: #f5f5f5; color: #222; padding: 24px; min-height: 100vh; }
h1 { font-size: 1.4em; margin-bottom: 4px; color: #111; }
.subtitle { color: #666; font-size: 0.85em; margin-bottom: 20px; }
.main { display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-start; }
.panel { background: #fff; border-radius: 8px; padding: 20px; border: 1px solid #ddd; }
.panel h2 { font-size: 1em; margin-bottom: 12px; color: #1a5fb4; }
.col-left { display: flex; flex-direction: column; gap: 20px; }
/* Lowheight toggle */
.lowheight-section { min-width: 300px; }
.lowheight-row { display: flex; align-items: center; gap: 12px; }
.lowheight-btn {
width: 140px; height: 36px; border: 2px solid #bbb; border-radius: 4px;
background: #f0f0f0; color: #666; font-weight: bold; font-size: 0.9em;
cursor: pointer; transition: all 0.15s; user-select: none;
}
.lowheight-btn:hover { border-color: #888; background: #e8e8e8; }
.lowheight-btn.active { background: #2a5a8a; border-color: #1a4a7a; color: #fff; }
.lowheight-hint { font-size: 0.8em; color: #888; margin-top: 8px; }
/* Shape grid */
.shape-section { min-width: 300px; }
.grid-wrapper { display: flex; gap: 20px; align-items: flex-start; }
.shape-grid { display: grid; grid-template-columns: auto 56px 10px 56px auto; grid-template-rows: repeat(9, auto); align-items: center; gap: 2px 0; }
.zone-btn {
width: 52px; height: 32px; border: 2px solid #bbb; border-radius: 4px;
background: #f0f0f0; color: #666; font-weight: bold; font-size: 0.9em;
cursor: pointer; transition: all 0.15s; display: flex; align-items: center; justify-content: center;
user-select: none;
}
.zone-btn:hover { border-color: #888; background: #e8e8e8; }
.zone-btn.active { background: #2a5a8a; border-color: #1a4a7a; color: #fff; }
.zone-btn.active.wye { background: #7b3f9e; border-color: #5a2d75; }
.grid-label { color: #888; font-size: 0.75em; text-align: center; padding: 0 4px; white-space: nowrap; }
.grid-label-left { text-align: right; }
.grid-label-right { text-align: left; }
.grid-sep { grid-column: 1 / -1; height: 3px; background: #999; margin: 2px 0; border-radius: 1px; }
.grid-dot { text-align: center; color: #ccc; font-size: 0.7em; }
.grid-spacer { height: 12px; }
/* Y toggle */
.y-toggle { margin-top: 16px; }
.y-toggle label { display: flex; align-items: center; gap: 10px; cursor: pointer; font-size: 0.9em; }
.y-toggle input { display: none; }
.toggle-track {
width: 48px; height: 24px; background: #2a5a8a; border-radius: 12px;
position: relative; transition: background 0.2s;
}
.toggle-track::after {
content: ''; position: absolute; top: 2px; left: 2px;
width: 20px; height: 20px; background: #fff; border-radius: 50%;
transition: transform 0.2s; box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
.y-toggle input:checked + .toggle-track { background: #7b3f9e; }
.y-toggle input:checked + .toggle-track::after { transform: translateX(24px); }
.toggle-labels { display: flex; gap: 4px; font-size: 0.8em; }
.toggle-labels span { padding: 2px 6px; border-radius: 3px; }
.toggle-labels .active-label { background: #2a5a8a; color: #fff; }
.toggle-labels .active-label.wye { background: #7b3f9e; }
/* Codepoint input */
.cp-section { min-width: 300px; }
.cp-input-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.cp-input {
width: 180px; height: 34px; border: 2px solid #bbb; border-radius: 4px;
background: #fafafa; padding: 0 8px; font-family: 'Consolas', 'Fira Code', monospace;
font-size: 0.95em; color: #222; outline: none;
}
.cp-input:focus { border-color: #1a5fb4; }
.cp-input.error { border-color: #c00; background: #fff0f0; }
.cp-formats { font-size: 0.75em; color: #888; margin-top: 6px; line-height: 1.5; }
.cp-formats code { background: #eee; padding: 1px 4px; border-radius: 3px; font-family: 'Consolas', monospace; color: #333; }
.cp-resolved { margin-top: 8px; font-size: 0.85em; color: #444; }
.cp-resolved .cp-char { font-size: 1.3em; }
/* Output */
.output-section { min-width: 280px; }
.pixel-row { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; padding: 10px; background: #f8f8f8; border-radius: 6px; border: 1px solid #e0e0e0; }
.colour-swatch {
width: 48px; height: 48px; border-radius: 6px; border: 2px solid #ccc;
flex-shrink: 0; image-rendering: pixelated;
}
.pixel-info { font-size: 0.85em; line-height: 1.6; }
.pixel-info .hex { font-family: 'Consolas', 'Fira Code', monospace; font-size: 1.1em; color: #111; }
.pixel-info .channels { color: #555; }
.pixel-info .channel-r { color: #c00; }
.pixel-info .channel-g { color: #070; }
.pixel-info .channel-b { color: #00c; }
.pixel-label { font-size: 0.8em; color: #1a5fb4; margin-bottom: 4px; font-weight: 600; }
.pixel-inactive { font-size: 0.85em; color: #999; }
.bit-display { font-family: 'Consolas', 'Fira Code', monospace; font-size: 0.8em; color: #777; margin-top: 2px; }
/* Mask display */
.mask-section { margin-top: 16px; padding: 10px; background: #f8f8f8; border-radius: 6px; border: 1px solid #e0e0e0; }
.mask-section .label { font-size: 0.8em; color: #1a5fb4; margin-bottom: 4px; }
.mask-val { font-family: 'Consolas', 'Fira Code', monospace; font-size: 0.95em; color: #111; }
/* Examples */
.examples-section { margin-top: 20px; }
.examples-section h2 { font-size: 1em; margin-bottom: 8px; color: #1a5fb4; }
.example-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 4px; }
.example-item {
font-size: 0.8em; padding: 4px 8px; border-radius: 4px;
background: #f0f0f0; cursor: pointer; transition: background 0.15s;
display: flex; justify-content: space-between; align-items: center;
border: 1px solid #e0e0e0;
}
.example-item:hover { background: #e0ecf8; border-color: #b0c8e8; }
.example-item .ex-code { color: #555; }
.example-item .ex-char { font-size: 1.2em; min-width: 24px; text-align: center; }
/* Notes */
.notes { margin-top: 20px; font-size: 0.8em; color: #666; line-height: 1.5; }
.notes code { background: #eee; padding: 1px 5px; border-radius: 3px; font-family: 'Consolas', monospace; color: #333; }
</style>
</head>
<body>
<h1>Keming Machine Tag Calculator</h1>
<p class="subtitle">Calculate pixel colour values for the three Keming Machine tag pixels (K at Y+6, Y+7, Y+8)</p>
<div class="main">
<div class="col-left">
<!-- Pixel 1: Lowheight -->
<div class="panel lowheight-section">
<h2>Pixel 1 &mdash; Low Height (Y+6)</h2>
<div class="lowheight-row">
<button class="lowheight-btn" id="lowheightBtn" onclick="toggleLowheight()">Low Height: OFF</button>
</div>
<p class="lowheight-hint">
Set for lowercase-height characters (a, b, c, d, e, etc.).<br>
Set if above-diacritics should be lowered.
</p>
</div>
<!-- Pixel 2: Shape Grid -->
<div class="panel shape-section">
<h2>Pixel 2 &mdash; Glyph Shape (Y+7)</h2>
<p style="font-size:0.8em; color:#666; margin-bottom:12px;">Click zones to mark which parts of the glyph are occupied.</p>
<div class="grid-wrapper">
<div class="shape-grid" id="shapeGrid">
<!-- Row: A B (top / ascenders) -->
<span class="grid-label grid-label-left">top</span>
<button class="zone-btn" data-zone="A" onclick="toggleZone(this)">A</button>
<span class="grid-dot">&middot;</span>
<button class="zone-btn" data-zone="B" onclick="toggleZone(this)">B</button>
<span class="grid-label grid-label-right">ascender</span>
<!-- Spacer row -->
<span></span><span class="grid-spacer"></span><span></span><span class="grid-spacer"></span><span></span>
<!-- Row: C D -->
<span class="grid-label grid-label-left">mid</span>
<button class="zone-btn" data-zone="C" onclick="toggleZone(this)">C</button>
<span class="grid-dot">&middot;</span>
<button class="zone-btn" data-zone="D" onclick="toggleZone(this)">D</button>
<span class="grid-label grid-label-right">cap hole</span>
<!-- Row: E F -->
<span class="grid-label grid-label-left"></span>
<button class="zone-btn" data-zone="E" onclick="toggleZone(this)">E</button>
<span class="grid-dot">&middot;</span>
<button class="zone-btn" data-zone="F" onclick="toggleZone(this)">F</button>
<span class="grid-label grid-label-right">lc hole</span>
<!-- Row: G H -->
<span class="grid-label grid-label-left">btm</span>
<button class="zone-btn" data-zone="G" onclick="toggleZone(this)">G</button>
<span class="grid-dot">&middot;</span>
<button class="zone-btn" data-zone="H" onclick="toggleZone(this)">H</button>
<span class="grid-label grid-label-right">baseline</span>
<!-- Baseline separator -->
<div class="grid-sep"></div>
<!-- Spacer row -->
<span></span><span class="grid-spacer"></span><span></span><span class="grid-spacer"></span><span></span>
<!-- Row: J K (below baseline) -->
<span class="grid-label grid-label-left">desc</span>
<button class="zone-btn" data-zone="J" onclick="toggleZone(this)">J</button>
<span class="grid-dot">&middot;</span>
<button class="zone-btn" data-zone="K" onclick="toggleZone(this)">K</button>
<span class="grid-label grid-label-right">descender</span>
</div>
</div>
<!-- Y (Bar/Wye) toggle -->
<div class="y-toggle">
<label>
<input type="checkbox" id="yToggle" onchange="recalc()">
<span class="toggle-track"></span>
<span class="toggle-labels">
<span id="barLabel" class="active-label">Bar (B-type, 2px kern)</span>
<span id="wyeLabel">Wye (Y-type, 1px kern)</span>
</span>
</label>
<p style="font-size:0.75em; color:#888; margin-top:6px; margin-left:58px;">
Set Wye when top/bottom of glyph tapers to a point (V, Y, A, v, etc.)
</p>
</div>
<!-- Kerning mask output -->
<div class="mask-section">
<div class="label">Kerning Mask (24-bit, used by rules)</div>
<div id="maskVal" class="mask-val">0x0000FF</div>
<div id="maskBin" class="bit-display">00000000 00000000 11111111</div>
</div>
</div>
<!-- Pixel 3: Dot Removal -->
<div class="panel cp-section">
<h2>Pixel 3 &mdash; Dot Removal (Y+8)</h2>
<p style="font-size:0.8em; color:#666; margin-bottom:12px;">Replacement character for diacritics dot removal. All 24 bits encode the codepoint.</p>
<div class="cp-input-row">
<input type="text" class="cp-input" id="cpInput" placeholder="e.g. U+0041, 65, A" oninput="updateCodepoint()">
</div>
<p class="cp-formats">
Accepts: <code>U+0041</code> or <code>0x41</code> (hex), <code>65</code> (decimal), or a literal character <code>A</code>
</p>
<div class="cp-resolved" id="cpResolved"></div>
</div>
</div><!-- col-left -->
<!-- Output -->
<div class="panel output-section">
<h2>Pixel Colour Values</h2>
<div class="pixel-label">Pixel 1: Low Height (Y+6)</div>
<div class="pixel-row">
<canvas id="swatch1" class="colour-swatch" width="48" height="48"></canvas>
<div class="pixel-info">
<div class="hex" id="hex1">&mdash;</div>
<div id="p1desc" class="pixel-inactive">No pixel (not lowheight)</div>
</div>
</div>
<div class="pixel-label" style="margin-top:12px;">Pixel 2: Glyph Shape (Y+7)</div>
<div class="pixel-row">
<canvas id="swatch2" class="colour-swatch" width="48" height="48"></canvas>
<div class="pixel-info">
<div class="hex" id="hex2">#000000</div>
<div class="channels">
R: <span class="channel-r" id="r2">0</span> &nbsp;
G: <span class="channel-g" id="g2">0</span> &nbsp;
B: <span class="channel-b" id="b2">0</span>
</div>
<div class="bit-display" id="bits2">00000000 00000000 00000000</div>
</div>
</div>
<div class="pixel-label" style="margin-top:12px;">Pixel 3: Dot Removal (Y+8)</div>
<div class="pixel-row">
<canvas id="swatch3" class="colour-swatch" width="48" height="48"></canvas>
<div class="pixel-info">
<div class="hex" id="hex3">&mdash;</div>
<div class="channels" id="p3channels" style="display:none">
R: <span class="channel-r" id="r3">0</span> &nbsp;
G: <span class="channel-g" id="g3">0</span> &nbsp;
B: <span class="channel-b" id="b3">0</span>
</div>
<div id="p3desc" class="pixel-inactive">No replacement character set</div>
</div>
</div>
<div class="notes" style="margin-top:16px;">
<strong>Alpha channel:</strong> must be non-zero (1&ndash;254) for the pixel to be read as a tag.
Set alpha to <code>1</code> (or any value &lt; 255 and &gt; 0).<br>
A fully transparent pixel (alpha = 0) means &ldquo;no data&rdquo;.
</div>
</div>
</div>
<!-- Examples -->
<div class="panel examples-section" style="margin-top: 20px;">
<h2>Examples &mdash; Glyph Shape (click to load)</h2>
<div class="example-grid" id="exampleGrid"></div>
</div>
<script>
const ZONES = ['A','B','C','D','E','F','G','H','J','K'];
const state = { A:0, B:0, C:0, D:0, E:0, F:0, G:0, H:0, J:0, K:0 };
let isLowheight = false;
// Bit positions within kerning_mask (24-bit RGB):
// Blue byte: A=bit7, B=bit6, C=bit5, D=bit4, E=bit3, F=bit2, G=bit1, H=bit0
// Green byte: J=bit15(=green bit7), K=bit14(=green bit6)
// Red byte: Y=bit23(=red bit7) -- tracked separately as isKernYtype
const BIT_POS = { A:7, B:6, C:5, D:4, E:3, F:2, G:1, H:0, J:15, K:14 };
function toggleLowheight() {
isLowheight = !isLowheight;
const btn = document.getElementById('lowheightBtn');
btn.classList.toggle('active', isLowheight);
btn.textContent = isLowheight ? 'Lowheight: ON' : 'Lowheight: OFF';
recalc();
}
function toggleZone(btn) {
const zone = btn.dataset.zone;
state[zone] = state[zone] ? 0 : 1;
btn.classList.toggle('active', !!state[zone]);
recalc();
}
function recalc() {
const isWye = document.getElementById('yToggle').checked;
// Update button styling for wye mode
document.querySelectorAll('.zone-btn.active').forEach(btn => {
btn.classList.toggle('wye', isWye);
});
// Update toggle labels
const barLabel = document.getElementById('barLabel');
const wyeLabel = document.getElementById('wyeLabel');
barLabel.className = isWye ? '' : 'active-label';
wyeLabel.className = isWye ? 'active-label wye' : '';
// --- Pixel 1: Lowheight ---
if (isLowheight) {
// Any non-zero pixel; use white with alpha=1 for visibility in editors
drawSwatchSolid('swatch1', 255, 255, 255);
document.getElementById('hex1').textContent = '#FFFFFF';
document.getElementById('p1desc').textContent = 'Any pixel with alpha > 0';
document.getElementById('p1desc').className = 'channels';
} else {
drawSwatchEmpty('swatch1');
document.getElementById('hex1').innerHTML = '&mdash;';
document.getElementById('p1desc').textContent = 'No pixel (not lowheight)';
document.getElementById('p1desc').className = 'pixel-inactive';
}
// --- Pixel 2: Shape Data ---
// Red: Y bit in MSB (bit 7)
const r = isWye ? 0x80 : 0x00;
// Green: J in bit 7, K in bit 6
const g = (state.J ? 0x80 : 0) | (state.K ? 0x40 : 0);
// Blue: ABCDEFGH
const b = (state.A ? 0x80 : 0) | (state.B ? 0x40 : 0) |
(state.C ? 0x20 : 0) | (state.D ? 0x10 : 0) |
(state.E ? 0x08 : 0) | (state.F ? 0x04 : 0) |
(state.G ? 0x02 : 0) | (state.H ? 0x01 : 0);
// Full 24-bit mask (same as what code extracts)
const fullMask = (r << 16) | (g << 8) | b;
document.getElementById('hex2').textContent = '#' + hex2(r) + hex2(g) + hex2(b);
document.getElementById('r2').textContent = r;
document.getElementById('g2').textContent = g;
document.getElementById('b2').textContent = b;
document.getElementById('bits2').textContent = bin8(r) + ' ' + bin8(g) + ' ' + bin8(b);
document.getElementById('maskVal').textContent = '0x' + fullMask.toString(16).toUpperCase().padStart(6, '0');
document.getElementById('maskBin').textContent = bin8((fullMask >> 16) & 0xFF) + ' ' + bin8((fullMask >> 8) & 0xFF) + ' ' + bin8(fullMask & 0xFF);
drawSwatchSolid('swatch2', r, g, b);
}
function updateCodepoint() {
const input = document.getElementById('cpInput');
const raw = input.value.trim();
if (raw === '') {
input.classList.remove('error');
drawSwatchEmpty('swatch3');
document.getElementById('hex3').innerHTML = '&mdash;';
document.getElementById('p3channels').style.display = 'none';
document.getElementById('p3desc').textContent = 'No replacement character set';
document.getElementById('p3desc').style.display = '';
document.getElementById('cpResolved').textContent = '';
return;
}
const cp = parseCodepoint(raw);
if (cp === null || cp < 0 || cp > 0xFFFFFF) {
input.classList.add('error');
drawSwatchEmpty('swatch3');
document.getElementById('hex3').innerHTML = '&mdash;';
document.getElementById('p3channels').style.display = 'none';
document.getElementById('p3desc').textContent = cp !== null ? 'Codepoint out of 24-bit range' : 'Invalid input';
document.getElementById('p3desc').style.display = '';
document.getElementById('cpResolved').textContent = '';
return;
}
input.classList.remove('error');
const r3 = (cp >> 16) & 0xFF;
const g3 = (cp >> 8) & 0xFF;
const b3 = cp & 0xFF;
document.getElementById('hex3').textContent = '#' + hex2(r3) + hex2(g3) + hex2(b3);
document.getElementById('r3').textContent = r3;
document.getElementById('g3').textContent = g3;
document.getElementById('b3').textContent = b3;
document.getElementById('p3channels').style.display = '';
document.getElementById('p3desc').style.display = 'none';
drawSwatchSolid('swatch3', r3, g3, b3);
// Show resolved character
let charDisplay = '';
try { charDisplay = String.fromCodePoint(cp); } catch(e) {}
document.getElementById('cpResolved').innerHTML =
'U+' + cp.toString(16).toUpperCase().padStart(4, '0') +
' (decimal ' + cp + ')' +
(charDisplay ? ' &mdash; <span class="cp-char">' + escapeHtml(charDisplay) + '</span>' : '');
}
function parseCodepoint(s) {
// U+XXXX or u+XXXX
if (/^[Uu]\+([0-9A-Fa-f]+)$/.test(s)) {
return parseInt(RegExp.$1, 16);
}
// 0xXXXX
if (/^0[xX]([0-9A-Fa-f]+)$/.test(s)) {
return parseInt(RegExp.$1, 16);
}
// Pure decimal number
if (/^[0-9]+$/.test(s)) {
return parseInt(s, 10);
}
// Literal character (single grapheme — could be a surrogate pair)
const codepoints = [...s];
if (codepoints.length === 1) {
return codepoints[0].codePointAt(0);
}
return null;
}
function escapeHtml(s) {
const d = document.createElement('span');
d.textContent = s;
return d.innerHTML;
}
function drawSwatchSolid(id, r, g, b) {
const canvas = document.getElementById(id);
const ctx = canvas.getContext('2d');
// Chequerboard background
ctx.fillStyle = '#ddd';
ctx.fillRect(0, 0, 48, 48);
ctx.fillStyle = '#fff';
for (let y = 0; y < 48; y += 8) {
for (let x = (y % 16 === 0) ? 8 : 0; x < 48; x += 16) {
ctx.fillRect(x, y, 8, 8);
}
}
// Colour
ctx.fillStyle = `rgb(${r},${g},${b})`;
ctx.fillRect(4, 4, 40, 40);
}
function drawSwatchEmpty(id) {
const canvas = document.getElementById(id);
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#ddd';
ctx.fillRect(0, 0, 48, 48);
ctx.fillStyle = '#fff';
for (let y = 0; y < 48; y += 8) {
for (let x = (y % 16 === 0) ? 8 : 0; x < 48; x += 16) {
ctx.fillRect(x, y, 8, 8);
}
}
// Dash to indicate empty
ctx.fillStyle = '#aaa';
ctx.font = '20px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('\u2014', 24, 24);
}
function hex2(v) { return v.toString(16).toUpperCase().padStart(2, '0'); }
function bin8(v) { return v.toString(2).padStart(8, '0'); }
// Load a preset (shape grid only)
function loadPreset(zones, wye) {
for (const z of ZONES) state[z] = 0;
for (const z of zones) state[z] = 1;
document.getElementById('yToggle').checked = wye;
document.querySelectorAll('.zone-btn').forEach(btn => {
btn.classList.toggle('active', !!state[btn.dataset.zone]);
});
recalc();
}
// Examples from keming_machine.txt
const EXAMPLES = [
{ zones: 'AB', wye: false, chars: 'T', desc: 'AB(B)' },
{ zones: 'ABCEGH', wye: false, chars: 'C', desc: 'ABCEGH(B)' },
{ zones: 'ABCEFGH', wye: true, chars: 'K', desc: 'ABCEFGH(Y)' },
{ zones: 'ABCDEG', wye: false, chars: 'P', desc: 'ABCDEG' },
{ zones: 'ABCDEFGH', wye: false, chars: 'B,D,O', desc: 'ABCDEFGH' },
{ zones: 'ABCDFH', wye: false, chars: '\u0427', desc: 'ABCDFH' },
{ zones: 'ABCEG', wye: false, chars: '\u0413', desc: 'ABCEG' },
{ zones: 'ABGH', wye: false, chars: '\u13C6', desc: 'ABGH' },
{ zones: 'ACDEG', wye: false, chars: '\u13B0', desc: 'ACDEG' },
{ zones: 'ACDEFGH', wye: false, chars: 'h,\u0184', desc: 'ACDEFGH' },
{ zones: 'ACDFH', wye: false, chars: '\u07C6', desc: 'ACDFH' },
{ zones: 'ACEGH', wye: false, chars: 'L', desc: 'ACEGH' },
{ zones: 'AH', wye: true, chars: '\\', desc: 'AH(Y)' },
{ zones: 'BDEFGH', wye: false, chars: 'J', desc: 'BDEFGH' },
{ zones: 'BDFGH', wye: false, chars: '\u027A', desc: 'BDFGH' },
{ zones: 'BG', wye: true, chars: '/', desc: 'BG(Y)' },
{ zones: 'CD', wye: false, chars: '\u10B5', desc: 'CD' },
{ zones: 'CDEF', wye: true, chars: '\u03A6', desc: 'CDEF(Y)' },
{ zones: 'CDEFGH', wye: false, chars: 'a,c,e', desc: 'CDEFGH' },
{ zones: 'CDEFGHJK', wye: false, chars: 'g', desc: 'CDEFGHJK' },
{ zones: 'CDEFGHK', wye: false, chars: '\u019E', desc: 'CDEFGHK' },
{ zones: 'AB', wye: true, chars: 'Y', desc: 'AB(Y)' },
{ zones: 'ABCD', wye: true, chars: 'V', desc: 'ABCD(Y)' },
{ zones: 'CDEF', wye: true, chars: 'v', desc: 'CDEF(Y)' },
{ zones: 'EFGH', wye: true, chars: '\u028C', desc: 'EFGH(Y)' },
{ zones: 'CDEFGH', wye: true, chars: 'A', desc: 'CDEFGH(Y)' },
];
function buildExamples() {
const grid = document.getElementById('exampleGrid');
for (const ex of EXAMPLES) {
const div = document.createElement('div');
div.className = 'example-item';
div.innerHTML = `<span class="ex-code">${ex.desc}</span> <span class="ex-char">${ex.chars}</span>`;
div.onclick = () => loadPreset(ex.zones.split(''), ex.wye);
grid.appendChild(div);
}
}
// Init
buildExamples();
drawSwatchEmpty('swatch1');
drawSwatchEmpty('swatch3');
recalc();
</script>
</body>
</html>

108
keming_machine.txt Normal file
View File

@@ -0,0 +1,108 @@
--- Pixel 0
- Lowheight bit
- encoding: has pixel - it's low height
- used by the diacritics system to quickly look up if the character is low height without parsing the Pixel 1
### Legends
#
# A·B < unset for lowheight miniscules, as in e
# |·| < space we don't care
# C·D < middle hole for majuscules, as in C
# E·F < middle hole for miniscules, as in c
# G·H
# ――― < baseline
# |·|
# J·K
--- Pixel 1
- A..K Occupied (1024)
- Is ABGH are all Ys instead of Bars? (2)
- Say, A is Bar but E is wye (e.g. Ꮨ), this condition is false; this character must be encoded as ABDFGH(B).
- encoding:
- <MSB> Y0000000 JK000000 ABCDEFGH <LSB>
- Y: Bar/Wye Mode
- A..K: arguments
- B-type will contract the space by 2 pixels, while Y-type will do it by 1
# Capital/lower itself is given using the pixel 0 due to the diacritics processing
--- Examples
- AB(B): T
- ABCEGH(B): C
- ABCEFGH(Y): K
- ABCDEG: Ꮅ
- ABCDEFGH: B,D,O
- ABCDFH: Ч
- ABCEG: Г
- ABGH: Ꮖ
- ACDEG: Ꮀ
- ACDEFGH: h,Ƅ
- ACDFH: ߆
- ACEGH: L
- AH(Y): \
- BDEFGH: J
- BDFGH: ɺ,ป
- BG(Y): /
- CD: Ⴕ
- CDEF(Y): Φ
- CDEFGH: a,c,e,i,o,φ,ϕ
- CDEFGHJK: g
- CDEFGHK: ƞ
- AB(Y): Y
- ABCD(Y): V
- CDEF(Y): v
- EFGH(Y): ʌ
- CDGH(Y): A
--- Rules
# Legend: _ dont care
# @ must have a bit set
# ` must have a bit unset
- ͟A͟B͟C͟D͟E͟F͟G͟H͟J͟K͟ ͟ ͟ ͟A͟B͟C͟D͟E͟F͟G͟H͟J͟K͟
- _@_`___`__ — `_________ # Γe,TJ ; Ye,YJ,Ve,VJ,TA,ΓA,VA,Vʌ,YA,Yʌ,yA,yʌ,/a,/d
- _@_@___`__ — `___`_@___ # Pɺ but NOT Po,PJ
- _@_@___`__ — `___@_____ # Fo,PJ (always 1 px)
- ___`_`____ — `___@_`___ # Cꟶ,Kꟶ,Lꟶ,Γꟶ
- ___`_`____ — `_@___`___ # CꟵ,KꟵ,LꟵ,ΓꟵ
-----------------------------------------------------
- _`________ — @_`___`___ # eꞀ,LT ; eY,LY,eV,LV,AT,AꞀ,AY,Ay,λY,λy,a\,b\
- _`___`_@__ — @_@___`___ # Lꟼ but NOT oꟼ,bꟼ
- _`___@____ — @_@___`___ # oꟼ,bꟼ (always 1 px)
- _`___@_`__ — __`_`_____ # ⱶƆ,ⱶJ
- _`_@___`__ — __`_`_____ # ⱵƆ,ⱵJ
--- Implementation
code: |
val posTable = intArrayOf(7,6,5,4,3,2,1,0,9,8)
class RuleMask(s: String) {
private var careBits = 0
private var ruleBits = 0
init {
s.forEachIndexed { index, char ->
when (char) {
'@' -> {
careBits = careBits or (1 shl posTable[index])
ruleBits = ruleBits or (1 shl posTable[index])
}
'`' -> {
careBits = careBits or (1 shl posTable[index])
}
}
}
}
fun matches(shapeBits: Int) = ((shapeBits and careBits) and ruleBits) == 0
}
--- Pixel 2
dot removal for diacritics:
- All 24 bits are used to put replacement character
- encoding:
- <MSB> RRRRRRRR GGGGGGGG BBBBBBBB <LSB>

2
samples/README.md Normal file → Executable file
View File

@@ -1,3 +1,3 @@
Following is a rendering of various versions of Wikipedia article "x86". The particular article was chosen because it contains good mixture of English and numbers.
![Sample Article Wikipedia x86](https://github.com/minjaesong/Terrarum-sans-bitmap/blob/master/samples/wikipedia_x86.png)
![Sample Article Wikipedia x86](wikipedia_x86.png)

BIN
samples/wikipedia_x86.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 130 B

BIN
src/assets/ascii_variable.tga LFS Executable file

Binary file not shown.

BIN
src/assets/bengali_variable.tga LFS Executable file

Binary file not shown.

BIN
src/assets/braille_variable.tga LFS Normal file

Binary file not shown.

BIN
src/assets/cjkpunct_variable.tga LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/assets/cyrilic_serbian_variable.tga LFS Executable file

Binary file not shown.

BIN
src/assets/cyrilic_variable.tga LFS Executable file

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More