47 Commits
Author SHA1 Message Date
Idrees Hassan 99b32ab406 Add font files to repository for CDN access 2025-01-02 12:17:30 -05:00
Idrees Hassan 9951196477 Revert "Update socials link"
This reverts commit 0034e018aa.
2024-11-15 21:36:17 -05:00
Idrees Hassan 0034e018aa Update socials link 2024-11-15 21:15:50 -05:00
Idrees Hassan 69c175192a Update window title 2024-11-15 18:27:38 -05:00
Idrees Hassan 270bbfb3ab Merge branch 'main' of https://github.com/IdreesInc/Monocraft 2024-08-16 14:53:58 -04:00
Idrees Hassan 63e481ba29 Update index.md 2024-08-16 14:53:52 -04:00
Idrees 7893cb1659 Create CNAME 2024-08-16 14:28:32 -04:00
Idrees Hassan 4d9c1386d7 Add project site 2024-08-16 14:27:22 -04:00
Idrees 0d88c49c2c Merge pull request #148 from IdreesInc/v4
V4 - New font weights, small fixes
2024-08-10 16:53:41 -04:00
Idrees 785603ba93 Merge pull request #136 from eminforks/main
addition of latin small letter dotless i
2024-08-10 16:51:31 -04:00
Idrees 1f972371d1 Merge pull request #139 from ya0guang/patch-1
Change variable name
2024-08-10 16:49:24 -04:00
Idrees 16ded48b20 Merge pull request #130 from Ciubix8513/html-commment-ligature
Add html comment ligature
2024-08-10 16:46:45 -04:00
Idrees 696ada3de7 Merge pull request #128 from Dheatly23/bold-italic
Adds Bold, Italic, and Thin Variants
2024-08-10 16:44:23 -04:00
Idrees Hassan 5db1c0abde Update README.md 2024-08-10 16:44:09 -04:00
Idrees Hassan ee1ca3a096 Update README.md 2024-08-10 16:01:49 -04:00
Idrees Hassan f4ef05670f Update generated file names and version 2024-08-10 15:58:54 -04:00
Idrees 8555032acc Update README.md 2024-06-28 17:59:06 -04:00
Hongbo dfccf446e5 enhance the variable naming to avoid confusion 2024-03-27 22:43:11 -04:00
Idrees 74d9255a80 Update README.md 2024-02-09 17:04:13 -05:00
Idrees f825d5211d Update README.md 2024-02-09 17:04:01 -05:00
Emin Arıcan 5d00a3c3bd latin small letter dotless i 2024-01-13 13:33:20 +03:00
Dheatly23 0d1c8192b8 Reduce Black Boldness 2024-01-08 10:25:57 +07:00
Dheatly23 ed296701fe Port Features 2023-12-24 17:19:25 +07:00
Luna b4294f637b Add html comment ligature 2023-07-07 18:08:49 +03:00
Dheatly23 1ee5a40d6e Reduce Boldness 2023-06-23 20:52:34 +07:00
Dheatly23 5ec7862f57 Adds Bold Italic and Collect Fonts into .ttc 2023-06-23 20:52:16 +07:00
Dheatly23 5bbe9790da Fix Bold Font 2023-06-19 20:24:45 +07:00
Dheatly23 dc7fc21780 Speedup on Bold Variant 2023-06-19 14:45:26 +07:00
Dheatly23 00037d85de Adds Bold and Italic Variant 2023-06-19 13:30:56 +07:00
Idrees e79ff56105 Update README.md 2023-06-10 14:24:54 -04:00
Idrees ec2b8014ad Merge pull request #125 from IdreesInc/v3
V3
2023-06-10 14:24:08 -04:00
Idrees Hassan 9a9e4c8d83 Update readme with new glyphs 2023-06-10 14:18:56 -04:00
Idrees Hassan d4ea373b62 Remove soft hyphen 2023-06-10 14:12:16 -04:00
Idrees Hassan ac2252b81c Update code style 2023-06-10 13:38:20 -04:00
Idrees Hassan bd29dc05a9 Merge branch 'v3-beta' into v3 2023-06-10 13:17:52 -04:00
Idrees 7da29e1173 Merge pull request #116 from Ciubix8513/progress-bar-ligatures
Added continuous ligatures
2023-06-10 13:15:21 -04:00
Luna e10bf402dc Fix typo 2023-05-28 22:23:58 +03:00
Luna d44916965e Fix type continuos -> continuous 2023-05-28 06:13:10 +03:00
Luna c0cabc34e9 Fix incorect continious ligature length 2023-05-11 16:20:56 +03:00
Luna 3080f38f80 Add plus sign continious ligature 2023-05-11 15:58:01 +03:00
Luna 7ef1429d17 Added wavy underline ligature 2023-03-09 15:21:48 +03:00
Luna 9c26e52eb5 Renamed to continuos ligatures 2023-03-07 22:38:11 +03:00
Luna 267882c42d Added more bars, removed debug 2023-03-07 22:33:16 +03:00
Luna 04245a37e7 Added progress bar generation to monocraft, adjusted ==> 2023-03-07 22:21:42 +03:00
Luna f33b99b2b2 Fixed, to work properly 2023-03-07 21:43:31 +03:00
Luna 7f8d16087e Improved documentation(thank you chatGPT), tried to optimize 2023-03-07 20:29:35 +03:00
Luna d994da27d7 Added generate progress bar function 2023-03-07 18:49:25 +03:00
44 changed files with 1430 additions and 267 deletions

No files matched your search

+3 -2
View File
@@ -1,5 +1,6 @@
.DS_Store
src/__pycache__
dist/
build/
build/
docs/_site/
docs/.jekyll-cache/
+13 -5
View File
@@ -4,10 +4,16 @@
![](https://img.shields.io/github/license/IdreesInc/Monocraft)
[![](https://img.shields.io/github/v/release/IdreesInc/Monocraft)](https://GitHub.com/IdreesInc/Monocraft/releases/)
## [`Download it here!`](https://github.com/IdreesInc/Monocraft/releases)
<br/>
![](images/preview.png)
The monospaced font for developers who like Minecraft a bit _too_ much.
If you'd like to see a vectorized version of this font, try [Miracode](https://github.com/IdreesInc/Miracode)!
*Notice: This project is not affiliated with Minecraft or Mojang in any way and is exclusively a fan project. This font emulates the typeface of the font used in the Minecraft UI, but it does not include any assets or font files from the original game.*
## Features
@@ -15,7 +21,7 @@ The monospaced font for developers who like Minecraft a bit _too_ much.
- Minecraft!
- The characters in this font were based around the [typeface](https://github.com/IdreesInc/Minecraft-Font) used in the Minecraft UI, with each glyph updated for better readability and spacing
- Monospaced!
- Each of the 500+ glyphs included in this font have been carefully redesigned to work in a monospaced font
- Each of the 1500+ glyphs included in this font have been carefully redesigned to work in a monospaced font
- Thin characters like "i" and "l" have been reworked with tasteful tails and serifs to look better in a monospaced environment
- Programming ligatures!
- Add some spice to your programming life with all new ligature characters
@@ -29,7 +35,7 @@ The monospaced font for developers who like Minecraft a bit _too_ much.
### Windows
Download the most recent `Monocraft.ttf` file from the [Releases](https://github.com/IdreesInc/Monocraft/releases) page. Right click on the downloaded font and select **Install**. You might need administrative access to install fonts, depending on your machine.
Download the most recent `Monocraft.ttc` file from the [Releases](https://github.com/IdreesInc/Monocraft/releases) page. Right click on the downloaded font and select **Install**. You might need administrative access to install fonts, depending on your machine.
### Mac
@@ -42,11 +48,11 @@ brew install --cask font-monocraft
#### Manually
Download the most recent `Monocraft.ttf` file from the [Releases](https://github.com/IdreesInc/Monocraft/releases) page. Double click on the downloaded font file and select **Install Font** in the window that appears. More help available [here](https://support.apple.com/en-us/HT201749).
Download the most recent `Monocraft.ttc` file from the [Releases](https://github.com/IdreesInc/Monocraft/releases) page. Double click on the downloaded font file and select **Install Font** in the window that appears. More help available [here](https://support.apple.com/en-us/HT201749).
### Linux
Download the most recent `Monocraft.ttf` file from the [Releases](https://github.com/IdreesInc/Monocraft/releases) page. Move the file to ~/.local/share/fonts (create the folder if it doesn't already exist). In a terminal, run `fc-cache -fv`. Alternatively, log out and log in again.
Download the most recent `Monocraft.ttc` file from the [Releases](https://github.com/IdreesInc/Monocraft/releases) page. Move the file to ~/.local/share/fonts (create the folder if it doesn't already exist). In a terminal, run `fc-cache -fv`. Alternatively, log out and log in again.
## How to use
@@ -60,6 +66,8 @@ So far, the following ligatures have been added to the font:
<img src="images/ligatures.png" width="300">
And with the contributions of [@Ciubix8513](https://github.com/Ciubix8513), Monocraft now includes continuous ligatures that enable you to type arrows and lines that _just keep going_.
If there is another character combination that you feel could be a ligature, feel free to create an issue!
### How are these characters generated?
@@ -72,4 +80,4 @@ I've got you covered, check out my rendition of the Minecraft typeface [here](ht
### What if I want to use a font that's actually good?
Understandable, check out [Fira Code](https://github.com/tonsky/FiraCode) for a font with amazing attention to detail, or [Scientifica](https://github.com/nerdypepper/scientifica) for a bitmap font similar to this.
Understandable, check out [Fira Code](https://github.com/tonsky/FiraCode) for a font with amazing attention to detail, or [Scientifica](https://github.com/nerdypepper/scientifica) for a bitmap font similar to this. Feel free to also check out my font [Miracode](https://github.com/IdreesInc/Miracode) for a font based on Monocraft that's a little more usable!
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
monocraft.idreesinc.com
+3
View File
@@ -0,0 +1,3 @@
gem "jekyll"
gem "webrick"
gem "jekyll-remote-theme"
+78
View File
@@ -0,0 +1,78 @@
GEM
specs:
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
colorator (1.1.0)
concurrent-ruby (1.3.4)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
eventmachine (1.2.7)
ffi (1.17.0-arm64-darwin)
forwardable-extended (2.6.0)
http_parser.rb (0.8.0)
i18n (1.8.11)
concurrent-ruby (~> 1.0)
jekyll (4.3.3)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 1.0)
jekyll-sass-converter (>= 2.0, < 4.0)
jekyll-watch (~> 2.0)
kramdown (~> 2.3, >= 2.3.1)
kramdown-parser-gfm (~> 1.0)
liquid (~> 4.0)
mercenary (>= 0.3.6, < 0.5)
pathutil (~> 0.9)
rouge (>= 3.0, < 5.0)
safe_yaml (~> 1.0)
terminal-table (>= 1.8, < 4.0)
webrick (~> 1.7)
jekyll-remote-theme (0.4.3)
addressable (~> 2.0)
jekyll (>= 3.5, < 5.0)
jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
rubyzip (>= 1.3.0, < 3.0)
jekyll-sass-converter (2.1.0)
sassc (> 2.0.1, < 3.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
kramdown (2.3.1)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.3)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (4.0.7)
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
rexml (3.3.5)
strscan
rouge (3.26.1)
rubyzip (2.3.2)
safe_yaml (1.0.5)
sassc (2.4.0)
ffi (~> 1.9)
strscan (3.1.0)
terminal-table (2.0.0)
unicode-display_width (~> 1.1, >= 1.1.1)
unicode-display_width (1.8.0)
webrick (1.7.0)
PLATFORMS
arm64-darwin
DEPENDENCIES
jekyll
jekyll-remote-theme
webrick
BUNDLED WITH
2.5.17
+3
View File
@@ -0,0 +1,3 @@
remote_theme: IdreesInc/Jekyll-Project-Theme
plugins:
- jekyll-remote-theme
+24
View File
@@ -0,0 +1,24 @@
---
window_title: "Get Monocraft - The Minecraft Font for Devs"
title: "Monocraft"
subtitle: "the programming font for the minecraft obsessed"
annotation: "*plus, it has ligatures!"
source_code: "https://github.com/IdreesInc/Monocraft"
background: "linear-gradient(90deg, #f5e1c4, #daf0d3, #BCE7FF)"
layout: "project"
---
{% include link.html
url="https://github.com/IdreesInc/Monocraft/releases"
text="download it here"
%}
{% include link.html
url="https://arstechnica.com/gaming/2022/09/developer-creates-delightful-programming-font-based-on-minecraft/"
text="in the press"
%}
{% include link.html
url="https://socials.idreesinc.com"
text="follow for updates"
%}
+139
View File
@@ -0,0 +1,139 @@
@import url('https://fonts.googleapis.com/css2?family=Epilogue:wght@400;500&display=swap');
html {
font-family: 'Epilogue', sans-serif;
}
html, body {
margin: 0;
padding: 0;
}
body {
display: flex;
flex-direction: column;
color: black;
}
#foil {
position: absolute;
z-index: -1;
width: 60%;
height: 15%;
left: 15%;
top: 30%;
transform: rotate(-20deg);
background-image: url('foil.jpg');
background-size: cover;
background-position: center;
}
#intro-page {
height: 100vh;
width: 65%;
margin-left: 30%;
display: flex;
flex-direction: column;
justify-content: center;
}
#title {
font-size: 100px;
}
#subtitle {
font-size: 35px;
margin-top: 25px;
}
#links {
font-family: Epilogue;
font-size: 30px;
margin-top: 30px;
text-decoration-line: underline;
}
.link {
margin-bottom: 20px;
}
.link a {
color: black;
text-decoration: none;
}
#github a {
color: black;
}
#github {
margin-top: 10px;
}
#annotation {
font-size: 20px;
position: absolute;
bottom: 40px;
right: 40px;
margin-left: 20px;
}
#me {
font-size: 16px;
position: absolute;
bottom: 12px;
right: 40px;
margin-left: 20px;
}
#me a {
color: black;
}
@media screen and (orientation:portrait) and (max-width: 85vh) {
#foil {
width: 90%;
height: 11%;
left: 5%;
top: 27%;
transform: rotate(-20deg);
}
#intro-page {
width: 80%;
margin-top: -10%;
margin-left: 10%;
}
#title {
font-size: 16vw;
}
#subtitle {
font-size: 6.4vw;
margin-top: 50px;
}
#links {
font-size: 6vw;
margin-top: 110px;
}
.link {
margin-bottom: 50px;
}
#annotation {
font-size: 3vw;
position: absolute;
bottom: 6vw;
right: 25px;
}
#me {
font-size: 3vw;
position: absolute;
bottom: 1.5vw;
right: 25px;
}
}
+239 -44
View File
@@ -6,53 +6,38 @@ a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9
! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À
Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à
á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ Ā
ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Ē ē Ĕ ĕ Ė ė Ę ę Ě ě Ĝ ĝ Ğ ğ Ġ ġ Ģ
ģ Ĥ ĥ Ĩ ĩ Ī ī Ĭ ĭ İ IJ ij Ĵ ĵ Ķ ķ ĸ Ĺ ĺ Ļ ļ Ľ ľ Ł ł Ń ń Ņ ņ Ň ň Ō
ō Ŏ ŏ Ő ő Œ œ Ŕ ŕ Ř ř Ś ś Ŝ ŝ Ş ş Š š Ť ť Ũ ũ Ū ū Ŭ ŭ Ů ů Ű ű Ŵ
¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ
Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ Ā ā
Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Ē ē Ĕ ĕ Ė ė Ę ę Ě ě Ĝ ĝ Ğ ğ Ġ ġ Ģ ģ Ĥ ĥ Ĩ ĩ Ī ī Ĭ ĭ İ ı IJ ij Ĵ ĵ Ķ ķ
ĸ Ĺ ĺ Ļ ļ Ľ ľ Ł ł Ń ń Ņ ņ Ň ň Ō ō Ŏ ŏ Ő ő Œ œ Ŕ ŕ Ř ř Ś ś Ŝ ŝ Ş ş Š š Ť ť Ũ ũ Ū ū Ŭ ŭ Ů ů Ű ű Ŵ
ŵ Ŷ ŷ Ÿ Ź ź Ż ż Ž
ž ſ Ɓ Ƅ ƅ Ɔ Ɖ Ǝ Ə Ɛ Ƒ ƚ ƛ ƞ Ɵ Ƨ ƨ Ʃ ƪ Ʋ Ʒ Ƹ ƹ ƻ Ƽ ƽ ƾ ƿ ǀ ǁ ǂ ǃ
lj Ǎ ǎ Ǐ ǐ Ǒ ǒ Ǔ ǔ ǝ Ǣ ǣ Ǧ ǧ Ǩ ǩ Ǯ ǯ ǰ Ǵ ǵ Ƿ Ǹ ǹ Ǽ ǽ Ș ș Ț ț Ȝ ȝ
Ȟ ȟ Ƞ ȡ Ȥ ȥ Ȧ ȧ Ȯ ȯ Ȳ ȳ ȴ ȵ ȶ Ⱥ Ȼ ȼ Ƚ Ⱦ Ɂ ɂ Ƀ Ʉ Ʌ Ɇ ɇ Ɉ ɉ Ɋ ɋ Ɍ
ɍ Ɏ ɏ ɐ ɑ ɒ ɓ ɔ ɕ ɖ ɗ ɘ ə ɚ ɛ ɜ ɝ ɞ ɟ ɠ ɡ ɢ ɤ ɥ ɦ ɧ ɨ ɪ ɫ ɬ ɭ ɮ
ɯ ɰ ɱ ɲ ɳ ɴ ɵ ɷ ɸ ɹ ɺ ɻ ɼ ɾ ɿ ʀ ʁ ʄ ʅ ʆ ʇ ʈ ʉ ʊ ʋ ʌ ʍ ʎ ʏ ʑ ʒ ʓ
ʔ ʕ ʖ ʗ ʘ ʙ ʚ ʛ ʜ ʝ ʞ ʟ ʠ ʡ ʢ ʬ ʻ ˌ ˙ ; · Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ
Μ Ν Ξ Ο Π Ρ Σ Τ Υ Φ Χ Ψ Ω α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ ς σ
τ υ φ χ ψ ω ϛ Ѐ Ё Ђ Ѓ Є Ѕ І Ї Ј Љ Њ Ћ Ќ Ѝ Ў Џ А Б В Г Д Е Ж З И
Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я а б в г д е ж з и
й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я ѐ ё ђ ѓ є ѕ і ї ј
љ њ ћ ќ ѝ ў џ
ž ſ Ɓ Ƅ ƅ Ɔ Ɖ Ǝ Ə Ɛ Ƒ ƚ ƛ ƞ Ɵ Ƨ ƨ Ʃ ƪ Ʋ Ʒ Ƹ ƹ ƻ Ƽ ƽ ƾ ƿ ǀ ǁ ǂ ǃ lj Ǎ ǎ Ǐ ǐ Ǒ ǒ Ǔ ǔ ǝ Ǣ ǣ Ǧ ǧ Ǩ ǩ
Ǯ ǯ ǰ Ǵ ǵ Ƿ Ǹ ǹ Ǽ ǽ Ș ș Ț ț Ȝ ȝ Ȟ ȟ Ƞ ȡ Ȥ ȥ Ȧ ȧ Ȯ ȯ Ȳ ȳ ȴ ȵ ȶ Ⱥ Ȼ ȼ Ƚ Ⱦ Ɂ ɂ Ƀ Ʉ Ʌ Ɇ ɇ Ɉ ɉ Ɋ ɋ Ɍ
ɍ Ɏ ɏ ɐ ɑ ɒ ɓ ɔ ɕ ɖ ɗ ɘ ə ɚ ɛ ɜ ɝ ɞ ɟ ɠ ɡ ɢ ɤ ɥ ɦ ɧ ɨ ɪ ɫ ɬ ɭ ɮ ɯ ɰ ɱ ɲ ɳ ɴ ɵ ɷ ɸ ɹ ɺ ɻ ɼ ɾ ɿ ʀ
ʁ ʄ ʅ ʆ ʇ ʈ ʉ ʊ ʋ ʌ ʍ ʎ ʏ ʑ ʒ ʓ ʔ ʕ ʖ ʗ ʘ ʙ ʚ ʛ ʜ ʝ ʞ ʟ ʠ ʡ ʢ ʬ ʻ ˌ ˙ ; · Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ
Μ Ν Ξ Ο Π Ρ Σ Τ Υ Φ Χ Ψ Ω α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ ς σ τ υ φ χ ψ ω ϛ Ѐ Ё Ђ Ѓ Є Ѕ І Ї Ј
Љ Њ Ћ Ќ Ѝ Ў Џ А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я а б в г д е ж з и
й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я ѐ ё ђ ѓ є ѕ і ї ј љ њ ћ ќ ѝ ў џ
Ѣ ѣ Ѳ ѳ Ѵ ѵ Ғ ғ Җ җ Қ қ Ң ң Ү ү Ұ ұ Һ һ Ӏ Ӂ ӂ Ӑ ӑ Ӓ ӓ Ӗ ӗ Ә ә Ӛ
ӛ Ӝ ӝ Ӟ ӟ Ӣ ӣ Ӥ ӥ Ӧ ӧ Ө ө Ӫ ӫ Ӭ ӭ Ӯ ӯ Ӱ ӱ Ӳ ӳ Ӵ ӵ Ӹ ӹ Ԁ Ա Բ Գ Դ
Ե Զ Է Ը Թ Ժ Ի Լ Խ Ծ Կ Հ Ձ Ղ Ճ Մ Յ Ն Շ Ո Չ Պ Ջ Ռ Ս Վ Տ Ր Ց Ւ Փ Ք
Օ Ֆ ՙ ՚ ՛ ՜ ՝ ՞ ՟ ՠ ա բ գ դ ե զ է ը թ ժ ի լ խ ծ կ հ ձ ղ ճ մ յ ն
շ ո չ պ ջ ռ ս վ տ ր ց ւ փ ք օ ֆ և ֈ ֊ ֏ ־ ׃ ׆ א ב ג ד ה ו ז ח ט
י ך כ ל ם מ ן נ ס ע ף פ ץ צ ק ר ש ת װ ױ ײ ׳ ״ ߈ Ⴀ Ⴁ Ⴂ Ⴃ Ⴄ Ⴅ Ⴆ Ⴇ
Ⴈ Ⴉ Ⴊ Ⴋ Ⴌ Ⴍ Ⴎ Ⴏ Ⴐ Ⴑ Ⴒ Ⴓ Ⴔ Ⴕ Ⴖ Ⴗ Ⴘ Ⴙ Ⴚ Ⴛ Ⴜ Ⴝ Ⴞ Ⴟ Ⴠ Ⴡ Ⴢ Ⴣ Ⴤ Ⴥ Ⴧ Ⴭ
ა ბ გ დ ე ვ ზ თ ი კ ლ მ ნ ო პ ჟ რ ს ტ უ ფ ქ ღ ყ შ ჩ ც ძ წ ჭ ხ ჯ
ჰ ჱ ჲ ჳ ჴ ჵ ჶ ჷ ჸ ჹ ჺ ჻ ჼ ჽ ჾ ჿ ᗡ ᗺ ᘔ ᚠ ᚢ ᚣ ᚤ ᚥ ᚦ ᚧ ᚨ ᚩ ᚪ ᚫ ᚬ ᚭ
ᚮ ᚯ ᚰ ᚱ ᚲ ᚳ ᚴ ᚶ ᚷ ᚸ ᚹ ᚺ ᚻ ᚼ ᚽ ᚾ ᚿ ᛀ ᛁ ᛂ ᛃ ᛄ ᛅ ᛆ ᛇ ᛈ ᛉ ᛊ ᛋ ᛌ ᛍ ᛎ
ᛏ ᛐ ᛑ ᛒ ᛓ ᛔ ᛕ ᛖ ᛗ ᛘ ᛙ ᛚ ᛛ ᛜ ᛝ ᛞ ᛟ ᛠ ᛡ ᛢ ᛣ ᛤ ᛥ ᛦ ᛧ ᛨ ᛩ ᛪ ᛫ ᛬ ᛭ ᛮ
ᛯ ᛰ ᛱ ᛲ ᛳ ᛴ ᛵ ᛶ ᛷ ᛸ ᴀ ᴄ ᴅ ᴇ ᴉ ᴊ ᴋ ᴍ ᴏ ᴘ ᴚ ᴛ ᴜ ᴠ ᴡ ᴢ ᵷ ᶑ Ḃ ḃ Ḋ ḋ
Ḟ ḟ Ḡ ḡ Ḣ ḣ Ḧ ḧ Ḱ ḱ Ḿ ḿ Ṁ ṁ Ṅ ṅ Ṕ ṕ Ṗ ṗ Ṙ ṙ Ṡ ṡ Ṫ ṫ Ṽ ṽ Ẁ ẁ Ẃ ẃ
Ẅ ẅ Ẇ ẇ Ẋ ẋ Ẍ ẍ Ẏ ẏ Ẑ ẑ ẗ ẘ ẙ ẛ ẜ ẝ ẞ ẟ Ẽ ẽ Ỳ ỳ Ỹ ỹ Ỽ ỽ Ỿ ỿ ᾱ Ᾱ
ῑ Ῑ ῡ Ῡ ‑ – — ‘ ’ ‚ “ ” „ † ‡ • … ‰ ‱ ′ ″ ‴ ‵ ‶ ‷ ‹ › ※ ‼ ‽ ⁂ ⁈
⁉ ⁊ ⁋ ⁎ ⁏ ⁑ ⁒ ⁗ ⁰ ⁱ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ⁽ ⁾ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₊
₋ ₌ ₍ ₎ ₠ ₡ ₢ ₣ ₤ ₥ ₦ ₩ ₪
Ѣ ѣ Ѳ ѳ Ѵ ѵ Ғ ғ Җ җ Қ қ Ң ң Ү ү Ұ ұ Һ һ Ӏ Ӂ ӂ Ӑ ӑ Ӓ ӓ Ӗ ӗ Ә ә Ӛ ӛ Ӝ ӝ Ӟ ӟ Ӣ ӣ Ӥ ӥ Ӧ ӧ Ө ө Ӫ ӫ Ӭ
ӭ Ӯ ӯ Ӱ ӱ Ӳ ӳ Ӵ ӵ Ӹ ӹ Ԁ Ա Բ Գ Դ Ե Զ Է Ը Թ Ժ Ի Լ Խ Ծ Կ Հ Ձ Ղ Ճ Մ Յ Ն Շ Ո Չ Պ Ջ Ռ Ս Վ Տ Ր Ց Ւ Փ Ք
Օ Ֆ ՙ ՚ ՛ ՜ ՝ ՞ ՟ ՠ ա բ գ դ ե զ է ը թ ժ ի լ խ ծ կ հ ձ ղ ճ մ յ ն շ ո չ պ ջ ռ ս վ տ ր ց ւ փ ք օ ֆ
և ֈ ֊ ֏ ־ ׃ ׆ א ב ג ד ה ו ז ח ט י ך כ ל ם מ ן נ ס ע ף פ ץ צ ק ר ש ת װ ױ ײ ׳ ״ ߈ Ⴀ Ⴁ Ⴂ Ⴃ Ⴄ Ⴅ Ⴆ Ⴇ
Ⴈ Ⴉ Ⴊ Ⴋ Ⴌ Ⴍ Ⴎ Ⴏ Ⴐ Ⴑ Ⴒ Ⴓ Ⴔ Ⴕ Ⴖ Ⴗ Ⴘ Ⴙ Ⴚ Ⴛ Ⴜ Ⴝ Ⴞ Ⴟ Ⴠ Ⴡ Ⴢ Ⴣ Ⴤ Ⴥ Ⴧ Ⴭ ა ბ გ დ ე ვ ზ თ ი კ ლ მ ნ ო პ ჟ
რ ს ტ უ ფ ქ ღ ყ შ ჩ ც ძ წ ჭ ხ ჯ ჰ ჱ ჲ ჳ ჴ ჵ ჶ ჷ ჸ ჹ ჺ ჻ ჼ ჽ ჾ ჿ ᗡ ᗺ ᘔ ᚠ ᚢ ᚣ ᚤ ᚥ ᚦ ᚧ ᚨ ᚩ ᚪ ᚫ ᚬ ᚭ
ᚮ ᚯ ᚰ ᚱ ᚲ ᚳ ᚴ ᚶ ᚷ ᚸ ᚹ ᚺ ᚻ ᚼ ᚽ ᚾ ᚿ ᛀ ᛁ ᛂ ᛃ ᛄ ᛅ ᛆ ᛇ ᛈ ᛉ ᛊ ᛋ ᛌ ᛍ ᛎ ᛏ ᛐ ᛑ ᛒ ᛓ ᛔ ᛕ ᛖ ᛗ ᛘ ᛙ ᛚ ᛛ ᛜ ᛝ ᛞ
ᛟ ᛠ ᛡ ᛢ ᛣ ᛤ ᛥ ᛦ ᛧ ᛨ ᛩ ᛪ ᛫ ᛬ ᛭ ᛮ ᛯ ᛰ ᛱ ᛲ ᛳ ᛴ ᛵ ᛶ ᛷ ᛸ ᴀ ᴄ ᴅ ᴇ ᴉ ᴊ ᴋ ᴍ ᴏ ᴘ ᴚ ᴛ ᴜ ᴠ ᴡ ᴢ ᵷ ᶑ Ḃ ḃ Ḋ ḋ
Ḟ ḟ Ḡ ḡ Ḣ ḣ Ḧ ḧ Ḱ ḱ Ḿ ḿ Ṁ ṁ Ṅ ṅ Ṕ ṕ Ṗ ṗ Ṙ ṙ Ṡ ṡ Ṫ ṫ Ṽ ṽ Ẁ ẁ Ẃ ẃ Ẅ ẅ Ẇ ẇ Ẋ ẋ Ẍ ẍ Ẏ ẏ Ẑ ẑ ẗ ẘ ẙ ẛ
ẜ ẝ ẞ ẟ Ẽ ẽ Ỳ ỳ Ỹ ỹ Ỽ ỽ Ỿ ỿ ᾱ Ᾱ ῑ Ῑ ῡ Ῡ ‑ – — ‘ ’ ‚ “ ” „ † ‡ • … ‰ ‱ ′ ″ ‴ ‵ ‶ ‷ ‹ › ※ ‼ ‽ ⁂ ⁈
⁉ ⁊ ⁋ ⁎ ⁏ ⁑ ⁒ ⁗ ⁰ ⁱ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ⁽ ⁾ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₊ ₋ ₌ ₍ ₎ ₠ ₡ ₢ ₣ ₤ ₥ ₦ ₩ ₪
₫ € ₭ ₮ ₰ ₱ ₲ ₳ ₴ ₵ ₶ ₷ ₸ ₹ ₺ ₻ ₼ ₽ ₾ ₿ ℗ ™ Ⅎ ⅁ ⅄ ⅋ ⅐ ⅑ ⅓ ⅔ ⅕ ⅖
⅗ ⅙ ⅚ ⅛ ⅜ ⅝ ⅞ ⅟ ↉ ← ↑ → ↓ ↔ ⇄ ⇏ ⇐ ⇑ ⇒ ⇓ ⇔ ⇵ ∀ ∁ ∂ ∃ ∄ ∉ ∋ ∌ ∑ −
∓ ∛ ∜ ∝ ∞ ∥ ∧ ∨ ∩ ∫ ∮ ∴ ∵ ∷ ≔ ≠ ≢ ⊂ ⊃ ⊄ ⊅ ⊆ ⊇ ⊢ ⊤ ⊥ ⊨ ⊻ ⊼ ⊽ ⋃ ⋆
⌀ ⌂ ⌘ ⌚ ⌛ ⏏ ⏩ ⏪ ⏭ ⏮ ⏯ ⏳ ⏴ ⏵ ⏶ ⏷ ⏸ ⏹ ⏺ ⏻ ⏼ ⏽ □ ▲ △ ▶ ▷ ▼ ▽ ◀ ◁ ◆
◇ ○ ◎ ● ◘ ◦ ☀ ☁ ☂ ☃ ☄ ★ ☆ ☈ ☔ ☠ ☰ ☱ ☲ ☳ ☴ ☵ ☶ ☷ ☹ ☺ ☻ ☽ ♀ ♂ ♠ ♡
♢ ♣ ♤ ♥ ♦ ♧ ♩ ♪ ♫ ♬ ♭ ♮ ♯ ⚀ ⚁ ⚂ ⚃ ⚄ ⚅ ⚐ ⚑ ⚓ ⚔ ⚗ ⚡ ⚥ ⛄ ⛈ ⛏ ⛨ ✂ ✉
✎ ✔ ✘ ❄ ❌ ❣ ❤ ⟘ ⥝ ⧈ ⨰ ⩑ ⩒ ⫃ ⫄ ⭐ ⭘ ⯪ ⯫ ⱥ ⱦ Ɑ Ɐ ⱱ ⴀ ⴁ ⴂ ⴃ ⴄ ⴅ ⴆ ⴇ
ⴈ ⴉ ⴊ ⴋ ⴌ ⴍ ⴎ ⴏ ⴐ ⴑ ⴒ ⴓ ⴔ ⴕ ⴖ ⴗ ⴘ ⴙ ⴚ ⴛ ⴜ ⴝ ⴞ ⴟ ⴠ ⴡ ⴢ ⴣ ⴤ ⴥ ⸘ ⸚
⸮ ⸵ ⸸ ⹁ ⹋ 〒 ㄥ Ꜧ ꜧ Ꜩ ꜩ ꜰ ꜱ ꜹ ꜻ Ꝛ ꝛ Ꝺ Ꞁ ꞁ Ꞩ ꞩ ꞯ Ʞ ff fi fl ffi ſt יִ ײַ שׂ
₫ € ₭ ₮ ₰ ₱ ₲ ₳ ₴ ₵ ₶ ₷ ₸ ₹ ₺ ₻ ₼ ₽ ₾ ₿ ℗ ™ Ⅎ ⅁ ⅄ ⅋ ⅐ ⅑ ⅓ ⅔ ⅕ ⅖ ⅗ ⅙ ⅚ ⅛ ⅜ ⅝ ⅞ ⅟ ↉ ← ↑ → ↓ ↔ ⇄ ⇏
⇐ ⇑ ⇒ ⇓ ⇔ ⇵ ∀ ∁ ∂ ∃ ∄ ∉ ∋ ∌ ∑ − ∓ ∛ ∜ ∝ ∞ ∥ ∧ ∨ ∩ ∫ ∮ ∴ ∵ ∷ ≔ ≠ ≢ ⊂ ⊃ ⊄ ⊅ ⊆ ⊇ ⊢ ⊤ ⊥ ⊨ ⊻ ⊼ ⊽ ⋃ ⋆
⌀ ⌂ ⌘ ⌚ ⌛ ⏏ ⏩ ⏪ ⏭ ⏮ ⏯ ⏳ ⏴ ⏵ ⏶ ⏷ ⏸ ⏹ ⏺ ⏻ ⏼ ⏽ □ ▲ △ ▶ ▷ ▼ ▽ ◀ ◁ ◆ ◇ ○ ◎ ● ◘ ◦ ☀ ☁ ☂ ☃ ☄ ★ ☆ ☈ ☔ ☠
☰ ☱ ☲ ☳ ☴ ☵ ☶ ☷ ☹ ☺ ☻ ☽ ♀ ♂ ♠ ♡ ♢ ♣ ♤ ♥ ♦ ♧ ♩ ♪ ♫ ♬ ♭ ♮ ♯ ⚀ ⚁ ⚂ ⚃ ⚄ ⚅ ⚐ ⚑ ⚓ ⚔ ⚗ ⚡ ⚥ ⛄ ⛈ ⛏ ⛨ ✂ ✉
✎ ✔ ✘ ❄ ❌ ❣ ❤ ⟘ ⥝ ⧈ ⨰ ⩑ ⩒ ⫃ ⫄ ⭐ ⭘ ⯪ ⯫ ⱥ ⱦ Ɑ Ɐ ⱱ ⴀ ⴁ ⴂ ⴃ ⴄ ⴅ ⴆ ⴇ ⴈ ⴉ ⴊ ⴋ ⴌ ⴍ ⴎ ⴏ ⴐ ⴑ ⴒ ⴓ ⴔ ⴕ ⴖ ⴗ
ⴘ ⴙ ⴚ ⴛ ⴜ ⴝ ⴞ ⴟ ⴠ ⴡ ⴢ ⴣ ⴤ ⴥ ⸘ ⸚ ⸮ ⸵ ⸸ ⹁ ⹋ 〒 ㄥ Ꜧ ꜧ Ꜩ ꜩ ꜰ ꜱ ꜹ ꜻ Ꝛ ꝛ Ꝺ Ꞁ ꞁ Ꞩ ꞩ ꞯ Ʞ ff fi fl ffi ſt יִ ײַ שׂ
בּ וּ כּ פּ תּ וֹ בֿ פֿ + �
--- Ligatures ---
@@ -65,4 +50,214 @@ a b c d e f g h i j k l m n o p q r s t u v w x y z
 < - -> <-
 - > -> ->
 = > -> =>
 < = > -> <=>
 < = > -> <=>
 < ! - - -> <!--
 = = > -> ==>
 = = = > -> ===>
 = = = = > -> ====>
 = = = = = > -> =====>
 = = = = = = >-> ======>
 = = = = = = = >-> =======>
 = = = = = = = = >-> ========>
 = = = = = = = = = >-> =========>
 = = = = = = = = = = >-> ==========>
 = = = = = = = = = = = >-> ===========>
 = = = = = = = = = = = = >-> ============>
 = = = = = = = = = = = = = >-> =============>
 = = = = = = = = = = = = = = >-> ==============>
 = = = = = = = = = = = = = = = >-> ===============>
 = = = = = = = = = = = = = = = = >-> ================>
 = = = = = = = = = = = = = = = = = >-> =================>
 = = = = = = = = = = = = = = = = = = >-> ==================>
 = = = = = = = = = = = = = = = = = = = >-> ===================>
 = = = = = = = = = = = = = = = = = = = = >-> ====================>
 = = = = = = = = = = = = = = = = = = = = = >-> =====================>
 = = = = = = = = = = = = = = = = = = = = = = >-> ======================>
 = = = = = = = = = = = = = = = = = = = = = = = >-> =======================>
 = = = = = = = = = = = = = = = = = = = = = = = = >-> ========================>
 = = = = = = = = = = = = = = = = = = = = = = = = = >-> =========================>
 = = = = = = = = = = = = = = = = = = = = = = = = = = >-> ==========================>
 = = = = = = = = = = = = = = = = = = = = = = = = = = = >-> ===========================>
 = = = = = = = = = = = = = = = = = = = = = = = = = = = = >-> ============================>
 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = >-> =============================>
 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = >-> ==============================>
 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = >-> ===============================>
 < = = -> <==
 < = = = -> <===
 < = = = = -> <====
 < = = = = = -> <=====
 < = = = = = =-> <======
 < = = = = = = =-> <=======
 < = = = = = = = =-> <========
 < = = = = = = = = =-> <=========
 < = = = = = = = = = =-> <==========
 < = = = = = = = = = = =-> <===========
 < = = = = = = = = = = = =-> <============
 < = = = = = = = = = = = = =-> <=============
 < = = = = = = = = = = = = = =-> <==============
 < = = = = = = = = = = = = = = =-> <===============
 < = = = = = = = = = = = = = = = =-> <================
 < = = = = = = = = = = = = = = = = =-> <=================
 < = = = = = = = = = = = = = = = = = =-> <==================
 < = = = = = = = = = = = = = = = = = = =-> <===================
 < = = = = = = = = = = = = = = = = = = = =-> <====================
 < = = = = = = = = = = = = = = = = = = = = =-> <=====================
 < = = = = = = = = = = = = = = = = = = = = = =-> <======================
 < = = = = = = = = = = = = = = = = = = = = = = =-> <=======================
 < = = = = = = = = = = = = = = = = = = = = = = = =-> <========================
 < = = = = = = = = = = = = = = = = = = = = = = = = =-> <=========================
 < = = = = = = = = = = = = = = = = = = = = = = = = = =-> <==========================
 < = = = = = = = = = = = = = = = = = = = = = = = = = = =-> <===========================
 < = = = = = = = = = = = = = = = = = = = = = = = = = = = =-> <============================
 < = = = = = = = = = = = = = = = = = = = = = = = = = = = = =-> <=============================
 < = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =-> <==============================
 < = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =-> <===============================
 - - > -> -->
 - - - > -> --->
 - - - - > -> ---->
 - - - - - > -> ----->
 - - - - - - >-> ------>
 - - - - - - - >-> ------->
 - - - - - - - - >-> -------->
 - - - - - - - - - >-> --------->
 - - - - - - - - - - >-> ---------->
 - - - - - - - - - - - >-> ----------->
 - - - - - - - - - - - - >-> ------------>
 - - - - - - - - - - - - - >-> ------------->
 - - - - - - - - - - - - - - >-> -------------->
 - - - - - - - - - - - - - - - >-> --------------->
 - - - - - - - - - - - - - - - - >-> ---------------->
 - - - - - - - - - - - - - - - - - >-> ----------------->
 - - - - - - - - - - - - - - - - - - >-> ------------------>
 - - - - - - - - - - - - - - - - - - - >-> ------------------->
 - - - - - - - - - - - - - - - - - - - - >-> -------------------->
 - - - - - - - - - - - - - - - - - - - - - >-> --------------------->
 - - - - - - - - - - - - - - - - - - - - - - >-> ---------------------->
 - - - - - - - - - - - - - - - - - - - - - - - >-> ----------------------->
 - - - - - - - - - - - - - - - - - - - - - - - - >-> ------------------------>
 - - - - - - - - - - - - - - - - - - - - - - - - - >-> ------------------------->
 - - - - - - - - - - - - - - - - - - - - - - - - - - >-> -------------------------->
 - - - - - - - - - - - - - - - - - - - - - - - - - - - >-> --------------------------->
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - >-> ---------------------------->
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >-> ----------------------------->
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >-> ------------------------------>
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >-> ------------------------------->
 < - - -> <--
 < - - - -> <---
 < - - - - -> <----
 < - - - - - -> <-----
 < - - - - - --> <------
 < - - - - - - --> <-------
 < - - - - - - - --> <--------
 < - - - - - - - - --> <---------
 < - - - - - - - - - --> <----------
 < - - - - - - - - - - --> <-----------
 < - - - - - - - - - - - --> <------------
 < - - - - - - - - - - - - --> <-------------
 < - - - - - - - - - - - - - --> <--------------
 < - - - - - - - - - - - - - - --> <---------------
 < - - - - - - - - - - - - - - - --> <----------------
 < - - - - - - - - - - - - - - - - --> <-----------------
 < - - - - - - - - - - - - - - - - - --> <------------------
 < - - - - - - - - - - - - - - - - - - --> <-------------------
 < - - - - - - - - - - - - - - - - - - - --> <--------------------
 < - - - - - - - - - - - - - - - - - - - - --> <---------------------
 < - - - - - - - - - - - - - - - - - - - - - --> <----------------------
 < - - - - - - - - - - - - - - - - - - - - - - --> <-----------------------
 < - - - - - - - - - - - - - - - - - - - - - - - --> <------------------------
 < - - - - - - - - - - - - - - - - - - - - - - - - --> <-------------------------
 < - - - - - - - - - - - - - - - - - - - - - - - - - --> <--------------------------
 < - - - - - - - - - - - - - - - - - - - - - - - - - - --> <---------------------------
 < - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <----------------------------
 < - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <-----------------------------
 < - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <------------------------------
 < - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <-------------------------------
 - - - -> ---
 - - - - -> ----
 - - - - - -> -----
 - - - - - - -> ------
 - - - - - - --> -------
 - - - - - - - --> --------
 - - - - - - - - --> ---------
 - - - - - - - - - --> ----------
 - - - - - - - - - - --> -----------
 - - - - - - - - - - - --> ------------
 - - - - - - - - - - - - --> -------------
 - - - - - - - - - - - - - --> --------------
 - - - - - - - - - - - - - - --> ---------------
 - - - - - - - - - - - - - - - --> ----------------
 - - - - - - - - - - - - - - - - --> -----------------
 - - - - - - - - - - - - - - - - - --> ------------------
 - - - - - - - - - - - - - - - - - - --> -------------------
 - - - - - - - - - - - - - - - - - - - --> --------------------
 - - - - - - - - - - - - - - - - - - - - --> ---------------------
 - - - - - - - - - - - - - - - - - - - - - --> ----------------------
 - - - - - - - - - - - - - - - - - - - - - - --> -----------------------
 - - - - - - - - - - - - - - - - - - - - - - - --> ------------------------
 - - - - - - - - - - - - - - - - - - - - - - - - --> -------------------------
 - - - - - - - - - - - - - - - - - - - - - - - - - --> --------------------------
 - - - - - - - - - - - - - - - - - - - - - - - - - - --> ---------------------------
 - - - - - - - - - - - - - - - - - - - - - - - - - - - --> ----------------------------
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> -----------------------------
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> ------------------------------
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> -------------------------------
 ^ ^ -> ^^
 ^ ^ ^ -> ^^^
 ^ ^ ^ ^ -> ^^^^
 ^ ^ ^ ^ ^ -> ^^^^^
 ^ ^ ^ ^ ^ ^ -> ^^^^^^
 ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 + + -> ++
 + + + -> +++
 + + + + -> ++++
 + + + + + -> +++++
 + + + + + + -> ++++++
 + + + + + + +-> +++++++
 + + + + + + + +-> ++++++++
 + + + + + + + + +-> +++++++++
 + + + + + + + + + +-> ++++++++++
 + + + + + + + + + + +-> +++++++++++
 + + + + + + + + + + + +-> ++++++++++++
 + + + + + + + + + + + + +-> +++++++++++++
 + + + + + + + + + + + + + +-> ++++++++++++++
 + + + + + + + + + + + + + + +-> +++++++++++++++
 + + + + + + + + + + + + + + + +-> ++++++++++++++++
 + + + + + + + + + + + + + + + + +-> +++++++++++++++++
 + + + + + + + + + + + + + + + + + +-> ++++++++++++++++++
 + + + + + + + + + + + + + + + + + + +-> +++++++++++++++++++
 + + + + + + + + + + + + + + + + + + + +-> ++++++++++++++++++++
 + + + + + + + + + + + + + + + + + + + + +-> +++++++++++++++++++++
 + + + + + + + + + + + + + + + + + + + + + +-> ++++++++++++++++++++++
 + + + + + + + + + + + + + + + + + + + + + + +-> +++++++++++++++++++++++
 + + + + + + + + + + + + + + + + + + + + + + + +-> ++++++++++++++++++++++++
 + + + + + + + + + + + + + + + + + + + + + + + + +-> +++++++++++++++++++++++++
 + + + + + + + + + + + + + + + + + + + + + + + + + +-> ++++++++++++++++++++++++++
 + + + + + + + + + + + + + + + + + + + + + + + + + + +-> +++++++++++++++++++++++++++
 + + + + + + + + + + + + + + + + + + + + + + + + + + + +-> ++++++++++++++++++++++++++++
 + + + + + + + + + + + + + + + + + + + + + + + + + + + + +-> +++++++++++++++++++++++++++++
 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +-> ++++++++++++++++++++++++++++++
 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +-> +++++++++++++++++++++++++++++++
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 615 KiB

After

Width:  |  Height:  |  Size: 1019 KiB

+16 -15
View File
@@ -2207,6 +2207,22 @@
],
"leftMargin": -0.5
},
{
"character": "ı",
"name": "latin_small_letter_dotless_i",
"codepoint": 305,
"pixels": [
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 1]
],
"leftMargin": -0.5
},
{
"character": "ĵ",
"name": "latin_small_letter_j_with_circumflex",
@@ -4386,21 +4402,6 @@
"group": "general_punctuation",
"character": "‑"
},
{
"codepoint": 173,
"name": "soft_hyphen",
"pixels": [
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
],
"group": "latin_1_supplement",
"character": "­"
},
{
"codepoint": 254,
"name": "latin_small_letter_thorn",
+197
View File
@@ -0,0 +1,197 @@
[
{
"head": "=>",
"body": "=",
"head_name": "right thick arrow",
"body_name": "equals sign",
"direction": "right",
"min_length": 1,
"max_length": 30,
"head_pixels": [
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
],
"body_pixels": [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]
]
},
{
"head": "<=",
"body": "=",
"head_name": "left thick arrow",
"body_name": "equals sign",
"direction": "left",
"min_length": 1,
"max_length": 30,
"head_pixels": [
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
],
"body_pixels": [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]
]
},
{
"head": "->",
"body": "-",
"head_name": "right arrow",
"body_name": "minus",
"direction": "right",
"min_length": 1,
"max_length": 30,
"head_pixels": [
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
],
"body_pixels": [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]
]
},
{
"head": "<-",
"body": "-",
"head_name": "left arrow",
"body_name": "minus",
"direction": "left",
"min_length": 1,
"max_length": 30,
"head_pixels": [
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
],
"body_pixels": [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]
]
},
{
"head": "-",
"body": "-",
"head_name": "minus",
"body_name": "minus",
"direction": "left",
"min_length": 2,
"max_length": 30,
"head_pixels": [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
],
"body_pixels": [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]
]
},
{
"head": "^",
"body": "^",
"head_name": "caret",
"body_name": "caret",
"direction": "right",
"min_length": 1,
"max_length": 30,
"head_pixels": [
[0, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 1, 0],
[1, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]
],
"body_pixels": [
[0, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 1, 0],
[1, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]
]
},
{
"head": "+",
"body": "+",
"head_name": "plus",
"body_name": "plus",
"direction": "right",
"min_length": 1,
"max_length": 30,
"head_pixels": [
[0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0]
],
"body_pixels": [
[0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 1],
[0, 0, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0]
]
}
]
+75
View File
@@ -0,0 +1,75 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
import copy
def generate_continuous_ligatures(filename):
"""
Generates continuous ligature data from a JSON file.
The JSON file should have the following structure:
[
{
"head_name": "string",
"body_name": "string",
"head": "string",
"body": "string",
"direction": "left" or "right",
"min_length": int,
"max_length": int,
"head_pixels": [[int, int, ...], ...],
"body_pixels": [[int, int, ...], ...]
},
...
]
Returns a list of progress bars, where each progress bar is a dictionary with the following keys:
- "name": a string representing the name of the progress bar
- "ligature": a string representing the progress bar
- "sequence": a list of integers representing the Unicode code points of the characters in the progress bar
- "pixels": a list of integers representing the pixels of the progress bar
"""
continuous_ligatures = json.load(open(filename))
out = []
for ligature in continuous_ligatures:
name = f'{ligature["head_name"]} {ligature["body_name"]} '
body_pixels = ligature["body_pixels"]
head_pixels = ligature["head_pixels"]
body = [[] for _ in range(len(body_pixels))]
for i in range( ligature["min_length"],ligature["max_length"] + 1):
glyph = {}
# Generate ligature data
glyph["name"] = name + str(i);
if ligature["direction"] == "right":
glyph["ligature"] = ligature["body"] * i + ligature["head"]
else:
glyph["ligature"] = ligature["head"] + ligature["body"] * i
glyph["sequence"] = [ord(c) for c in glyph["ligature"]]
# Generate pixels
# Expand the body by 1
for k in range(len(body)):
body[k] += body_pixels[k]
pixels = []
if ligature["direction"] == "right":
pixels = copy.deepcopy(body)
for j in range(len(pixels)):
pixels[j] += head_pixels[j]
else:
pixels = copy.deepcopy(head_pixels)
for j in range(len(pixels)):
pixels[j] += body[j]
glyph["pixels"] = pixels
out.append(glyph)
return out
+1 -1
View File
@@ -48,7 +48,7 @@ def generateExamples(characters, ligatures, charactersByCodepoint):
index = 0
characterOutput += "\n"
if i in charactersByCodepoint:
if index % 32 == 0:
if index % 48 == 0:
characterOutput += "\n"
characterOutput += chr(i) + " "
index += 1
+15 -1
View File
@@ -149,5 +149,19 @@
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
]
},
{
"ligature": "<!--",
"name": "html comment",
"sequence": [60, 33, 45, 45],
"pixels": [
[0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
}
]
]
+295 -38
View File
@@ -1,5 +1,5 @@
# Monocraft, a monospaced font for developers who like Minecraft a bit too much.
# Copyright (C) 2022-2023 Idrees Hassan
# Copyright (C) 2022-2024 Idrees Hassan
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
@@ -17,64 +17,236 @@ import os
import fontforge
import json
import math
import argparse
from generate_diacritics import generateDiacritics
from generate_examples import generateExamples
from polygonizer import PixelImage, generatePolygons
from generate_continuous_ligatures import generate_continuous_ligatures
PIXEL_SIZE = 120
characters = json.load(open("./characters.json"))
diacritics = json.load(open("./diacritics.json"))
ligatures = json.load(open("./ligatures.json"))
ligatures += generate_continuous_ligatures("./continuous_ligatures.json")
characters = generateDiacritics(characters, diacritics)
charactersByCodepoint = {}
def generateFont():
monocraft = fontforge.font()
monocraft.fontname = "Monocraft"
monocraft.familyname = "Monocraft"
monocraft.fullname = "Monocraft"
monocraft.copyright = "Idrees Hassan, https://github.com/IdreesInc/Monocraft"
monocraft.encoding = "UnicodeFull"
monocraft.version = "3.0"
monocraft.weight = "Regular"
monocraft.ascent = PIXEL_SIZE * 8
monocraft.descent = PIXEL_SIZE
monocraft.em = PIXEL_SIZE * 9
monocraft.upos = -PIXEL_SIZE # Underline position
monocraft.addLookup("ligatures", "gsub_ligature", (), (("liga",(("dflt",("dflt")),("latn",("dflt")))),))
monocraft.addLookupSubtable("ligatures", "ligatures-subtable")
def parseArgs():
parser = argparse.ArgumentParser()
parser.add_argument(
"--output-ttc",
action="store_true",
dest="output_ttc",
)
parser.add_argument(
"-a",
"--all",
action="store_true",
dest="all",
)
parser.add_argument(
"-O",
"--black",
action="store_true",
dest="black",
)
parser.add_argument(
"-B",
"--bold",
action="store_true",
dest="bold",
)
parser.add_argument(
"-b",
"--semibold",
action="store_true",
dest="semibold",
)
parser.add_argument(
"-l",
"--light",
action="store_true",
dest="light",
)
parser.add_argument(
"-L",
"--extralight",
action="store_true",
dest="extralight",
)
parser.add_argument(
"-i",
"--italic",
action="store_true",
dest="italic",
)
ret = parser.parse_args()
if ret.all:
ret.black = ret.bold = ret.semibold = ret.light = ret.extralight = ret.italic = True
return ret
def generateFont(*, black=False, bold=False, semibold=False, light=False, extralight=False, italic=False, output_ttc=False, **kw):
fontList = [
fontforge.font(),
fontforge.font() if italic else None,
fontforge.font() if black else None,
fontforge.font() if black and italic else None,
fontforge.font() if bold else None,
fontforge.font() if bold and italic else None,
fontforge.font() if semibold else None,
fontforge.font() if semibold and italic else None,
fontforge.font() if light else None,
fontforge.font() if light and italic else None,
fontforge.font() if extralight else None,
fontforge.font() if extralight and italic else None,
]
for font in fontList:
if font is None:
continue
font.fontname = "Monocraft"
font.familyname = "Monocraft"
font.fullname = "Monocraft"
font.copyright = "Idrees Hassan, https://github.com/IdreesInc/Monocraft"
font.encoding = "UnicodeFull"
font.version = "4.0"
font.weight = "Regular"
font.ascent = PIXEL_SIZE * 8
font.descent = PIXEL_SIZE
font.em = PIXEL_SIZE * 9
font.upos = -PIXEL_SIZE # Underline position
font.addLookup("ligatures", "gsub_ligature", (),
(("liga", (("dflt", ("dflt")), ("latn", ("dflt")))), ))
font.addLookupSubtable("ligatures", "ligatures-subtable")
font = fontList[0]
if font is not None:
font.macstyle = 0
font.os2_stylemap = 0x40
font = fontList[1]
if font is not None:
font.fontname = "Monocraft-Italic"
font.fullname = "Monocraft Italic"
font.macstyle = 2
font.os2_stylemap = 1
font.italicangle = -15
font = fontList[2]
if font is not None:
font.fontname = "Monocraft-Black"
font.fullname = "Monocraft Black"
font.weight = "Black"
font.macstyle = 1
font.os2_stylemap = 0x20
font = fontList[3]
if font is not None:
font.fontname = "Monocraft-Black-Italic"
font.fullname = "Monocraft Black Italic"
font.weight = "Black"
font.macstyle = 3
font.os2_stylemap = 0x21
font.italicangle = -15
font = fontList[4]
if font is not None:
font.fontname = "Monocraft-Bold"
font.fullname = "Monocraft Bold"
font.weight = "Bold"
font.macstyle = 1
font.os2_stylemap = 0x20
font = fontList[5]
if font is not None:
font.fontname = "Monocraft-Bold-Italic"
font.fullname = "Monocraft Bold Italic"
font.weight = "Bold"
font.macstyle = 3
font.os2_stylemap = 0x21
font.italicangle = -15
font = fontList[6]
if font is not None:
font.fontname = "Monocraft-SemiBold"
font.fullname = "Monocraft SemiBold"
font.weight = "Demi"
font.macstyle = 1
font.os2_stylemap = 0x20
font = fontList[7]
if font is not None:
font.fontname = "Monocraft-SemiBold-Italic"
font.fullname = "Monocraft SemiBold Italic"
font.weight = "Demi"
font.macstyle = 3
font.os2_stylemap = 0x21
font.italicangle = -15
font = fontList[8]
if font is not None:
font.fontname = "Monocraft-Light"
font.fullname = "Monocraft Light"
font.weight = "Light"
font.macstyle = 0
font.os2_stylemap = 0
font = fontList[9]
if font is not None:
font.fontname = "Monocraft-Light-Italic"
font.fullname = "Monocraft Light Italic"
font.weight = "Light"
font.macstyle = 2
font.os2_stylemap = 1
font.italicangle = -15
font = fontList[10]
if font is not None:
font.fontname = "Monocraft-ExtraLight"
font.fullname = "Monocraft ExtraLight"
font.weight = "Extra-Light"
font.macstyle = 0
font.os2_stylemap = 0
font = fontList[11]
if font is not None:
font.fontname = "Monocraft-ExtraLight-Italic"
font.fullname = "Monocraft ExtraLight Italic"
font.weight = "Extra-Light"
font.macstyle = 2
font.os2_stylemap = 1
font.italicangle = -15
for character in characters:
charactersByCodepoint[character["codepoint"]] = character
monocraft.createChar(character["codepoint"], character["name"])
pen = monocraft[character["name"]].glyphPen()
top = 0
drawn = character
image, kw = generateImage(character)
drawImage(image, pen, **kw)
monocraft[character["name"]].width = PIXEL_SIZE * 6
createChar(fontList, character["codepoint"], character["name"], image, **kw)
print(f"Generated {len(characters)} characters")
outputDir = "../dist/"
if not os.path.exists(outputDir):
os.makedirs(outputDir)
monocraft.generate(outputDir + "Monocraft-no-ligatures.ttf")
if output_ttc:
fontList[0].generateTtc(
outputDir + "Monocraft-no-ligatures.ttc",
[i for i in fontList[1:] if i is not None],
ttcflags=("merge", ),
layer=1,
)
for ligature in ligatures:
lig = monocraft.createChar(-1, ligature["name"])
pen = monocraft[ligature["name"]].glyphPen()
image, kw = generateImage(ligature)
drawImage(image, pen, **kw)
monocraft[ligature["name"]].width = PIXEL_SIZE * len(ligature["sequence"]) * 6
lig.addPosSub("ligatures-subtable", tuple(map(lambda codepoint: charactersByCodepoint[codepoint]["name"], ligature["sequence"])))
createChar(fontList, -1, ligature["name"], image, width=PIXEL_SIZE * len(ligature["sequence"]) * 6, **kw)
for font in fontList:
if font is None:
continue
font[ligature["name"]].addPosSub("ligatures-subtable", tuple(map(lambda codepoint: charactersByCodepoint[codepoint]["name"], ligature["sequence"])))
print(f"Generated {len(ligatures)} ligatures")
monocraft.generate(outputDir + "Monocraft.ttf")
monocraft.generate(outputDir + "Monocraft.otf")
for font in fontList:
if font is None:
continue
font.generate(f"{outputDir}{font.fontname}.otf")
font.generate(f"{outputDir}{font.fontname}.ttf")
if output_ttc:
fontList[0].generateTtc(
outputDir + "Monocraft.ttc",
[i for i in fontList[1:] if i is not None],
ttcflags=("merge", ),
layer=1,
)
def generateImage(character):
image = PixelImage()
@@ -115,15 +287,15 @@ def imageFromArray(arr, x=0, y=0):
y=y,
width=len(arr[0]),
height=len(arr),
data=bytes(x for a in reversed(arr) for x in a),
data=bytes(v for a in reversed(arr) for v in a),
)
def drawImage(image, pen, *, dx=0, dy=0):
for polygon in generatePolygons(image):
def drawPolygon(poly, pen):
for polygon in poly:
start = True
for x, y in polygon:
x = (x + dx) * PIXEL_SIZE
y = (y + dy) * PIXEL_SIZE
x = int(math.floor(x * PIXEL_SIZE))
y = int(math.floor(y * PIXEL_SIZE))
if start:
pen.moveTo(x, y)
start = False
@@ -131,5 +303,90 @@ def drawImage(image, pen, *, dx=0, dy=0):
pen.lineTo(x, y)
pen.closePath()
generateFont()
generateExamples(characters, ligatures, charactersByCodepoint)
BOLD_THIN_DISTS = {
"Black": 0.3,
"Bold": 0.2,
"Demi": 0.1,
"Light": -0.1,
"Extra-Light": -0.3,
}
ITALIC_RATIO = math.tan(math.radians(15))
def boldify(p, boldness):
l = len(p)
for i in range(l):
x, y = p[i]
dx, dy = 0, 0
px, py = p[i - 1]
if px < x:
dy += boldness
elif px > x:
dy -= boldness
elif py < y:
dx -= boldness
else:
dx += boldness
px, py = p[(i + 1) % l]
if px < x:
dy -= boldness
elif px > x:
dy += boldness
elif py < y:
dx += boldness
else:
dx -= boldness
yield (dx + x, dy + y)
def createChar(
fontList,
code,
name,
image=None,
*,
width=None,
dx=0,
dy=0,
glyphclass=None,
):
if image is not None:
poly = [[(x + dx, y + dy) for x, y in p]
for p in generatePolygons(image)]
poly_b = None
poly_t = None
for font in fontList:
if font is None:
continue
char = font.createChar(code, name)
#char.manualHints = True
if glyphclass is not None:
char.glyphclass = glyphclass
if image is None:
char.width = width if width is not None else PIXEL_SIZE * 6
continue
p = poly
try:
dist = BOLD_THIN_DISTS[font.weight]
except KeyError:
dist = 0
if dist > 0:
if poly_b is None:
poly_b = [[(x + dx, y + dy) for x, y in p] for p in generatePolygons(image, join_polygons=False)]
p = (boldify(p, dist) for p in poly_b)
elif dist < 0:
if poly_t is None:
poly_t = [[(x + dx, y + dy) for x, y in p] for p in generatePolygons(image, join_polygons=False, exclude_corners=True)]
p = (boldify(p, dist) for p in poly_t)
if font.macstyle & 2:
p = (((x + y * ITALIC_RATIO, y) for x, y in p) for p in p)
drawPolygon(p, char.glyphPen())
char.width = width if width is not None else PIXEL_SIZE * 6
args = parseArgs()
generateFont(**vars(args))
generateExamples(characters, ligatures, charactersByCodepoint)
+328 -161
View File
@@ -94,7 +94,7 @@ class PixelImage:
def __setitem__(self, key, value):
''' Sets a pixel at (x, y).
Do nothing if out of bounds.
'''
x, y = key
@@ -187,10 +187,36 @@ class PixelImage:
return ret
def crop(self, min_x, max_x, min_y, max_y):
if max_x < min_x or max_y < min_y:
raise ValueError('Invalid bounds')
def generatePolygons(image):
min_x = max(min_x, self._x)
max_x = min(max_x, self.x_end)
min_y = max(min_y, self._y)
max_y = min(max_y, self.y_end)
if max_x <= min_x or max_y <= min_y:
return PixelImage()
data = bytearray((max_x - min_x) * (max_y - min_y))
ix = 0
for j in range(min_y, max_y):
for i in range(min_x, max_x):
data[ix] = self[i, j]
ix += 1
return PixelImage(
x=min_x,
y=min_y,
width=max_x - min_x,
height=max_y - min_y,
data=data,
)
def generatePolygons(image, **kw):
for segment, start_pos in segmentize(image):
yield from polygonizeSegment(segment, start_pos)
yield from polygonizeSegment(segment, start_pos, **kw)
def segmentize(image):
@@ -322,25 +348,20 @@ class CellFlag(IntFlag):
return ret
def polygonizeSegment(image, start_pos):
x, y = start_pos
class Turtle:
__slots__ = ['img', 'x', 'y', 'dir']
# Make sure position is top left
assert image[x, y]
assert not (image[x - 1, y] or image[x - 1, y - 1] or image[x, y - 1]
or image[x + 1, y - 1])
dir = CellFlag.RIGHT
def doMove():
nonlocal x, y, dir
def __init__(self, img, x, y, dir):
self.img, self.x, self.y, self.dir = img, x, y, dir
def _move(self):
# Set wall
d_ccw, d_cw, d_rev = dir.ccw(), dir.cw(), dir.reverse()
image[x, y] |= d_ccw
d_ccw, d_cw, d_rev = self.dir.ccw(), self.dir.cw(), self.dir.reverse()
p = self.x, self.y
self.img[p] |= d_ccw
x_, y_ = d_ccw.move((x, y))
assert not image[x_, y_] & CellFlag.ACTIVE
p_ = d_ccw.move(p)
assert not self.img[p_] & CellFlag.ACTIVE
# Build boundary wall from this point
# For all diagrams, it is assumed movement direction is right
@@ -348,252 +369,398 @@ def polygonizeSegment(image, start_pos):
# ? . #
# ? X ?
# ? ? ?
x_, y_ = (dir | d_ccw).move((x, y))
if image[x_, y_] & CellFlag.ACTIVE:
p_ = (self.dir | d_ccw).move(p)
if self.img[p_] & CellFlag.ACTIVE:
# Top-right corner
ret = [(dir | d_ccw).corner((x, y))]
ret = [(self.dir | d_ccw).corner(p)]
# ? . ^
# ? X ?
# ? ? ?
x, y = x_, y_
dir = d_ccw
(self.x, self.y), self.dir = p_, d_ccw
return ret
# ? . .
# ? X #
# ? ? ?
x_, y_ = dir.move((x, y))
if image[x_, y_] & CellFlag.ACTIVE:
p_ = self.dir.move(p)
if self.img[p_] & CellFlag.ACTIVE:
# ? . .
# ? X >
# ? ? ?
x, y = x_, y_
self.x, self.y = p_
return [] # No corner
# ? . .
# ? X .
# ? ? #
x_, y_ = (dir | d_cw).move((x, y))
if image[x_, y_] & CellFlag.ACTIVE:
p_ = (self.dir | d_cw).move(p)
if self.img[p_] & CellFlag.ACTIVE:
# Two corners: top-right and bottom-right
ret = [
(dir | d_ccw).corner((x, y)),
(dir | d_cw).corner((x, y)),
(self.dir | d_ccw).corner(p),
(self.dir | d_cw).corner(p),
]
# ? . .
# ? X .
# ? ? >
image[x, y] |= dir
x, y = x_, y_
self.img[p] |= self.dir
self.x, self.y = p_
return ret
# ? . .
# ? X .
# ? # .
x_, y_ = d_cw.move((x, y))
if image[x_, y_] & CellFlag.ACTIVE:
p_ = d_cw.move(p)
if self.img[p_] & CellFlag.ACTIVE:
# Top-right corner
ret = [(dir | d_ccw).corner((x, y))]
ret = [(self.dir | d_ccw).corner(p)]
# ? . .
# ? X .
# ? v .
image[x, y] |= dir
x, y = x_, y_
dir = d_cw
self.img[p] |= self.dir
(self.x, self.y), self.dir = p_, d_cw
return ret
# ? . .
# ? X .
# # . .
x_, y_ = (d_rev | d_cw).move((x, y))
if image[x_, y_] & CellFlag.ACTIVE:
p_ = (d_rev | d_cw).move(p)
if self.img[p_] & CellFlag.ACTIVE:
# Three corners: top-right, bottom-right, and bottom-left
ret = [
(dir | d_ccw).corner((x, y)),
(dir | d_cw).corner((x, y)),
(d_cw | d_rev).corner((x, y)),
(self.dir | d_ccw).corner(p),
(self.dir | d_cw).corner(p),
(d_cw | d_rev).corner(p),
]
# ? . .
# ? X .
# v . .
image[x, y] |= dir | d_cw
x, y = x_, y_
dir = d_cw
self.img[p] |= self.dir | d_cw
(self.x, self.y), self.dir = p_, d_cw
return ret
# ? . .
# # X .
# . . .
x_, y_ = d_rev.move((x, y))
if image[x_, y_] & CellFlag.ACTIVE:
p_ = d_rev.move(p)
if self.img[p_] & CellFlag.ACTIVE:
# Two corners: top-right and bottom-right
ret = [
(dir | d_ccw).corner((x, y)),
(dir | d_cw).corner((x, y)),
(self.dir | d_ccw).corner(p),
(self.dir | d_cw).corner(p),
]
# ? . .
# < X .
# . . .
image[x, y] |= dir | d_cw
x, y = x_, y_
dir = d_rev
self.img[p] |= self.dir | d_cw
(self.x, self.y), self.dir = p_, d_rev
return ret
# # . .
# . X .
# . . .
x_, y_ = (d_rev | d_ccw).move((x, y))
if image[x_, y_] & CellFlag.ACTIVE:
p_ = (d_rev | d_ccw).move(p)
if self.img[p_] & CellFlag.ACTIVE:
# Four corners: top-right, bottom-right, bottom-left, and top-left
ret = [
(dir | d_ccw).corner((x, y)),
(dir | d_cw).corner((x, y)),
(d_cw | d_rev).corner((x, y)),
(d_ccw | d_rev).corner((x, y)),
(self.dir | d_ccw).corner(p),
(self.dir | d_cw).corner(p),
(d_cw | d_rev).corner(p),
(d_ccw | d_rev).corner(p),
]
# < . .
# . X .
# . . .
image[x, y] |= dir | d_cw | d_rev
x, y = x_, y_
dir = d_rev
self.img[p] |= self.dir | d_cw | d_rev
(self.x, self.y), self.dir = p_, d_rev
return ret
# . . .
# . X .
# . . .
image[x, y] |= dir | d_cw | d_rev
self.img[p] |= self.dir | d_cw | d_rev
# Four corners: top-right, bottom-right, bottom-left, and top-left
return [
(dir | d_ccw).corner((x, y)),
(dir | d_cw).corner((x, y)),
(d_cw | d_rev).corner((x, y)),
(d_ccw | d_rev).corner((x, y)),
(self.dir | d_ccw).corner(p),
(self.dir | d_cw).corner(p),
(d_cw | d_rev).corner(p),
(d_ccw | d_rev).corner(p),
]
def find(l, x):
try:
return l.index(x)
except ValueError:
return None
def _move_4way(self):
# Set wall
d_ccw, d_cw, d_rev = self.dir.ccw(), self.dir.cw(), self.dir.reverse()
p = self.x, self.y
self.img[p] |= d_ccw
# Generate outer polygon
outer_poly = []
line = doMove()
i = find(line, start_pos)
while i is None:
outer_poly += line
line = doMove()
i = find(line, start_pos)
outer_poly += line[:i + 1]
p_ = d_ccw.move(p)
assert not self.img[p_] & CellFlag.ACTIVE
assert checkPoly(outer_poly)
# Build boundary wall from this point
# For all diagrams, it is assumed movement direction is right
points_ = dict(
zip(
reversed(outer_poly),
range(len(outer_poly) - 1, -1, -1),
))
points = defaultdict(set)
for p in outer_poly:
points[p].add(0)
# ? . ?
# ? X #
# ? ? ?
p_ = self.dir.move(p)
if self.img[p_] & CellFlag.ACTIVE:
poly = [(outer_poly, points_)]
# ? . #
# ? X #
# ? ? ?
p__ = d_ccw.move(p_)
if self.img[p__] & CellFlag.ACTIVE:
# Top-right corner
ret = [(self.dir | d_ccw).corner(p)]
# Calculate bounding box
x_min, y_min, x_max, y_max = x, y, x, y
for x, y in outer_poly:
if x_min > x:
x_min = x
if x_max < x:
x_max = x
if y_min > y:
y_min = y
if y_max < y:
y_max = y
# ? . ^
# ? X #
# ? ? ?
(self.x, self.y), self.dir = p__, d_ccw
return ret
# Generate inner polygon
for y in range(y_min, y_max):
for x in range(x_min, x_max):
# ? . .
# ? X #
# ? ? ?
else:
# ? . .
# ? X >
# ? ? ?
self.x, self.y = p_
return [] # No corner
# ? . ?
# ? X .
# ? # ?
p_ = d_cw.move(p)
if self.img[p_] & CellFlag.ACTIVE:
self.img[p] |= self.dir
# ? . ?
# ? X .
# ? # #
p__ = self.dir.move(p_)
if self.img[p__] & CellFlag.ACTIVE:
# Two corners: top-right and bottom-right
ret = [
(self.dir | d_ccw).corner(p),
(self.dir | d_cw).corner(p),
]
# ? . ?
# ? X .
# ? # >
self.x, self.y = p__
return ret
# ? . ?
# ? X .
# ? # .
else:
# Top-right corner
ret = [(self.dir | d_ccw).corner(p)]
# ? . ?
# ? X .
# ? v .
(self.x, self.y), self.dir = p_, d_cw
return ret
# ? . ?
# # X .
# ? . ?
p_ = d_rev.move(p)
if self.img[p_] & CellFlag.ACTIVE:
self.img[p] |= self.dir | d_cw
# ? . ?
# # X .
# # . ?
p__ = d_cw.move(p_)
if self.img[p__] & CellFlag.ACTIVE:
# Three corners: top-right, bottom-right, and bottom-left
ret = [
(self.dir | d_ccw).corner(p),
(self.dir | d_cw).corner(p),
(d_cw | d_rev).corner(p),
]
# ? . ?
# # X .
# v . .
(self.x, self.y), self.dir = p__, d_cw
return ret
# ? . ?
# # X .
# . . ?
else:
# Two corners: top-right and bottom-right
ret = [
(self.dir | d_ccw).corner(p),
(self.dir | d_cw).corner(p),
]
# ? . ?
# < X .
# . . ?
(self.x, self.y), self.dir = p_, d_rev
return ret
# ? . ?
# . X .
# ? . ?
self.img[p] |= self.dir | d_cw | d_rev
# Four corners: top-right, bottom-right, bottom-left, and top-left
return [
(self.dir | d_ccw).corner(p),
(self.dir | d_cw).corner(p),
(d_cw | d_rev).corner(p),
(d_ccw | d_rev).corner(p),
]
def trace(self, stop_pos, exclude_corners=False):
poly = []
while True:
j = len(poly)
poly += self._move_4way() if exclude_corners else self._move()
try:
i = poly.index(stop_pos, j)
except ValueError:
continue
return poly[:i + 1]
def polygonize(image, exclude_corners=False):
image = PixelImage(image)
ret = []
turtle = Turtle(image, 0, 0, CellFlag.LEFT)
for y in range(image.y, image.y_end):
for x in range(image.x, image.x_end):
v = image[x, y]
if not v & CellFlag.ACTIVE:
continue
if not (image[x, y + 1] & CellFlag.ACTIVE or v & CellFlag.DOWN):
dir = CellFlag.LEFT
if not (v & CellFlag.UP or image[x, y - 1] & CellFlag.ACTIVE):
turtle.dir, stop_pos = CellFlag.RIGHT, (x, y)
elif not (v & CellFlag.RIGHT or image[x + 1, y] & CellFlag.ACTIVE):
turtle.dir, stop_pos = CellFlag.DOWN, (x + 1, y)
else:
continue
turtle.x, turtle.y = x, y
# Store iterator value
p = x, y
ret.append(turtle.trace(stop_pos, exclude_corners)[::-1])
assert checkPoly(ret[-1])
inner_poly = []
line = doMove()
i = None
while i is None:
inner_poly += line
line = doMove()
i = find(line, inner_poly[0])
inner_poly += line[:i]
return ret
assert checkPoly(inner_poly)
j = len(poly)
poly.append((
inner_poly,
dict(
zip(
reversed(inner_poly),
range(len(inner_poly) - 1, -1, -1),
)),
))
for i in inner_poly:
points[i].add(j)
def joinPolygons(polygons):
def first_equals(i, j, *, key=lambda x: x):
i, j = enumerate(map(key, i)), enumerate(map(key, j))
try:
(ai, av), (bi, bv) = next(i), next(j)
while av!= bv:
if av < bv:
ai, av = next(i)
else:
bi, bv = next(j)
return ai, bi
except StopIteration:
pass
# Restore iterator value
x, y = p
def merge(i, j):
r = []
i, j = iter(i), iter(j)
a, b = next(i, None), next(j, None)
while a is not None and b is not None:
if a[1] == b[1]:
r.append((min(a[0], b[0]), a[1]))
a, b = next(i, None), next(j, None)
elif a[1] < b[1]:
r.append(a)
a = next(i, None)
else:
r.append(b)
b = next(j, None)
# Try to join polygons
for i in range(len(poly) - 1, -1, -1):
inner_poly, points_ = poly[i]
if a is not None:
r.extend(i)
elif b is not None:
r.extend(j)
for v in points.values():
if len(v) > 1 and i in v:
j = min(v)
break
else:
continue
return r
outer_poly, points__ = poly[j]
kf = lambda v: v[1]
for i_, p in enumerate(inner_poly):
j_ = points__.get(p)
if j_ is not None:
break
else:
raise RuntimeError(
f'Should not happened {inner_poly} {outer_poly}')
def f(p):
l = []
x = None
for i, v in sorted(enumerate(p), key=kf):
if x != v:
x = v
l.append((i, v))
return p, l
outer_poly[j_:j_] = [*inner_poly[i_:], *inner_poly[:i_]]
points__.update(
zip(
reversed(outer_poly),
range(len(outer_poly) - 1, -1, -1),
))
def checkPoints(p, l):
if not checkPoly(p):
return False
for i in range(len(l)):
j, v = l[i]
if j >= len(p) or p[j] != v:
print(f'{p}\n{l} Error at {i} ({j}, {v}) (got {None if j >= len(p) else p[j]})')
return False
if i < 1:
continue
_, t = l[i - 1]
if t == v:
print(f'{p}\n{l} Error at {i} ({j}, {v})')
return False
return True
del poly[i]
for v in points.values():
v.discard(i)
points = [f(i) for i in polygons]
for i in range(len(points) - 1, 0, -1):
p, l = points[i]
for j in range(i):
p_, l_ = points[j]
t = first_equals(l, l_, key=kf)
if t is None:
continue
# Emit polygons
for i, _ in poly:
yield i
a, b = t
a, _ = l[a]
b, _ = l_[b]
p = [*p[:a], *p_[b:], *p_[:b], *p[a:]]
t = len(p_)
s = t - b + a
r = a - b
l = merge(
((i + t if i >= a else i, v) for i, v in l),
((i + r if i >= b else i + s, v) for i, v in l_),
)
assert checkPoints(p, l)
points[j] = p, l
del points[i]
break
return [p[0] for p in points]
def generatePolygons(image, *, join_polygons=True, exclude_corners=False, **kw):
ret = polygonize(image, exclude_corners)
if join_polygons:
ret = joinPolygons(ret)
return ret
def checkPoly(poly):
@@ -651,7 +818,7 @@ def testChar(name, pixels):
print(f'Character: {name}\n{image}\n\n')
for poly in generatePolygons(image):
for poly in generatePolygons(image, join_polygons=False, exclude_corners=True):
print('Polygon:\n ' + '\n '.join(f'{x}, {y}'
for x, y in poly) + '\n\n')