├── .gitignore ├── LICENSE ├── README.md ├── doc └── screen001.png ├── hershey ├── LibreBaskerville.hf.txt ├── TimesNewRoman.hf.txt ├── Ubuntu.hf.txt └── UbuntuMono-B.hf.txt ├── index.html ├── truetype ├── __init__.py ├── glyphcurves.py ├── truetype.py └── ttfparser.py ├── ttf └── Ubuntu.ttf └── ttf2hershey.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | wheels/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | MANIFEST 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *.cover 46 | .hypothesis/ 47 | .pytest_cache/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | db.sqlite3 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # SageMath parsed files 81 | *.sage.py 82 | 83 | # Environments 84 | .env 85 | .venv 86 | env/ 87 | venv/ 88 | ENV/ 89 | env.bak/ 90 | venv.bak/ 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | .spyproject 95 | 96 | # Rope project settings 97 | .ropeproject 98 | 99 | # mkdocs documentation 100 | /site 101 | 102 | # mypy 103 | .mypy_cache/ 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lingdong Huang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ttf2hershey 3 | 4 | Convert True Type Fonts (.ttf) to Hershey vector fonts 5 | ([Live Demo](https://lingdong-.github.io/ttf2hershey/)) 6 | 7 | > The Hershey fonts are a collection of vector fonts developed c. 1967 by Dr. Allen Vincent Hershey at the Naval Weapons Laboratory, originally designed to be rendered using vectors on early cathode ray tube displays. The fonts are publicly available and have few usage restrictions. Vector fonts are easily scaled and rotated in two or three dimensions; consequently the Hershey fonts have been widely used in computer graphics, computer-aided design programs, and more recently also in computer-aided manufacturing applications like laser engraving. ([Wikipedia](https://en.wikipedia.org/wiki/Hershey_fonts)) 8 | 9 | 10 | This tool converts the common True Type Font (.ttf) files to Hershey font format. A few pre-converted samples can be found in the `./hershey` folder. 11 | 12 | ![](doc/screen001.png) 13 | 14 | ## Dependencies 15 | - Python 2 16 | 17 | ## Usage 18 | 19 | ### Quick Start 20 | 21 | First `cd` into the directory, then 22 | 23 | ``` 24 | python ttf2hershey.py path/to/font.ttf > path/to/output.hf.txt 25 | ``` 26 | Done! you can find the generated hershey font at `path/to/output.hf.txt`. 27 | 28 | ### Use as Module 29 | 30 | ```python 31 | from ttf2hershey import * 32 | output_str = tohershey("ABCDEFG0123",font_path="font.ttf",kern=2) 33 | print output_str 34 | ``` 35 | - First argument: the set of characters to be encoded. This can be the 95 printable ASCII characters, or other unicode characters as you like, as long as the font has them. 36 | - `font_path`: this is the path to your ttf file to be converted 37 | - `kern`: this is the amount of extra spacing to the left and right of a character. At `kern=0`, all characters are squeezed together. However the more kern you put, the less accurate the vertices are, since there're only 95 possible values for a coordinate in a Hershey font. 38 | 39 | ### Character Mapping 40 | 41 | The original Hershey fonts are indexed differently from unicode, so there're tables for mapping characters to Hershey font's special index. In this implementation however, the index is exactly the same as unicode entry point, so no mapping is required. 42 | 43 | ### Parsing & Rendering 44 | 45 | An example Hershey font parser & renderer in javascript and [p5.js](http://p5js.org) can be found at [LingDong-/p5-hershey-js](https://github.com/LingDong-/p5-hershey-js), which is used to drive this [Live Demo](https://lingdong-.github.io/ttf2hershey/). 46 | 47 | 48 | ## Known Issues 49 | 50 | - There're multiple ways to encode `cmap` (which maps unicode characters to glyph indices) in a TTF file. Only parsing of the most common is implemented (PlatformID = 0, Format = 4). 51 | 52 | 53 | ## Resources 54 | 55 | - http://paulbourke.net/dataformats/hershey/ 56 | - https://docs.microsoft.com/en-us/typography/opentype/spec 57 | - https://developer.apple.com/fonts/TrueType-Reference-Manual 58 | - http://stevehanov.ca/blog/index.php?id=143 -------------------------------------------------------------------------------- /doc/screen001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/ttf2hershey/9877eba3735a7bdfec230fe42ce4bcb8154ae67d/doc/screen001.png -------------------------------------------------------------------------------- /hershey/LibreBaskerville.hf.txt: -------------------------------------------------------------------------------- 1 | 32 2LX 2 | 33 74OUQJQFPAP?P>OT?TASFSJQJ ROOONQLRLSLUNUOUQSRRRQROQOO 3 | 34 76LXNQ6P6N6M8L9L: 6 | 37156E_HRER\4_4HR RL3M3O4Q6R8R:R;Q=O?M@L@K@J?H=G;G:G8H6J4K3L3 RL>M>OL> RXFYFZG\I]K]L]N\PZQYRXRWRUQSPSNSLSKSIUGWFXF RXQYQZOZLZIYGXGWGUIULUOWQXQ 7 | 38174AcO?P>R;R:R8P6O6L6L:L;M>O? RLDJEHIHKHMJPLPMPQNSMLD RVIWHYFZD[C[B[BYAXAX?b?bAaA_A^B]C]CXKYM]O_O`OaObNcNbP_R]R\RZRXQVOTNRPMRJRHREQCOAMAKAIFDJBI@H=H;H9I7K4N3O3Q3S4U6V8V9V;S?PAVI 8 | 39 38PTQQCQEQGRLTRVVXXWZQULKLEL@Q6V1W2 10 | 41 40LXN1S6X@XEXKSUMZLXNVPRRLSGSESCR>Q:N5M2N1 11 | 42190JZS=S?S@TATATBTCSDRDQDPCPBPAPAQ@Q>Q=P>O?N@MALALAJ@J?J?K=L=L=M=N=OZ?Z@XAXAWAV@U?T>S= 12 | 43 28H\QOQFHFHCQCQ;S;SC\C\FSFSOQO 13 | 44 42OUOVQUSSSRRRRRQRQROQOOONQLRLTLUNUPURSVPXOV 14 | 45 12LXLHLEXEXHLH 15 | 46 28OUOOONQLRLSLUNUOUQSRRRQROQOO 16 | 47 12JZLRJRX4Z4LR 17 | 48 88E_R3U3Y6]:_@_C_F]LYPURRRORKPGLEFECE@G:K6O3R3 RRPSPVOWLXGXCX?W:V7S6R6P6N7M:L?LCLGMLNOPPRP 18 | 49 58KYULUMUOVPXPYPYRKRKPLPNPOOOMOLO>O;O8O8O7M7K7K5L5N5Q4T4U3UL 19 | 50104G]NMVMWMYMZL[J[I]I\RGRGPLLRFUBV>VZCWGQKNMNM 20 | 51170G]WJWFTCRCQCPDODNDMCMBMANAOAPAQARATAV>V;V8T5R5P5N7N9N:O;P;PZ@WAVBVBXBZD\F]H]J]L[OXQTRRRORKQIOGMGKGJHHIGKFLFNFOGPIOIMJMLMNOPQPTPWMWJ 21 | 52 82F^FGT3X3XGYG[G\F]E]D^D^M]M]L\K[JYJXJXKXLXNYP[P\P\RNRNPPPQPRNRLRKRJFJFG RR;R;IGRGR; 22 | 53124G]O?O?P?R>S>U>Y?[B]E]G]J[NWQRRPRNRKRIPGNGMGLIJJJKJKKLMLNMOOPPPQPTOULVIVFVCU@S@Q@OBNDNDMELEKEKDKDKDP3Q4S4U4W4[4\3\3[6W8U8Q8O? 23 | 54122G]]H]J\NYQTRRRPRKPHMGHGEGAI;M6R3T3V3Y4[5\7\8\9[:Y:Y:X9W8V7V6T5S5R5P7N:M>MAMAO?Q>S>U>Y?\B]F]H RMGMLPPRPUPWLWHWDU@S@Q@NBMEMG 24 | 55 86H\P8O8M9K9K:J;JH;H8K6L4P3R3T3W4X5Z6[9[:[T@ 26 | 57122G]G>G]A]E[KWPRRPRNRKRIQHOHNHMILKLKLLLMMNONPPPQPRPTOVLWHWEWEVGSHQHOHKFHDG@G> RW?W:T5R5O5M:M>MBOFQFSFVCWAW? 27 | 58 56OUOOONQLRLSLUNUOUQSRRRQROQOO ROCOBQ@R@S@UBUCUDSFRFQFODOC 28 | 59 70OUOVQUSSSRRRRRQRQROQOOONQLRLTLUNUPURSVPXOV ROCOBQ@R@T@UBUCUDTFRFQFODOC 29 | 60 18I[[L[OIFID[:[=KE[L 30 | 61 24I[[AIAI?[?[A R[HIHIF[F[H 31 | 62 18I[YEI=I:[D[FIOILYE 32 | 63 88LXOIO?P?U?UeAeDcI`M]OZOXOVMVJVJTMQOOONOKNILHIHHHFJBM?R=T=V=X>XAXAY= RT?S?Q@OCNGNINMPMQMTKVHWDWCWAV?T? 34 | 65 80AcUIJIIMIOIPJPLPLRARAPBPDPEOENFMP3S3\J]L^O`PaPcPcRTRTPVPWPWOWNWLVKUI RKFUFP;P;KF 35 | 66124D`DRDPEPGPHOHMHLH:H8H7G6F5E5E4Q4W4^8^;^@WBWBYB\C_E`H`J`L^O]PYRSRDR RSPYPYJYFWEVDRCPCNCNKNNPPSP RPARAT@U@W>W]>\:X6T6R6N8L;K@KCKIPPTPXP]L_G`G`R^R^OZRSRPRJPFLDGDCD@F:J6P3T3V3Z5]6^4 37 | 68 88BbBRBPCPDPEOFMFLF:F8E7E6C5B5B4Q4U4[6`:b@bCbG`L\PVRRRBR RL6K6K7KKKMNPQPVP\I\C\@Z;X8T6Q6L6 38 | 69110D`NAQATAV@V>VX=Y=YIXIWHWEUDRDODOL 40 | 71 96BbQRNRHPDLBGBCB@D:H6N3R3T3Y5[6\4]4]>[>[:V6R6N6I=ICIINPRPVPYLYIYHXFUFUDbDbF`F_G_J_O^O]O[OZPYQVRQR 41 | 72124Ac_L_M_O`PbPcPcRURUPVPXPYOYMYLYDKDKLKMKOLPNPOPORARAPBPDPEOEMELE:E8E7D6B5A5A4O4O5M5L6K7K9K:KAYAY:Y8Y7X6W5U5U4c4c5b5`6_7_9_:_L 42 | 73 60KYYRKRKPLPNPOOOMOKO;O9O7N6M5K5K4Y4Y5W5V6U7U9U;UKUMUOVPXPYPYR 43 | 74 70G]S:S8S7R6Q5O5O4]4]5[5Z6Y7Y9Y:YRYWT\O\K\GZGXGWIVJVKVLVMXMYOZPZQZSVSSS: 44 | 75126BbLLLMLOMPOPPPPRBRBPCPEPFOFMFLF:F8F7E6D5C5C4P4P5O5M6L7L9L:LCV8W7W6W6V5T5T4_4_5^5]6\6Z8Y9R@]N^O`PbPbRSRSPUPVPVOVOVOVNUNUMNDLFLL 45 | 76 72D`_RDRDPEPGPHOHMHLH:H8H7G6F5E5E4R4R5Q5O6N7N9N:NKNLONPOSPUPWP[O]N^K^I`I_R 46 | 77108=gRK[4g4g5f5d6c7c9c;cJcLcOdPfPgPgRYRYPZP\P]O]M]K]8]8RRORD8D8DJDLEOFPGPIPIR=R=P>P@PAOAMAKA;A9A7@6>5=5=4I4RKRK 47 | 78 88AcZRH:H:HJHLHOIPKPLPLRARAPBPDPEOEMEKE;E9E7D6B5A5A4L4\J\J\;\9\7[6Y5X5X4c4c5b5`6_7_9_;_RZR 48 | 79 88AcR3V3\6`:c@cCcF`L\PURRRORHPDLAFACA@D:H6N3R3 RRPTPXN[K\F\C\@[;X8T6R6P6L8I;H@HCHFIKLNPPRP 49 | 80 88E__<_@XEREOEOLOMOOPPSPUPUREREPFPHPIOIMILI:I8I7H6G5E5E4S4Y4_8_< ROBQBUBY?YZKRHR?;>9=7<6:59594H4H5F5D6D7D7D8E9E:E;KJQ9Q8P7O6N5M5M4[4[5Y5W6W7W8X:X;]J 57 | 88134Ac^N_OaPcPcRTRTPUPVPWPWPWOWOWOWNVMULQFNIMJLLKNJOJOJPKPMPMRARAPBPDPEPFOGNOCF8E6C5B5B4P4P5O5N6N7N8O9S?X9Y8Y7Y7Y6Y6X5W5V5V4b4b5`5^6^6UA^N 58 | 89100BbG9F8E6D6C5B5B4P4P5N5M6M7M7M8N9SDSDX;Z9[7[6[6Y5X5X4b4b5a5_6^7\:[=UFUJULUOVPXPYPYRKRKPLPNPOOOMOKOGG9 59 | 90 44Ca`RCRCQW6M6J6G9GS=U=W=Z>]A_E_H_J]NZQVRTRRRNQMOJRIRI;I7H6H5H5G5F4E4E3N1N@ 68 | 99 82H\[OZPVRSRQRMQJNHJHHHEJAM>Q=T=U=X=[?\@\A\CZDYDXDWCVBVAU@T?T?S?Q?NCNGNKQPTPVPYNZM[O 69 | 100122F^WOWOTRPRNRJQGNFJFHFEGAJ>M=P=Q=U>V?V?VQ=S=W=\A\E\F\F[FNFNGNHNKQPTPVPYN[M\N RTDVDWDWCWAU?S?Q?OBNDTD 71 | 102 94H\H=L=L;L9N5P3T1V1W1Z2[3\4\5\6[7Z7Y7X6W5W4U4U4S4R7R;R=X=X?R?RKRMROSPUPVPVRIRIPJPKPLOLMLLL?H?H= 72 | 103196F^NNONQNRNTNUOXO[S[U[YV\P\K\FYFWFUHSJSKSMSMTLTKVKWKXNZQZVZVVVUUSSSLRHQHNHLJJLJLJKJIHHGHEHDHBI@K>N=P=Q=U>V?W>X=Y=Z=[=\=^>^?^@]A\A\A[AZ@Y?Y?X?W@XAXCXDXEWHUJRKPKNKLLLMLNNN RPIQISFSDSAQ?P?O?MAMDMFOIPI 73 | 104120E_MLMMNONPPPQPQREREPFPGPHOHMHLHR=T=X=Z?[@\D\E\L\M\O]P^P_P_RSRSPTPVPVOWMWLWGWCU?S?P?OANBMEMFML 74 | 105 78LXULUMUOVPWPXPXRLRLPMPNPOOOMOLOGOEOBOAO@N@L@L>U=UL RO4O3Q1R1S1U3U4U6S7R7Q7O6O4 75 | 106 86JZZRZWU\P\M\JZJXJWKVLVMVNXOYPZQZSZTXTUTGTDTBTAT@R@Q@Q>Z=ZR RT4T3V1W1X1Z3Z4Z6X7W7V7T6T4 76 | 107108E_SPTPUPUPUPTNSMPHNJNLNMNOOPPPQPQREREPFPHPHOIMILI;I9I6H5F4E4E3N1NGRDTBV@V?V?T?R?R=]=]?[?Y@XATE[N\O]P_P_RSRSP 77 | 108 50LXULUMUOVPWPXPXRLRLPMPNPOOOMOLOP@P@OAMALAGAEAB@A@A@@?@>@=@=>E=E@E@G>J=M=O=S?S@T@U?Y=[=]=a>b?c@dCdEdLdMdOdPfPgPgR[R[P\P]P^O^M^L^G^E^B]@[?Z?X?VAVBUDUFULUMUOVPWPXPXRLRLPMPNPOOOMOLOGOEOBN@L?K?I?HAGBGCFEFFFL 79 | 110128D`NLNMNONPPPQPQREREPFPGPHOHMHLHGHEHBHAHAG@G@E@D@D>L=M@M@N?R=T=V=Y>[@[A\D\F\L\M\O]P^P`P`RSRSPTPVPWOWMWLWGWEVBU@S?R?Q?OANBNCNENFNL 80 | 111 88F^R=T=X>\A^E^G^J\NXQTRRRPRKQHNFJFGFEHAK>P=R= RR?Q?O@MBMFMGMIMMOOQPRPSPUOWMWIWGWFWCU@S?R? 81 | 112120E_M@M@P=T=V=Z>]A_E_H_J]NZQWRTRPRNPNPNUNWOYPZQZSZS\E\EZGZHZIYIWIVIGIEIBIAH@G@E@E>M=M@ RT?S?P@PAOBNENFNINLPNQPTPVPYLYHY?T? 82 | 113108E_UFUDUBS@Q?P?N?KCKGKLNPPPRPTNTMUKUHUF RUPUPTQQRORMRIQGNEJEHEEGAI>M=O=S=V?Y=[=[U[W[Y\Z]Z_Z_\Q\QZRZTZUYUWUVUP 83 | 114 92I[RLRMROSPUPVPVRIRIPJPLPLOLMLLLGLELBK@J@I@I>Q=QAQAR?V=X=Y=[>[?[AZBYBXBWBVAUAUATASCSDRFRIRJRL 84 | 115104KYLRKRKKLKMNOPQPSPUOUNUMSKPJMHKEKCKBL?M>P=Q=R=U=V>V=W=WCVCUAS?R?Q?O@OAOBQDSEUFWHYIYKYLYNXPVRTRRRQRNQMQLR 85 | 116 50KYK?K>M=Q:Q8S8S=X=X?S?SKSNTPVPWPXNYPXRTRQRNPNNN?K? 86 | 117 92E_VDVBV@U?T?S?S=\=\I\K\N\O\O]P]P^P_P_RXRWOWOVQRRPRLRJOHMHIHDHBH@G?F?E?E=M=MHMLOPRPTPUNVLVHVD 87 | 118 72F^JBIAH@H?G?F?F=R=R?Q?P?O?O@O@OAOBOCSMSMXCXBXAX@X@W?U?U=^=^?]?\@[BSRPRJB 88 | 119 84AcMRJRDBD@B?A?A=L=L?J?I@I@IAJBJCLLLLR=U=YLZL]D]C]A]A]@\?[?[=c=c?c?b?a@`C_EYRVRRDRDMR 89 | 120120E_UBVAV@V@V?V?U?U=^=^?]?\?Z@YAXBTF[N[P]P_P_RRRRPSPTPTOTOSNSMQJOLNMMNMOMOMOMPNPOPOREREPGPIPJOOHI@H?G?F?F=R=R?P?P@P@PASEUB 90 | 121102E_PXPYO[M\K\J\I\G[GZGYIXJXNWOUPSIBIAH@G?F?E?E=Q=Q?P?N?N@N@OAOBSMSMVEWDXCXBXAX@X?W?U?U=_=_?^?]?\AZCYEPX 91 | 122 48H\[RHRHQT?Q?O?M@L@KBKCICJ=[=[>OPSPUPWPYOZMZK\K[R 92 | 123 92LXTRTTVVXWXXVXRWPVPTPRPJPIOHNFMELELDMDOCOBP@P?P8P6P4R2U1X1X3V3T5T7T>T@TBSCQDPDPEQESFTGTITJTR 93 | 124 12PTT2T\P\P2T2 94 | 125 92LXPJPIPGQFSETETDSDQCPBP@P>P7P5N3L3L1O1R2T4T6T8T?T@UBUCWDXDXEWEVFUHTITJTRTTTVRWNXLXLWNVPTPRPJ 95 | 126 52JZJHJFMCOCOCQCTDVEVEWEXDXCZCZFWIUITISHQGOGMGMGLGLHJH 96 | 127 24H\H,\,\\H\H, RMZWZW.M.MZ 97 | -------------------------------------------------------------------------------- /hershey/TimesNewRoman.hf.txt: -------------------------------------------------------------------------------- 1 | 32 2OU 2 | 33 58PTRMRMPAP@P?P?Q=R=S=T?T@T@TARM RROSOTPTQTQSRRRQRPQPQPPQORO 3 | 34 62NVTFTBS@S@S?S>T=U=U=V>V?V@VBUFTF ROFNBN@N?N>O=O=P=Q>Q?Q@PBPFOF 4 | 35 72KYLRMLKLKJNJOEKEKDODP=R=PDUDW=X=WDYDYEVEUJYJYLULTRRRTLOLMRLR ROJTJUEPEOJ 5 | 36120LXLMMMMOOQQQQINGMELDLBLAO>Q>QS>T>U>W?WCVCVAT?R?RFVIXKXMXOURRRRTQTQRPRMRLQLM RQEQ?P?N@NANCPDQE RRQSQTQUPVOVNVMTKRIRQ 6 | 37152F^Z=KSJSY=Z= RK=M=OAOCOELHKHJHHGFDFCFAH?J=K= RK>J>I@ICIEIFIGJGJHKHKHLGMFMCM@L?K>K> RYHZH\I^L^M^P[SYSXSVQUOUMULVIXHYH RYIYIXJWKWNWPXQYRYRZR[Q[P[M[K[JZIYI 7 | 38160G]VE\E\F[FYGWKUMVOXPZP[P\O\N]N\PZRXRWRTQSPQQNRMRJRGPGNGLJIMGMELCLBL@N>O=Q=S=U@UAUCSEPFRJTMWIWGWGWFVFVFVE RPERETBTAT@R?Q?P?O@OAOBODPE RROPLOJNHLIJKJMJNLQNQOQQPRO 8 | 39 30QSRFQBQ@Q?Q>Q=R=S=S>S?S@SBRFRF 9 | 40 44NVVXVXTWRVPTNNNKNFR?V=V>T?RBQGQJQNQPRRSUUWVX 10 | 41 44NVN>N=P>R@TBVHVKVORWNXNXPWRSSNSKSHSERCQAO?N> 11 | 42170MWRCRAQ@Q?Q>Q>R=R=S=S>S>S?RBRCSBTAU@V@V@W@W@WAWAVBUBSCSCSCTDVDWEWFWFWGVGVGUFTESDRDRDSESGSHSHRIRIRIQIQHQHQGQERDRDQDPEOFOFNGNGMGMFMFMEMDNDNDPDQCQCQCOBNBMBMAMAM@M?N?N?O@QBRC 12 | 43 28JZQPQIJIJGQGQ@S@SGZGZISISPQP 13 | 44 52PTPWPVQVSTSSSRSRSRSRRRRRRRQRQRPRPQPPQOROSOTQTRTTRVPW 14 | 45 12NVNJVJVLNLNJ 15 | 46 28PTROSOTPTQTQSRRRQRPQPQPPQORO 16 | 47 12NVV=ORNRU=V= 17 | 48 82KYKHKEN@O>Q=R=T=V@YCYHYKVPSRRRORMOKLKH RNHNMOOPQRQSQTPUNVLVGVDUAT@T?S>R>Q>P?OANFNH 18 | 49 50NVN@S=S=SNSPTQUQVQVRNRNQPQQQQPQNQDQAQAQ@P@P@O@N@N@ 19 | 50 66KYYNWRKRKQQMUGUDUBS@Q@O@MALCLCL@O=R=T=WAWCWDWFVHSKOOOPTPUPWPXOXNYN 20 | 51106LXMBM@P=R=T=V?W@WBWDTFVGXJXKXNVPTRPRNRLQLQLPMPMPNPNPNPPQQQQQRQSQVOVMVLUKUJTJTIQHPHPHPHQGSFTDTCTAR?Q?O?MBMB 21 | 52 36KYYKYMVMVRTRTMKMKKT=V=VKYK RTKTAMKTK 22 | 53 72LXX>W@Q@OCSDVFXHXKXLVOTQSRQRPRNRLQLPLPMOMONONPOPPQQQSQVNVLVJSGQFPFMFQ>X> 23 | 54 90LXX=X>V>T?QBPEOGQESEUEXIXKXNWPURRRPRNQLNLJLGNCR?V=W=X= ROHNJNKNMPPQQQRRRTRVOVMVJTGRGQGPGOH 24 | 55 28LXN>X>X>RRPRV@P@O@NAMALCLCN> 25 | 56116LXPHNFLDLBL@O=R=T=W@WBWCVESGVIWJXLXMXOURRRORMQLOLMLLNIPH RSFTEUCUBU@S>R>P>O@OAOBODPDSF RQHPINLNMNOQRRRTRVPVNVMUMTKQH 26 | 57 88LXLRLRNRQPTLUISKQKOKLGLELBM@O=R=T=V@XBXFXIUORQPRMRLR RUHVFVEVDU@S>R>P>NANCNFPHQIRISIUIUH 27 | 58 56PTRDSDTETFTFSGRGQGPFPFPEQDRD RROSOTPTQTQSRRRQRPQPQPPQORO 28 | 59 80PTRDSDTETFTFSGRGQGPFPFPEQDRD RPWPVQVSTSSSRSRSRSRRRRRRRQRQRPRPQPPQOROSOTQTRTTRVPW 29 | 60 18JZJHZAZBLHZNZOJHJH 30 | 61 24JZJEZEZFJFJE RJJZJZKJKJJ 31 | 62 18JZZHJOJNXHJBJAZHZH 32 | 63102MWRMQMQKRISGTEUCUBU@S>Q>P>N?N@NAOBOCOCODNDNDMCMBM@O=R=U=V?WAWBWCVEUGSIRKRM RROROSPSQSQRRRRQRPQPQPPQORO 33 | 64178E_YDWJVMUOUOUPVPWPXP[N]I]G]DZ@V>S>P>JAGHGLGOJUOXRXVX\S^O_O^TWYRYOYHUEOEKEHIAO=S=V=[?^D^G^J[OXRVRTRSPSPSOTMQPORNRMRKPKNKLNGPERDSDTDUEVFVDYD RSDRDQFOGNJMLMNMONPOPPPRNTLTJUHUGUFTDSD 34 | 65 72G]UKMKLNKPKPKQLQNQNRGRGQIQIQJPKNR=R=YNZP[Q]Q]RURUQVQWQWPWPVNUK RUJQBNJUJ 35 | 66126I[VHYHZI[K[L[NYQVRRRIRIQJQKQLQLPLNLAL@K?K>J>I>I>R>T>V>X?ZAZCZEXGVH ROGOGQGRGTGVFWDWCWAT?Q?P?O?OG ROQPQRQUQXOXMXLVISHQHPHOHOHOQ 36 | 67 78I[Z=[DZDYAV>S>Q>NALELHLKNORQTQVQYO[M[MZPVRSRNRKOILIHIEL@P=S=U=W>X?X?Y?Y?Y>Z=Z= 37 | 68 76H\HRHQIQJQJQKPKNKAK@J?J>I>H>H>P>U>Y@\E\H\LZOWRQRHR RNQOQQQTQYLYHYDT?Q?O?N?NQ 38 | 69108I[O?OGTGUGVFWFWDWDWKWKVJVIVIUHTHOHOOOPOQPQQQTQVQWPXPYOZM[MYRIRIQJQKQKQLQLPLNLAL?L?K>J>I>I>Y>YBYBXAW@W?V?U?O? 39 | 70 96JZP?PGTGUGVFVDWDWKVKVJVIUHTHPHPNPPPPPQQQRQRQSQSRJRJQKQLQMQMPMNMAM@M?M?L?L>K>J>J>Y>ZBYBYAX@V?U?P? 40 | 71110H\Y=ZDYDYAW@V>S>O>MBKDKHKJMOQQSQTQVQWPWKWIWHVHTHTG\G\H\H[HZHZIZKZQXRURSRMRJOHLHHHFICK@M?O=R=T=U>W>X?X?X?Y>Y=Y= 41 | 72144H\MGWGWAW@V?V?V?U>U>T>T>\>\>\>[>Z?Z?Z@ZAZNZPZPZQZQ[Q\Q\Q\RTRTQUQVQVQWPWNWHMHMNMPNPNQNQOQOQPQPRHRHQHQJQJQJPJNJAJ@J?J?J?I>H>H>H>P>P>O>O>N?N?M@MAMG 42 | 73 68NVVQVRNRNQNQPQPQQPQNQAQ@P?P?P?O>N>N>N>V>V>V>T>T?S@SASNSPTPTQTQUQVQVQ 43 | 74 76LXO>O>X>X>W>V>U?U@UAUKUNTPQRORNRLQLPLOMOMONONOOOPQPQQQQQRPRORAR@R?Q?Q?P>P>O> 44 | 75140G]PGWNYP\Q]Q]RSRSQTQUQUQUPUPTOMHMNMPMPMQNQNQOQPQPRGRGQHQIQJQJPJNJAJ@J?J?I?I>H>G>G>P>P>O>N>N?M?M@MAMHMGOFSBT@U@U?U?T>S>S>S>Z>Z>Z>X?W@VAUASDPG 45 | 76 70I[ZL[MYRIRIQJQKQLQLPLNLAL@L?K>J>I>I>R>R>Q>P?O@OBONOPOPPPPQRQTQVQXPYNZL 46 | 77102E_QRIAIOIPJQJQKQLQLREREQFQGQHQHPHOHAH@H?G?F>E>E>K>RNY>_>_>^>]>\?\@\A\O\P\Q]Q^Q_Q_RVRVQWQXQYQYPYOYAQRQR 47 | 78 84G]G>M>YMYAY@X?X>W>V>V>]>]>\>[>Z?Z@ZAZRZRLBLOLPMQMQNQOQORHRHQIQJQKQKPKOKAJ@I?H?H>G>G> 48 | 79 64H\R=V=\C\H\LVRRRNRHMHHHCK@N=R= RR?O?NALCLHLMNOOQRQUQXMXHXCVAU?R? 49 | 80 98JZPIPNPPPQQQRQSQSRJRJQKQLQMQMPMNMAM@M?L>K>J>J>R>T>X?ZBZCZFVISISIQIPI RPHQHRHRHTHVFVDVBU@S?R?Q?P?PH 50 | 81 86H\TRVUYW\W\XZXUVQTORMQLPJOHKHHHCN=R=V=\C\H\LXQTR RR?O?NALCLHLMNOOQRQUQVOXMXHXEWBW@T?R? 51 | 82114H\\RWRPIOIOININININNNPNQOQPQQQQRHRHQIQJQKQKPKNKAK@J?J>I>H>H>O>S>V?XAXCXEUHSHWNXPZQ\Q\R RNHNHNHOHQHTETCTAR?P?O?N?NH 52 | 83118KYW=WDWDWBU@S?Q?P?NANBNCNCODSFVHXIYLYMYOURRRQRQRPRMQMQMQLRLRLRLLLLLNNPPQRQTQVOVNVMULTKTKOHLFKDKCKAO=Q=S=U>U?V?V?W>W=W= 53 | 84 68JZZ>ZCZCZAYAY@W?V?S?SOSPTQTQUQVQVRNRNQNQPQPQQPQOQ?N?M?L?K@JAJCJCJ>Z> 54 | 85 98G]V>V>]>]>\>[>Z?Z@ZBZJZMYPURRRORKPKNJMJIJAJ@I>H>G>G>P>P>O>N>M?M@MAMJMKMNOPQQRQTQWOXMXJXAX@X?X>V>V> 55 | 86 68G]]>]>\?[?Z@ZASRRRKAJ@J?I?H?G>G>P>P>N?M?M@MANBSNXBYAY@Y?X?W>W>V>V>]> 56 | 87120D``>`>_>^?^@^@]BWRWRRFNRMRGBF@F@F?E>D>D>K>K>K>J>I?I@I@JBNMQDQBPAP@P?O?O?O?N?N>N>N>U>U>U>T>S?S@SATBXM\B\@\@\?\?[?[>Z>Z>`> 57 | 88132G]SGXMZP[Q]Q]RTRTQUQUQVQVQVPVPVPVOUNRIMOMOMPMPMQMQNQNRGRGQHQIQJQKOLNQHMBK@I>H>H>Q>Q>P>O?O@O@PASFVAW@W@W@W?W?W?V>U>U>\>\>[>[?Z?Y@WBSG 58 | 89 98G]V>]>]>\>\>Z?Y@XBSISOSPTQTQVQVQVRNRNQNQPQPQQPQOQJKAJ@I?H?H>G>G>P>P>P>O>N?N@N@OBSHWBXAX@X?W?V>V>V> 59 | 90 40I[Z>MQUQWQYOZM[MZRIRIQV?P?N?M@LALCKCL>Z> 60 | 91 20OUUXOXO=U=U?Q?QWUWUX 61 | 92 12NVO=VRURN=O= 62 | 93 20OUO=U=UXOXOWSWS?O?O= 63 | 94 18KYR=YHWHR?MHKHR=R= 64 | 95 12JZZYJYJWZWZY 65 | 96 12PTP=S=TCTCP= 66 | 97132LXSPQRQRPRORNRLPLOLNLMMLPJSISHSFREQEPEOEOFOGOGOHNINIMILHLGLFODQDSDTEUEVFVGVIVMVOVPVPWPWPWPWPXOXPWRURTRSQSP RSOSJQKQKOLNMNNNOPQPQRQSO 67 | 98 82KYPGRDTDVDYHYKYNWQURRRQRNRMQMCM@M?L?L?L?K?K?O=P=PG RPHPPPQRQSQTQWNWLWITFSFRFQGQGPH 68 | 99 72LXXMWPTRRRPRLOLKLHPDSDUDWFWGWHWIVIUIUHTHTFTESEREQEPFOHOJOLQPSPUPVOWNWMXM 69 | 100100KYTPSRRRPRNRKOKLKIODQDSDTETCT@T?T?S?S?R?R?V=W=WMWOWPXPXPXPYPYQURTRTP RTOTHTGSFREQEPEOFNHNKNNPQRQSQTO 70 | 101 68LXNJNMPNQPSPUPWOWMXMWOTRRRPRLOLKLHPDSDUDXGXJNJ RNIUIUGTGTFREREPEOGNI 71 | 102 92LXQEQNQPQQRQSQTQTRLRLQMQMQNQOPONOELELDODODOAP?S=T=V=W>X?X?X@WAWAVAV@U?T>T>S>R>Q?Q@QCQDUDUEQE 72 | 103192KYOMNLLJLILGODRDTDUEXEYEYEYEYEYFYFYFYFYFXFVFWGWIWKTNRNQNPMONNONONOOPPPPPRPUPVPWPYRYSYUWVUYQYNYLWKWKVKUKULTLSLSNRMQLQLPLPMNOM RQEPEOFOHOKPLQMRMSMTKTITGSFSEQE RORNSMTMUMUNVPWRWUWXUXTXSWSVRTRPROR 73 | 104114KYO=OGQESDTDUDWEWGWHWJWOWPXQXQXQYQYRSRSQSQTQUQUPUPUOUJUHTGSFSFRFPGOHOOOPPQQQRQRRKRKQLQLQMQMPMOMCM@M?L?L?L?K?K?O=O= 74 | 105 78OUR=S=T>T>T?S@R@Q@Q?Q>Q>Q=R= RSDSOSPTQTQUQUROROQPQPQQPQOQJQGQGQFPFPFOFOFOFSDSD 75 | 106 92NVU=U=V>V>V?U@U@T@S?S>S>T=U= RVDVRVUSYQYOYNXNWNWOVOVOVPVPVRWRWRWSWSVSSSJSGSGSFSFRFRFQFQFUDVD 76 | 107116JZO=OJSGTFTFTFTFTETESESDYDYEXEVFUFRJUNWPWPXQXQYQZQZRSRSQTQTQTQTQSPOJOOOPPQPQQQQRJRJQKQLQLQLQMPMOMCM@L?L?L?K?K?J?N=O= 77 | 108 48OUS=SOSPTQTQUQUROROQPQPQQPQOQCQ@Q?P?P?O?O?O?S=S= 78 | 109180F^KGMEMENEODPDQDSESGUEWDXDYD[E[F\G\I\O\P\Q\Q]Q^Q^RWRWQWQXQYQYQYPYPYOYIYGYGXFWFVFTGSHSHSHSOSPTQUQVQVROROQPQQQQPQPQOQIQGPGPFOFNFMFLGKHKOKPKQLQMQMRGRGQHQHQIPIOIJIHIGHFHFHFGFGFFFJDKDKG 79 | 110108KYOGRDTDUDWEWFWGWIWOWPXQXQXQYQYRSRSQSQTQUQUPUPUOUIUHTFSFQFOHOOOPOQPQPQRQRRKRKQKQLQMPMOMJMGMFLFLFKFKFKFODODOG 80 | 111 68KYRDUDWFYHYKYMWQTRRRORMPKNKKKIMFPDRD RREQEOFNHNJNMQQRQTQVOVLVHTFSERE 81 | 112124KYKFODPDPGQESDTDVDWFYHYKYNWQURSRRRQRPRPQPUPWPXQXRXRXKXKXKXLXMXMWMWMUMHMGMFLFLFLFKFKF RPHPMPOPOPPRRSRTRUPVOVLVIUGTFSFRFQGQGPH 82 | 113 96KYWDWUWWWXXXYXYXRXRXSXSXTXTXTWTUTPSQQRPRNRKOKLKHODRDSDTDUEVEVDWD RTOTHTGTFREQEPEMHMKMMPPRPRPTPTO 83 | 114 86MWRDRGSDUDVDWEWFWFVGVGUGTFTFSFSFRGRHRORPRQRQSQTQTRMRMQNQOQOQOPOPOOOJOGOFOFNFNFMFMFQDRD 84 | 115104MWVDVIUIUFSEQEPEOFOGOGPHPIQITJWLWNWPTRRRQRORORNRNRNRMRMMNMNOQRRRSRTPTOTNSMOKNJMIMHMFPDQDRDSDTETEUEUDUDVD 85 | 116 60NVS@SDVDVESESNSPSQTQTQUPVOVOVQTRSRRRQQPPPOPENENEOEPCQBQAR@S@ 86 | 117 80JZWDWMWOWPXPXPYPYPZQURURUPSQQRPRORMQMOMMMGMFLELEJEJDODONOOQQQQRQTPUOUGUFTESESDWD 87 | 118 70KYKDQDQEQEPEOFOFOGPGSOVGVFVFVEVEVEVEUEUDYDYEYEXEXFWGSRRRMGMFLELEKEKEKD 88 | 119 94G]GDMDMELELELFLFLGOORHQFQFPEPEOEODUDUETETETFTFTFTGWOZGZFZFZEYEXEXD]D]E[E[GVRVRRJNRNRIGIFHEGEGD 89 | 120118KYKDQDQEQEPEPFPFQGQGQHRISHTFTFTETESESDXDXEWEWEVFUHSJVOWQXQYQYRSRSQSQTQTQTQTPSOQLOONPNQNQOQOQORKRKQKQLQLQNOQKNGMFLEKEKD 90 | 121106KYKDQDQEQEPEOFOFOGPHSNVGVFVFVEVEVEVEUEUDYDYEYEXEXFXFWGRUQWNYMYLYKXKWKVLVMVMVNVOVOVOVPUQTRQMGMGLFLFLEKEKEKD 91 | 122 48LXXNXRLRLQUEPEOENFNFMGMHMHMDXDXEOQTQVQWPWPWOWNXN 92 | 123 82NVVXVYTXQUQSQRQOQNQMOKNKNJOJQHQGQGQDQCQAT=V=V=T>S@SASBSESFSGQJOKQKSNSPSQSTSUSVTXVX 93 | 124 12QSS=SYQYQ=S= 94 | 125 82NVN=N=P=SASCSDSGSGSHUJVJVKUKSMSNSOSRSSSUPXNYNXPXQVQUQTQQQPQNSLUKSJQGQFQEQBQAQ@P>N= 95 | 126 52JZYHZHZJXLVLVLTLQJNIMILIKKKLJLJJLHNHNHOHPIUKVKXKYIYH 96 | 127 24JZJRJ?Z?ZRJR RKRYRY?K?KR 97 | -------------------------------------------------------------------------------- /hershey/Ubuntu.hf.txt: -------------------------------------------------------------------------------- 1 | 32 2LX 2 | 33 52NVU7UY@\B^F^H^LYPTQTXPXPQLQHPFOHKILNMQM 6 | 37156>f>6>4?0A.E,F,H,K.M0O4O6O9M9>6 RK6K3H0F0D0B3B6B9DJ>F:F6F3G0J.M,O,Q,U.W0X3X4X6W9UQ>YG[D\@`@_C^H\J^LaPbR]R\QZOYNWPRRNRJRFQCMBJBH RGHGIHKIMLNNNQNULWKL@KAIBHDGFGH RT5T3Q0O0M0J3J5J9NZ?VBVAU?SK=O:P8N8J8H7G7I2I2K3O5Q6P4P0P.P-T-T. 12 | 43 28F^F@P@P5T5T@^@^DTDTOPOPDFDF@ 13 | 44 32NVVLVLVMVNVQTWRZNYPWPQPOPNPMPLVL 14 | 45 12KYK@Y@YDKDK@ 15 | 46 28NVVOVQTSRSPSNQNONNPLRLTLVNVO 16 | 47 12G]L\G\Y)])L\ 17 | 48 72F^F@F6L,R,X,^6^@^IXSRSLSFIF@ RY@Y=X7W3T1R1P1M3L7K=K@KCLHMLPNRNTNWLXHYCY@ 18 | 49 30KYK5N4S0V-Y-YRTRT4T5R6P7M8L9K5 19 | 50 94G]\7\9[X>Z@\C]F]H]J\NXQSSPSNSKRIRGQGQGMHMMNPN 21 | 52 52E_EEFCJ=N6S0V-Z-ZE_E_IZIZRVRVIEIEE RV3T5Q9N>KBJEVEV3 22 | 53 84G]N;V<]B]H]J\NXQSSPSNSKRIRGQGQHMIMMNPNRNUMWLXIXHXFWCSAM?I?I=J8J4J0J-\-\1O1O2N5N8N;N; 23 | 54100F^FCF>I6O0V-[-\1X1S3O5L:L=MX?^B^H RKIKJLLMNPOROTOWNXLYJYIYGXEUCRAPANBKFKI RX6X5X3V2T1R1P1N2L3L5L6L7M:NVY>Y=YR>P>N;N:N9P6R6T6V9V: 28 | 59 60NVULULUMUNUQSWRZNYOWPQPOPNPMPLUL RV:V;T>R>Q>O;O:O9Q6R6T6V9V: 29 | 60 18F^KB^J]NFDF@]6^:KB 30 | 61 24F^FF^F^JFJFF RF:^:^?F?F: 31 | 62 18F^F:G6^@^DGNFJYBF: 32 | 63100I[Q1M1J2I/J.O,Q,T,X.[1[4[5[7Z:XTARDRERFRFRGNGNENDNCO@Q=S;U9V7V6V3S1Q1 RTOTQRSQSOSMQMOMNOLQLRLTNTO 33 | 64180@5F/N,R,W,_/d5h\?_C_G_MWROR RJAJNJNKNLNNNONQNUMXLZIZGZEYCVBSAPAJA RJ=O=Q=TQ@VE[J_P`RZRYPTJPFKBIAIRDRD-I-I=KfPMOLNHLCI>G:F7E6DR?M?D@:A1A-F-G0J6N=QDRGSDV=Z6]0^-c-e?fRaR`K`<_6^7]:[>XCVHULTMPM 47 | 78 44Ca\R[PXKUFQAM;J7H5HRCRC-G-J0P7U>ZF\H\-a-aR\R 48 | 79 88@d@@@;C4H/O,R,V,\/a4d;d@dDaL\PVSRSOSHPCL@D@@ RF@FCHHKLONRNUNYL\H^C^@^<\7Y3U1R1O1K3H7FY;Y9Y6X4U2Q1O1 50 | 81110@d@@@;C4H/O,R,V,\/a4d;d@dDaJ^OYRVRVTXU[W_XaX`\][XZTXQUQSMSGPCK@D@@ RF@FCHHKLONRNUNYL\H^C^@^<\7Y3U1R1O1K3H7FZ@]C^F^I^NWSQSOSKRHQFQFPGLHMMNQN 53 | 84 20D``-`2U2URORO2D2D-`- 54 | 85 52D`RSNSIQFMDGDDD-I-IDIINNRNTNWMZJ[G[D[-`-`D`G^M[QVSRS 55 | 86 42Acc-a3]=ZFVNTRPRNNJFG=C3A-G-J6OERLUE[6]-c- 56 | 87 70;iR9P@KLIRDRAJ=8;-@-A1C:DAFIGLIFN9P2T2V9[F]L^I`Bb:c1d-i-g8cJ`R[RYLT@R9 57 | 88 52Bb\R[PYMVHSDRBQDNHKMIPHRBREMKDO?C-I-R;[-a-U>YD_MbR\R 58 | 89 38BbOROCL>E3B-I-K2P;R?U;Z2\-b-_3X>UCUROR 59 | 90 48E__1^3Z7W;SAOFLKKN_N_REREOFLIGLBP=T8W3X2F2F-_-_1 60 | 91 20LXL)X)X,Q,QXXXX\L\L) 61 | 92 12G]G)K)]\X\G) 62 | 93 20LXX\L\LXSXS,L,L)X)X\ 63 | 94 18F^T-^?[AR2IAF?P-T- 64 | 95 12E_EX_X_\E\EX 65 | 96 12MWP)W1T3M,P) 66 | 97116G]SNTNWNXNXFWFUESEREPFNGLHLJLLPNSN RR6U6Y7\:]>]@]R\RZRWRTSRSPSLRIPGLGJGHIDLBQASATAUAWBXBXBXAX?W=V;T:R:O:L;K;J7K7P6R6 67 | 98 76F^K8L7O6R6U6Y8\<^A^D^H\MYQSSPSMSHRFQF*K)K8 RKNLNNNPNTNYIYDYBX>VKBKDKIPNTNVNXNYNY< RY*^)^Q\RWSTSQSKQHMFHFDFAHU:R:Q:N;L=K@KBYB 71 | 102 44JZT)W)Z*Z*Y.Y.V-U-Q-O1O4O6Y6Y;O;ORJRJ4J/O)T) 72 | 103100F^YOXPUQRQPQKOHLFGFDFAHS:V8W6\6[8X;U>RAQC 77 | 108 28MWVSQRMOMKM*R)RJRKSMTNUNWNVS 78 | 109 96?e?7A7G6J6M6P7R9R8T7V7Y6Z6^6b8d;e@eCeR`R`D`A_>^;\:Z:W:TN;K:J:I:G:F:D;D;DR?R?7 79 | 110 52G]G7I7N6R6U6Y8\;]@]C]RXRXDXAX=V;S:Q:Q:O:M:L;L;LRGRG7 80 | 111 72E__D_H]MZQUSRSOSJQGMEHEDEAGI:N6S6T6V6X6Z7Z7Y;Y;U:S:Q:NN@PARBSBUCXD[F\I\K\OVSQSMSIQHQIMJMNNQN 85 | 116 52JZO6Y6Y;O;OGOIOLQNSNTNVNYMYMZQYQVSSSQSMQKNJJJGJ/O.O6 86 | 117 48G]]Q[RVSRSOSKQHMGIGFG6L6LELJONSNTNUNWNXNXNX6]6]Q 87 | 118 42F^^6\>WLTRPRMLH>F6K6K9M?OEQJRLSJUEW?Y9Y6^6 88 | 119 78>fXRWNSDR?QDMNLRHREM@?>6D6D9F?GEIJJLKJMDN>P9P6T6U9V>XDYJZL[J]E_?`9a6f6d?_M\RXR 89 | 120 52E_YRYQWNUKSHRGQHOKMNKQJRERGNLGODF6K6R@Y6^6UCWG]N_RYR 90 | 121 70E_FWGWIXJXMXPUQRNLH>F6K6L9N>PDRJSMUGXXMUSTURYO[L\J\I\H\G\F[E[FW 91 | 122 40G]\:[;W@SEOKMN]N]RGRGOILLGQATOV?X@Z@ZD 95 | 126 72E__?^@^C\EYGWGVGSERDPDNBLBKBJCIDIEIFEEFDGBH?K>M>N>Q?R@TAVBXBYBZA[@[?[>_? 96 | 127 52NVU7UG;E8E5E3F0H-L+N*N$V$V*Y*^,_,]3[2V1S1P1N3N5N6N7P8S9T:W;[=_@`D`F`H_L]OYQVQVXNXNQ 6 | 37124AcGRAR])c)GR RSISCW>Z>^>bCbIbN^SZSWSSNSI R]I]F[CZCYCXFXIXKYNZN[N]K]I RB2B-F(I(M(Q-Q2Q7M=I=F=B7B2 RL2L/J-I-H-G/G2G5H7I7J7L5L2 7 | 38144CaZRYPXNVPQSOSLSGQDNCICGCDF>IVBXCY@Y;`<`@^F]I^KaPaRZR RUJSGOCMAKBJEJGJILLOLPLSKUJ RM2M4N7O8Q7S3S1S0R.P.O.M0M2 8 | 39 32NVV%V)V+V/U3U7T9P9O7O3N/N+N)N%V% 9 | 40 32G]O@OHVU\XY]PXGIG@G7P(Y#](V,O8O@ 10 | 41 32G]U@U8N,G(K#T(]7]@]ITXK]HXNUUHU@ 11 | 42134D`R9R:Q

O@OAMCG>H=IWCUAU@T>STARAPAN>N=N;P8R8T8V;V= 18 | 49 38D`OKO4M5I8G8D1F1J/M-Q*R)W)WK`K`RGRGKOK 19 | 50 80D`^4^6\:Z=VAUBTCREPHNJNK`K`REREQEOEOELGFKAP=S9V6V4V2S/P/N/J1H3D-G+N(Q(T(X)\,^1^4 20 | 51102D`PSNSKRHREQDQFJGJLLPLTLWHWFWBR@N@L@L9O9P9R8T7V5V4V1S/Q/N/J0H1E,G*M(Q(T(Y*\-^1^3^6[:Y<\=`B`F`I^MZQTSPS 21 | 52 52CaT3S5Q8NG>H8I.I)^)^0P0P2O6O8X8`?`F`H^MZQSSOSNSJRGREQDQFJGKLLOLRLUKWIWGWF 23 | 54100D`Q>P>M?M?M@LALBLDMGNJQLRLTLVJWHXFXEXBU>Q> RR8V8[:^=`B`E`G^L[PVSSSLSDJDAD;H2N,X)])^/[0V0R3O6N9O8R8R8 24 | 55 34E_JRJNMDP;T3V0E0E)_)_/^1Y8VBSMRRJR 25 | 56124D`_3_5\;Y=]?`D`G`I_M[QVSRSOSJQFNDJDHDEG?J=G;E6E4E2F.I*N(R(X(_._3 RXGXFWDUBRAO@NALELGLIOLRLULXIXG RR/P/M1M3M6P9S:V9W5W3W1T/R/ 26 | 57 94D`SMNT@TB 34 | 65 54BbYRWHLHJRBREFK2N)W)Z2_FbRYR RR1Q3P7O;N?NBVBV?U;T7S3R1 35 | 66110D``F`I]NYQSSPSMSGRDQD)G)L(O(S(Y*]-^1^3^6[;Y<]=`C`F RL@LKMKOKPKRKTKVJXGXFXCT@Q@L@ RP9S9V6V4V3U1S0Q/P/O/M0L0L9P9 36 | 67 68CaUSLSCHC=C8F0K+R(V(X(\)^*`+a+_1]0Y/V/T/P0M4L9L=LDPLVLYL]J_JaP_QYSUS 37 | 68 76D`LKMKMKNKQKTIWFX@X=X;W6U2Q/O/N/M/L0LK R`=`C]KXPQSMSKSFRDRD)G)L(N(R(Y+]0`8`= 38 | 69 28E_ERE)^)^0M0M9\9\@M@MK_K_RER 39 | 70 24E_ERE)_)_0N0N:\:\@N@NRER 40 | 71 76CaV/S/P1M5L:L=LEPLULVLWLXKX<`<`Q_RYSTSPSJPFKCCC=C8F0K+R(U(X([)^*`+a+^1]0Y/V/ 41 | 72 28D``)`RWRW@M@MRDRD)M)M9W9W)`) 42 | 73 28E_EREKNKN0E0E)_)_0V0VK_K_RER 43 | 74 44E__D_G^MZQUSQSLSGQEOHIJJNLPLSLWHWDW0I0I)_)_D 44 | 75 58CaXRWPTJQEMAK?KRCRC)K)K:M8P3S/U+V)_)^+\/Y4U9SYC]I`OaRXR 45 | 76 16E__RERE)M)MK_K_R 46 | 77 62CaL)M*N/P4Q9R;S9T3V.W*X)_)_.`8aBaMaRZRZ2UCOCJ2JRCRCMDBD7E-E)L) 47 | 78 36D`YRVKP=L7LRDRD)K)M,Q3T9W@XBX)`)`RYR 48 | 79 72CaK=K@LFMIPLRLTLWIXFY@Y=Y:X5W1T/R/P/M1L5K:K= RC=C3K(R(Y(a3a=aHYSRSKSCHC= 49 | 80 60D`O(W(`/`6`=WDODLDLRDRD)E)H)K(N(O( RP/P/M/L0L=P=T=X:X6X2T/P/ 50 | 81 86CaC>C3K(R(Y(a3a>aG[RUSVT\WaW_]W\OWNSIQCGC> RK>KALFMJPLRLTLWJXFYAY>Y;X5W1T/R/P/M1L5K;K> 51 | 82 82D`N(R(W*\-^2^6^9[?WAYC[G]K`P`RXRVNRGPCLCLRDRD)E)H)K(M(N( RU6U2Q/N/N/L/L0LG;E7E4E.L(S(W(\*^+\1Z0V/S/M/M3M5N6P8S9T9V:Z<^?`C`G`MXSQSLSFQDPFIHJMLQL 53 | 84 20Caa)a0V0VRNRN0C0C)a) 54 | 85 56D`RSNSHQEMDGDCD)L)LCLEMINKPLRLTLVKWIXEXBX)`)`C`G_M[QVSRS 55 | 86 50BbNRLMIBE6C,B)K)K,M4O=QFRJSFU=W4Y,Y)b)a-_7\AXMVRNR 56 | 87 86CaU7U9VWT9Q9P9N:M:MKNLPLPLTLWGWB R`B`F^LZPTSQSMSGRDQD%M$M3N3R2S2V2[4^9`?`B 68 | 99 68D`DBD?F9J4Q2V2Y2]3`4^:\:Y9W9T9P:M=L@LBLGQLWLYL]K_J`Q^RYSVSQSJPFLDFDB 69 | 100 64E_MBMFPLTLULVLWKW:V:T9S9P9M>MB R_Q]RWSSSLSEJECE;K2Q2S2V3W3W%_$_Q 70 | 101 84D`DCD>F8J4P2R2Y2`:`B`C`D`ELELHQLULXL\K^J_Q]RXSUSQSJQFMDGDC RY?Y>XL?Y? 71 | 102 60CaJRJ9C9C3J3J1J-L(P%T$W$Z$_%a&`-^,Z+X+V+T+S-R/R1R3_3_9R9RRJR 72 | 103 88E_MAMEPISITIVIWHW9V9U9T9M9MA R_N_VX]P]M]H\E[GTIUMVPVRVUUWTWQWPWOUOSPQPKPEHEAE:M2T2Y2_4_N 73 | 104 48E_ERE%N$N3O3Q2R2V2[4]8_=_@_RVRVAV=T9R9P9N:N:NRER 74 | 105 72D`T*T,Q/O/M/J,J*J'M$O$Q$T'T* R`Q^RYSWSTSPQMMLHLEL9D9D3T3TFTIWLYLZL]K_J`Q 75 | 106 72F^^P^T\YY\T]R]N]I\FZITKTOVQVSVUTUPU9I9I3^3^P R]*],Z/W/U/R,R*R'U$W$Z$]']* 76 | 107 58D`L?M>P;R7T4U3_3^4[8WU@UFOFO?OU9R9O9L>LB 81 | 112 66D`WCW>T9P9P9M9M9MJMKPLQLTLWGWC R`B`F^L[PVSSSPSMQM]D]D4G3M2Q2X2`;`B 82 | 113 76D`MCMGPLSLTLVKWJW9W9U9T9P9M>MC RDBD?F9J4P2S2U2X2\3^3`4`]W]WQVRRSQSNSIPFLDFDB 83 | 114 44F^];\;Z:X:U:U:S:P:N;NRFRF5I4P2T2U2X2Z3]3^3]; 84 | 115104E_QLTLWKWJWIVHTGRFPEOEKDHAF>FS?T?V@ZA]C_G_I_K]OZQUSQSMSGQEPFIIJNLQL 85 | 116 64CaK9C9C3K3K+S*S3`3`9S9SFSHSJUKVLWLYL[K\K^J_JaQ^RYSWSTSORLOKJKFK9 86 | 117 42E__Q\RVSRSNSIQFMEGEDE3N3NCNGPLSLTLVKV3_3_Q 87 | 118 42BbNRKLEUCQIPK^K^RFRFMGJKEN@R;S9F9F3^3^9 92 | 123 78F^N@QAUEUJUQUTVWXW^W^]X]R]MXMSMIMFKCICFCF=I=K=M:M7M.M)R#X#^#^*X*V*U,U/U7U;Q?N@ 93 | 124 12NVN#V#V]N]N# 94 | 125 86F^V@S?O;O7O/O,N*L*F*F#L#O#S%V'W+W.W7W:Y=[=^=^C[CYCWFWIWSWUVYS\O]L]F]FWLWNWOTOQOJOESAV@ 95 | 126 68BbXGVGSEPCMBLBLBKBJCIEIFBECCD?F[<[:b`A^EZGXG 96 | 127 24E_ERE _ _RER R\O\#H#HO\O 97 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 76 | 77 | 78 | 79 | 80 | 81 |

ttf2hershey

82 |
83 | 84 | 85 | -------------------------------------------------------------------------------- /truetype/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/ttf2hershey/9877eba3735a7bdfec230fe42ce4bcb8154ae67d/truetype/__init__.py -------------------------------------------------------------------------------- /truetype/glyphcurves.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # parse glyphs as curves 4 | # (c) Lingdong Huang 2018 5 | 6 | def lerp(p0,p1,t): 7 | x = (1-t) * p0[0] + t * p1[0] 8 | y = (1-t) * p0[1] + t * p1[1] 9 | return [x,y] 10 | 11 | def high_bezier(pts,t): 12 | if len(pts) == 1: 13 | return pts[0] 14 | elif len(pts) == 2: 15 | return lerp(pts[0],pts[1],t) 16 | else: 17 | return lerp(high_bezier(pts[:-1], t), high_bezier(pts[1:], t), t) 18 | 19 | def construct_curve(pts, precision=10, maxhandle=5): 20 | buff = [] 21 | curve = [] 22 | def flush(): 23 | n = precision*len(buff) 24 | for j in range(0,n): 25 | t = float(j)/float(n) 26 | curve.append(high_bezier(buff,t)) 27 | 28 | for i in range(len(pts)): 29 | x,y = pts[i]['x'], pts[i]['y'] 30 | buff.append([x,y]) 31 | 32 | if pts[i]['onCurve']: 33 | if 1 < len(buff) <= maxhandle+2: 34 | flush() 35 | buff = [[x,y]] 36 | else: 37 | curve += buff 38 | buff = [] 39 | elif i == 0: 40 | buff = [[pts[-1]['x'],pts[-1]['y']]]+buff 41 | elif i == len(pts)-1: 42 | buff += [[pts[0]['x'],pts[0]['y']]] 43 | if 1 < len(buff) <= maxhandle+2: 44 | flush() 45 | else: 46 | curve += buff 47 | buff = [] 48 | curve += buff 49 | curve = [[c[0],c[1]] for c in curve] 50 | 51 | return curve 52 | -------------------------------------------------------------------------------- /truetype/truetype.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # (c) Lingdong Huang 2018 4 | 5 | # TrueType specs: 6 | # https://docs.microsoft.com/en-us/typography/opentype/spec 7 | # https://developer.apple.com/fonts/TrueType-Reference-Manual 8 | 9 | import numpy as np 10 | import warnings 11 | import os 12 | import math 13 | import ttfparser 14 | import glyphcurves 15 | 16 | class TrueTypeFont(): 17 | def __init__(self, path, precision = 0, verbose=True): 18 | self.precision = precision 19 | self.verbose = verbose 20 | self.name = os.path.splitext(os.path.basename(path))[0] 21 | self.log("reading binary data from "+path+" ...") 22 | raw = open(path,'rb').read() 23 | self.cmapCache = {} 24 | 25 | self.log("parsing .ttf file...") 26 | self.ttf = ttfparser.TTFFile(raw) 27 | 28 | self.log("compiling character map...") 29 | self.compileCmap() 30 | 31 | self.log("compiling glyphs...") 32 | self.compileAllGlyphs() 33 | 34 | self.baseline = 0 #self.glyphData[self.chr2idx('x')]['rect'][1] 35 | self.basewidth = self.glyphData[self.chr2idx('x')]['rect'][2]-\ 36 | self.glyphData[self.chr2idx('x')]['rect'][0] 37 | self.log("initialized.") 38 | 39 | def log(self,info): 40 | if (self.verbose): 41 | print "["+self.name+"]", info 42 | 43 | def compileCmap(self): 44 | cmap = self.ttf.tables['cmap'] 45 | offset = cmap['offset'] 46 | self.ttf.file.seek(offset) 47 | version = self.ttf.file.getUint16() 48 | numTables = self.ttf.file.getUint16() 49 | encodingRecords = [] 50 | 51 | for i in range(numTables): 52 | encodingRecords.append({ 53 | "platformID": self.ttf.file.getUint16(), 54 | "encodingID": self.ttf.file.getUint16(), 55 | "offset": self.ttf.file.getUint32(), 56 | }) 57 | segments = [] 58 | for i in range(len(encodingRecords)): 59 | self.ttf.file.seek(offset+encodingRecords[i]['offset']) 60 | _format = self.ttf.file.getUint16() 61 | if _format != 4 or encodingRecords[i]['platformID'] != 0: 62 | continue 63 | length = self.ttf.file.getUint16() 64 | language = self.ttf.file.getUint16() 65 | segCountX2 = self.ttf.file.getUint16() 66 | searchRange = self.ttf.file.getUint16() 67 | entrySelector = self.ttf.file.getUint16() 68 | rangeShift = self.ttf.file.getUint16() 69 | segCount = segCountX2 // 2 70 | 71 | for j in range(segCount): 72 | segments.append({}) 73 | for j in range(segCount): 74 | segments[j]['endCode'] = self.ttf.file.getUint16() 75 | reservedPad = self.ttf.file.getUint16() 76 | for j in range(segCount): 77 | segments[j]['startCode'] = self.ttf.file.getUint16() 78 | for j in range(segCount): 79 | segments[j]['idDelta'] = self.ttf.file.getUint16() 80 | for j in range(segCount): 81 | segments[j]['idRangeOffset'] = self.ttf.file.getUint16() 82 | for i in range(segCount): 83 | segments[i]['glyphIndexArray'] = self.ttf.file.tell() 84 | break 85 | if len(segments) == 0: 86 | warnings.warn("Unimplemented: Encoding other than (PlatformID = 0, Format = 4) is not yet supported", Warning) 87 | self.segments = segments 88 | 89 | def chr2idx(self,char): 90 | try: 91 | return self.cmapCache[char] 92 | except KeyError: 93 | pass 94 | c = ord(char) 95 | seg = None 96 | result = 0 97 | for s in self.segments: 98 | if s['startCode'] <= c <= s['endCode']: 99 | seg = s 100 | break 101 | if seg is not None: 102 | if seg['idRangeOffset'] == 0: 103 | result = (seg['idDelta']+c) % 65536 104 | else: 105 | glyphIndexAddress = (c-seg['startCode']) 106 | self.ttf.file.seek(seg['glyphIndexArray']) 107 | for i in range(glyphIndexAddress): 108 | self.ttf.file.getUint16() 109 | result = self.ttf.file.getUint16() 110 | 111 | self.cmapCache[char] = result 112 | return result 113 | 114 | def compileGlyph (self, index): 115 | glyph = self.ttf.readGlyph(index); 116 | bbox = glyph['xMin'], glyph['yMin'], glyph['xMax'], glyph['yMax'] 117 | if (glyph == None): 118 | warnings.warn("No glyph!", Warning) 119 | return {"rect":(0,0,1,1), "poly":[]} 120 | 121 | if (glyph["type"] == "simple" ): 122 | points = glyph["points"] 123 | 124 | ptlist = [] 125 | for i in range(0,len(glyph["contourEnds"])): 126 | e0 = (glyph["contourEnds"][i-1]+1) if i != 0 else 0 127 | e1 = glyph["contourEnds"][i]+1 128 | ptlist.append(points[e0:e1]) 129 | 130 | polylines = [] 131 | for i in range(len(ptlist)): 132 | if self.precision == 0: 133 | polylines.append([]) 134 | for j in range(len(ptlist[i])): 135 | p = ptlist[i][j] 136 | polylines[-1].append([p['x'],p['y']]) 137 | else: 138 | 139 | cc = glyphcurves.construct_curve(ptlist[i],self.precision) 140 | polylines.append(cc) 141 | 142 | else: 143 | components = glyph["components"] 144 | polylines = [] 145 | for c in components: 146 | subglyf = self.compileGlyph(c['glyphIndex'])['poly'] 147 | xof,yof = 0,0 148 | xscale, yscale = 1,1 149 | if c['flags']['ARGS_ARE_XY_VALUES']: 150 | xof, yof = c['matrix']['e'], c['matrix']['f'] 151 | else: 152 | warnings.warn("Unimplemented: Compound glyphs point matching", Warning) 153 | if c['flags']['WE_HAVE_A_SCALE'] or c['flags']['WE_HAVE_AN_X_AND_Y_SCALE']: 154 | xscale , yscale = c['matrix']['a'], c['matrix']['d'] 155 | if c['flags']['WE_HAVE_A_TWO_BY_TWO']: 156 | warnings.warn("Unimplemented: Compound glyphs linear transformation", Warning) 157 | 158 | for i in range(len(subglyf)): 159 | try: 160 | subglyf[i] = [[xy[0]*xscale+xof, xy[1]*yscale+yof] for xy in subglyf[i]] 161 | except: 162 | self.log("subglyph failure:") 163 | self.log(subglyf) 164 | self.log(len(subglyf)) 165 | self.log(subglyf[i]) 166 | polylines += subglyf 167 | 168 | return {"rect":bbox, "poly":polylines} 169 | 170 | def compileAllGlyphs(self): 171 | self.glyphData = [] 172 | for i in xrange(self.ttf.length): 173 | self.glyphData.append(self.compileGlyph(i)) 174 | 175 | 176 | -------------------------------------------------------------------------------- /truetype/ttfparser.py: -------------------------------------------------------------------------------- 1 | # TrueType font file parser 2 | # Lingdong Huang 2018 3 | # based on js from: http://stevehanov.ca/blog/index.php?id=143 4 | 5 | import numpy as np 6 | import struct 7 | 8 | 9 | class BinaryReader(): 10 | def __init__(this,arrayBuffer): 11 | this.pos = 0 12 | this.data = [struct.unpack('B', x)[0] for x in arrayBuffer] 13 | 14 | def seek(this, pos): 15 | assert(pos >=0 and pos <= len(this.data)) 16 | oldPos = this.pos 17 | this.pos = pos 18 | return oldPos 19 | 20 | def tell(this): 21 | return this.pos; 22 | 23 | def getUint8(this): 24 | assert(this.pos < len(this.data)); 25 | this.pos += 1 26 | return this.data[this.pos-1]; 27 | 28 | def getUint16(this): 29 | return ((this.getUint8() << 8) | this.getUint8()) >> 0; 30 | 31 | def getUint32(this): 32 | return this.getInt32() >> 0; 33 | 34 | def getInt16(this): 35 | result = this.getUint16(); 36 | if (result & 0x8000): 37 | result -= (1 << 16); 38 | return result; 39 | 40 | def getInt32(this): 41 | return ((this.getUint8() << 24) | 42 | (this.getUint8() << 16) | 43 | (this.getUint8() << 8) | 44 | (this.getUint8() )); 45 | 46 | def getFword(this): 47 | return this.getInt16(); 48 | 49 | def get2Dot14(this): 50 | return this.getInt16() / (1 << 14); 51 | 52 | 53 | def getFixed(this): 54 | return this.getInt32() / (1 << 16); 55 | 56 | def getString(this,length): 57 | result = ""; 58 | for i in xrange(length): 59 | result += chr(this.getUint8()); 60 | return result 61 | 62 | def getDate(this): 63 | macTime = this.getUint32() * 0x100000000 + this.getUint32(); 64 | return macTime 65 | 66 | 67 | class TTFFile(): 68 | def __init__(this, arrayBuffer): 69 | this.file = BinaryReader(arrayBuffer); 70 | this.tables = this.readOffsetTables(this.file); 71 | this.readHeadTable(this.file); 72 | this.length = this.glyphCount(); 73 | 74 | 75 | def readOffsetTables(this, file): 76 | tables = {}; 77 | this.scalarType = file.getUint32(); 78 | numTables = file.getUint16(); 79 | this.searchRange = file.getUint16(); 80 | this.entrySelector = file.getUint16(); 81 | this.rangeShift = file.getUint16(); 82 | 83 | for i in xrange(numTables): 84 | tag = file.getString(4); 85 | tables[tag] = { 86 | "checksum": file.getUint32(), 87 | "offset": file.getUint32(), 88 | "length": file.getUint32() 89 | }; 90 | 91 | if (tag != 'head'): 92 | 93 | assert(this.calculateTableChecksum(file, tables[tag]["offset"], 94 | tables[tag]["length"]) == tables[tag]["checksum"]); 95 | 96 | 97 | return tables 98 | 99 | def calculateTableChecksum(this, file, offset, length): 100 | 101 | old = file.seek(offset); 102 | _sum = 0; 103 | nlongs = ((length + 3) / 4) | 0; 104 | while ( nlongs > 0 ): 105 | nlongs -= 1 106 | _sum = (_sum + file.getUint32() & 0xffffffff) >> 0; 107 | 108 | file.seek(old); 109 | return _sum; 110 | 111 | def readHeadTable(this,file): 112 | assert("head" in this.tables); 113 | file.seek(this.tables["head"]["offset"]); 114 | 115 | this.version = file.getFixed(); 116 | this.fontRevision = file.getFixed(); 117 | this.checksumAdjustment = file.getUint32(); 118 | this.magicNumber = file.getUint32(); 119 | assert(this.magicNumber == 0x5f0f3cf5); 120 | this.flags = file.getUint16(); 121 | this.unitsPerEm = file.getUint16(); 122 | this.created = file.getDate(); 123 | this.modified = file.getDate(); 124 | this.xMin = file.getFword(); 125 | this.yMin = file.getFword(); 126 | this.xMax = file.getFword(); 127 | this.yMax = file.getFword(); 128 | this.macStyle = file.getUint16(); 129 | this.lowestRecPPEM = file.getUint16(); 130 | this.fontDirectionHint = file.getInt16(); 131 | this.indexToLocFormat = file.getInt16(); 132 | this.glyphDataFormat = file.getInt16(); 133 | 134 | def glyphCount(this): 135 | assert("maxp" in this.tables); 136 | old = this.file.seek(this.tables["maxp"]["offset"] + 4); 137 | count = this.file.getUint16(); 138 | this.file.seek(old); 139 | return count; 140 | 141 | 142 | def getGlyphOffset(this,index): 143 | assert("loca" in this.tables); 144 | table = this.tables["loca"]; 145 | file = this.file; 146 | offset, old = 0, 0 147 | 148 | if (this.indexToLocFormat == 1): 149 | old = file.seek(table["offset"] + index * 4); 150 | offset = file.getUint32(); 151 | else: 152 | old = file.seek(table["offset"] + index * 2); 153 | offset = file.getUint16() * 2; 154 | 155 | file.seek(old); 156 | return offset + this.tables["glyf"]["offset"]; 157 | 158 | 159 | def readGlyph(this, index): 160 | offset = this.getGlyphOffset(index); 161 | file = this.file; 162 | 163 | if (offset >= this.tables["glyf"]["offset"] + this.tables["glyf"]["length"]): 164 | return None 165 | 166 | assert(offset >= this.tables["glyf"]["offset"]); 167 | assert(offset < this.tables["glyf"]["offset"] + this.tables["glyf"]["length"]); 168 | 169 | file.seek(offset); 170 | 171 | glyph = { 172 | "numberOfContours": file.getInt16(), 173 | "xMin": file.getFword(), 174 | "yMin": file.getFword(), 175 | "xMax": file.getFword(), 176 | "yMax": file.getFword() 177 | }; 178 | 179 | assert(glyph["numberOfContours"] >= -1); 180 | 181 | if (glyph["numberOfContours"] == -1): 182 | this.readCompoundGlyph(file, glyph); 183 | else: 184 | this.readSimpleGlyph(file, glyph); 185 | 186 | return glyph; 187 | 188 | def readSimpleGlyph(this, file, glyph): 189 | 190 | ON_CURVE = 1 191 | X_IS_BYTE = 2 192 | Y_IS_BYTE = 4 193 | REPEAT = 8 194 | X_DELTA = 16 195 | Y_DELTA = 32 196 | 197 | glyph["type"] = "simple"; 198 | glyph["contourEnds"] = []; 199 | points = glyph["points"] = []; 200 | 201 | for i in xrange(0, glyph["numberOfContours"]): 202 | glyph["contourEnds"].append(file.getUint16()); 203 | 204 | # skip over intructions 205 | file.seek(file.getUint16() + file.tell()); 206 | 207 | if (glyph["numberOfContours"] == 0): 208 | return; 209 | 210 | numPoints = max(glyph["contourEnds"]) + 1; 211 | 212 | flags = []; 213 | 214 | i = 0 215 | while (i < numPoints): 216 | flag = file.getUint8(); 217 | flags.append(flag); 218 | points.append({ 219 | "onCurve": (flag & ON_CURVE) > 0 220 | }); 221 | 222 | if ( flag & REPEAT ): 223 | repeatCount = file.getUint8(); 224 | assert(repeatCount > 0); 225 | i += repeatCount; 226 | while( repeatCount > 0): 227 | repeatCount -= 1 228 | flags.append(flag); 229 | points.append({ 230 | "onCurve": (flag & ON_CURVE) > 0 231 | }) 232 | i += 1 233 | 234 | def readCoords(name, byteFlag, deltaFlag, min, max): 235 | value = 0; 236 | 237 | for i in xrange(numPoints): 238 | flag = flags[i]; 239 | if ( flag & byteFlag ): 240 | if ( flag & deltaFlag ): 241 | value += file.getUint8(); 242 | else: 243 | value -= file.getUint8(); 244 | elif ( (~flag) & deltaFlag ): 245 | value += file.getInt16() 246 | else: 247 | pass 248 | # value is unchanged. 249 | points[i][name] = value; 250 | 251 | readCoords("x", X_IS_BYTE, X_DELTA, glyph["xMin"], glyph["xMax"]); 252 | readCoords("y", Y_IS_BYTE, Y_DELTA, glyph["yMin"], glyph["yMax"]); 253 | 254 | def readCompoundGlyph(this, file, glyph): 255 | ARG_1_AND_2_ARE_WORDS = 1 256 | ARGS_ARE_XY_VALUES = 2 257 | ROUND_XY_TO_GRID = 4 258 | WE_HAVE_A_SCALE = 8 259 | # RESERVED = 16 260 | MORE_COMPONENTS = 32 261 | WE_HAVE_AN_X_AND_Y_SCALE = 64 262 | WE_HAVE_A_TWO_BY_TWO = 128 263 | WE_HAVE_INSTRUCTIONS = 256 264 | USE_MY_METRICS = 512 265 | OVERLAP_COMPONENT = 1024 266 | 267 | glyph["type"] = "compound"; 268 | glyph["components"] = []; 269 | 270 | flags = MORE_COMPONENTS; 271 | while( flags & MORE_COMPONENTS ): 272 | arg1, arg2 = 0, 0 273 | 274 | flags = file.getUint16(); 275 | component = { 276 | "glyphIndex": file.getUint16(), 277 | "matrix": { 278 | 'a': 1, 'b': 0, 'c': 0, 'd': 1, 'e': 0, 'f': 0 279 | }, 280 | "flags":{ 281 | "ARGS_ARE_XY_VALUES": bool(flags & ARGS_ARE_XY_VALUES), 282 | "WE_HAVE_A_SCALE": bool(flags & WE_HAVE_A_SCALE), 283 | "WE_HAVE_AN_X_AND_Y_SCALE": bool(flags & WE_HAVE_AN_X_AND_Y_SCALE), 284 | "WE_HAVE_A_TWO_BY_TWO": bool(flags & WE_HAVE_A_TWO_BY_TWO), 285 | } 286 | }; 287 | 288 | if ( flags & ARG_1_AND_2_ARE_WORDS ): 289 | arg1 = file.getInt16(); 290 | arg2 = file.getInt16(); 291 | else: 292 | arg1 = file.getUint8(); 293 | arg2 = file.getUint8(); 294 | 295 | if ( flags & ARGS_ARE_XY_VALUES ): 296 | component["matrix"]["e"] = arg1; 297 | component["matrix"]["f"] = arg2; 298 | else: 299 | component.destPointIndex = arg1; 300 | component.srcPointIndex = arg2; 301 | 302 | if ( flags & WE_HAVE_A_SCALE ): 303 | component["matrix"]['a'] = file.get2Dot14(); 304 | component["matrix"]['d'] = component["matrix"]['a']; 305 | elif ( flags & WE_HAVE_AN_X_AND_Y_SCALE ): 306 | component["matrix"]['a'] = file.get2Dot14(); 307 | component["matrix"]['d'] = file.get2Dot14(); 308 | elif ( flags & WE_HAVE_A_TWO_BY_TWO ): 309 | component["matrix"]['a'] = file.get2Dot14(); 310 | component["matrix"]['b'] = file.get2Dot14(); 311 | component["matrix"]['c'] = file.get2Dot14(); 312 | component["matrix"]['d'] = file.get2Dot14(); 313 | glyph["components"].append(component); 314 | #print glyph["components"] 315 | if ( flags & WE_HAVE_INSTRUCTIONS ): 316 | file.seek(file.getUint16() + file.tell()); 317 | 318 | 319 | -------------------------------------------------------------------------------- /ttf/Ubuntu.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LingDong-/ttf2hershey/9877eba3735a7bdfec230fe42ce4bcb8154ae67d/ttf/Ubuntu.ttf -------------------------------------------------------------------------------- /ttf2hershey.py: -------------------------------------------------------------------------------- 1 | # Convert True Type Fonts (.ttf) to Hershey Fonts 2 | # (c) Lingdong Huang 2018 3 | 4 | # Info on Hershey Fonts: 5 | # - https://en.wikipedia.org/wiki/Hershey_fonts 6 | # - http://paulbourke.net/dataformats/hershey/ 7 | 8 | import sys 9 | from truetype.truetype import * 10 | 11 | def tohershey(text,font_path="ttf/ubuntu.ttf",kern=0,verbose=True): 12 | ttf = TrueTypeFont(font_path,verbose=verbose) 13 | outer = ttf.ttf.xMin, ttf.ttf.yMin, ttf.ttf.xMax, ttf.ttf.yMax 14 | cmax = (36-kern)*2 15 | umax = max( abs(outer[2]-outer[0]), 16 | abs(outer[3]-outer[1]), 17 | abs(outer[3]-ttf.baseline), 18 | abs(outer[1]-ttf.baseline) 19 | ) 20 | scale = float(cmax)/umax 21 | result = "" 22 | 23 | x_xmin,_,x_xmax,_ = ttf.glyphData[ttf.chr2idx('x')]['rect'] 24 | x_xcent = (x_xmin+x_xmax)/2.0 25 | 26 | for i in range(len(text)): 27 | ch = text[i] 28 | index = ttf.chr2idx(ch) 29 | 30 | polylines, (xmin,ymin,xmax,ymax) = ttf.glyphData[index]['poly'], \ 31 | ttf.glyphData[index]['rect'] 32 | width = xmax - xmin 33 | height = ymax - ymin 34 | 35 | xcent = xmin + width/2.0 36 | ycent = ymin + height/2.0 37 | 38 | if ch == " ": 39 | result += str(ord(ch)).rjust(5)+str(2).rjust(3) 40 | result += chr(int((x_xmin-x_xcent)/2.0*scale)+ord('R'))\ 41 | +chr(int((x_xmax-x_xcent)/2.0*scale)+ord('R')) 42 | result += "\n" 43 | continue 44 | 45 | res = chr(int(round((xmin-xcent)*scale))+ord('R')-kern)\ 46 | +chr(int(round((xmax-xcent)*scale))+ord('R')+kern) 47 | for i in range(len(polylines)): 48 | for _j in range(len(polylines[i])+1): 49 | j = _j%len(polylines[i]) 50 | x,y = polylines[i][j] 51 | x = x-xcent 52 | y = -(y-ttf.baseline) 53 | xs = chr(int(round(x*scale))+ord('R')) 54 | ys = chr(int(round(y*scale))+ord('R')) 55 | res += xs+ys 56 | if i != len(polylines)-1: 57 | res += " R" 58 | result += str(ord(ch)).rjust(5)+str(len(res)).rjust(3)+res+"\n" 59 | print result 60 | return result 61 | 62 | if __name__ == "__main__": 63 | input_path = sys.argv[1] 64 | characters = "".join([chr(i) for i in range(32,128)]) 65 | print tohershey(characters,font_path=input_path,verbose=False) 66 | 67 | 68 | 69 | --------------------------------------------------------------------------------