├── scripts ├── mmtool │ ├── __init__.py │ ├── data.py │ ├── font_action.py │ └── unicode.py ├── list-blocks.py ├── check-width.py ├── momiage-mono.py └── LICENSE ├── .gitignore ├── docs ├── MomiageMono.png ├── nerdfont-icon.png ├── various-kanji.png ├── symbol-ligature.png ├── blocks-jbmono.txt ├── blocks-genei.txt └── unicode-blocks-construction.md ├── .gitmodules ├── .editorconfig ├── generate.bash ├── setup.bash ├── README.md └── LICENSE /scripts/mmtool/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /fonts 2 | /dist 3 | __pycache__ 4 | -------------------------------------------------------------------------------- /docs/MomiageMono.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kb10uy/MomiageMono/HEAD/docs/MomiageMono.png -------------------------------------------------------------------------------- /docs/nerdfont-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kb10uy/MomiageMono/HEAD/docs/nerdfont-icon.png -------------------------------------------------------------------------------- /docs/various-kanji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kb10uy/MomiageMono/HEAD/docs/various-kanji.png -------------------------------------------------------------------------------- /docs/symbol-ligature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kb10uy/MomiageMono/HEAD/docs/symbol-ligature.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "nerd-fonts"] 2 | path = nerd-fonts 3 | url = https://github.com/ryanoasis/nerd-fonts 4 | branch = master 5 | shallow = true 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /scripts/list-blocks.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from mmtool.unicode import unicode_block_of 3 | import fontforge 4 | 5 | 6 | font = fontforge.open(sys.argv[1]) 7 | base_em = font.em 8 | 9 | found_blocks: set[tuple[int, int, str]] = set() 10 | for glyph in font.glyphs(): 11 | if glyph.unicode == -1: 12 | continue 13 | 14 | codepoint = glyph.unicode 15 | char = chr(codepoint) 16 | block = unicode_block_of(codepoint) 17 | glyph_width = glyph.width / base_em 18 | 19 | found_blocks.add(block) 20 | 21 | print("Unicode Blocks which the Font have:") 22 | for _, _, block in sorted(found_blocks): 23 | print(block) 24 | -------------------------------------------------------------------------------- /generate.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | rm -rf dist/* 5 | 6 | python3 "scripts/momiage-mono.py" 7 | 8 | pushd "dist" 9 | for filename in *.ttf; do 10 | subfamily=$(echo $filename | sed -E 's/.+-(.+)\.ttf/\1/') 11 | subfamily_separated=$(echo $subfamily | sed -E 's/([a-z])([A-Z])/\1 \2/g') 12 | 13 | ../nerd-fonts/font-patcher -c $filename 14 | mv "Momiage Mono ${subfamily_separated} Nerd Font Complete.ttf" "MomiageMono-${subfamily}-NerdFont.ttf" 15 | 16 | zip "package.zip" $filename "MomiageMono-${subfamily}-NerdFont.ttf" 17 | done 18 | 19 | zip -j "package.zip" "../LICENSE" 20 | popd 21 | -------------------------------------------------------------------------------- /setup.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | git submodule update --init 5 | 6 | mkdir -p "fonts" 7 | 8 | mkdir -p "fonts/gen-ei-mono-go" 9 | pushd "fonts/gen-ei-mono-go" 10 | if [[ ! -f "GenEiMonoGothic_v1.0.zip" ]]; then 11 | echo "You have to download \"GenEiMonoGothic_v1.0.zip\" manually." 12 | echo "https://okoneya.jp/font/genei-mono-go.html" 13 | else 14 | rm -rf *.ttf 15 | unzip -j "GenEiMonoGothic_v1.0.zip" "GenEiMonoGothic_v1.0/GenEiMonoGothic-*.ttf" 16 | fi 17 | popd 18 | 19 | mkdir -p "fonts/jetbrains-mono" 20 | pushd "fonts/jetbrains-mono" 21 | rm -rf * 22 | wget -O "jetbrains-mono.zip" "https://github.com/JetBrains/JetBrainsMono/releases/download/v2.242/JetBrainsMono-2.242.zip" 23 | unzip -j "jetbrains-mono.zip" "fonts/ttf/*" 24 | popd 25 | 26 | mkdir -p "dist" 27 | rm -rf "dist/*" 28 | -------------------------------------------------------------------------------- /docs/blocks-jbmono.txt: -------------------------------------------------------------------------------- 1 | Basic Latin 2 | Latin-1 Supplement 3 | Latin Extended-A 4 | Latin Extended-B 5 | IPA Extensions 6 | Spacing Modifier Letters 7 | Combining Diacritical Marks 8 | Greek and Coptic 9 | Cyrillic 10 | Gujarati 11 | Latin Extended Additional 12 | General Punctuation 13 | Superscripts and Subscripts 14 | Currency Symbols 15 | Letterlike Symbols 16 | Number Forms 17 | Arrows 18 | Mathematical Operators 19 | Miscellaneous Technical 20 | Control Pictures 21 | Box Drawing 22 | Block Elements 23 | Geometric Shapes 24 | Miscellaneous Symbols 25 | Dingbats 26 | Miscellaneous Mathematical Symbols-A 27 | Supplemental Arrows-A 28 | Supplemental Arrows-B 29 | Miscellaneous Mathematical Symbols-B 30 | Supplemental Mathematical Operators 31 | Miscellaneous Symbols and Arrows 32 | Private Use Area 33 | Arabic Presentation Forms-B 34 | Specials 35 | Mathematical Alphanumeric Symbols 36 | -------------------------------------------------------------------------------- /scripts/check-width.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from mmtool.unicode import target_width_of, block_width_of, unicode_block_of 3 | import fontforge 4 | 5 | 6 | font = fontforge.open(sys.argv[1]) 7 | font_tag = sys.argv[2] 8 | base_em = font.em 9 | 10 | for glyph in font.glyphs(): 11 | if glyph.unicode == -1: 12 | continue 13 | 14 | codepoint = glyph.unicode 15 | str_expr = f"U+{codepoint:06X} '{chr(codepoint)}'" 16 | _, _, block_name = unicode_block_of(codepoint) 17 | 18 | block_info = block_width_of(block_name) 19 | target_width = target_width_of(codepoint) 20 | glyph_width = "full" if glyph.width / base_em > 0.95 else "half" 21 | 22 | if block_info is None: 23 | continue 24 | block_width, block_font_tag = block_info 25 | 26 | if block_width == "complex": 27 | # print(f"[I] {str_expr} has complex width, skipping") 28 | continue 29 | 30 | if block_font_tag is not None and block_font_tag != font_tag: 31 | # print(f"[I] {str_expr} will not be copied, skipping") 32 | continue 33 | 34 | if target_width != glyph_width: 35 | print(f"[E] {str_expr} has different width") 36 | print(f" Unicode Block : {block_name}") 37 | print(f" East Asian Width : {target_width}") 38 | print(f" Glyph : {glyph_width}") 39 | continue 40 | 41 | # print(f"[I] {str_expr} has consistent width: {block_width}") 42 | -------------------------------------------------------------------------------- /docs/blocks-genei.txt: -------------------------------------------------------------------------------- 1 | Basic Latin 2 | Latin-1 Supplement 3 | Latin Extended-A 4 | Latin Extended-B 5 | IPA Extensions 6 | Spacing Modifier Letters 7 | Combining Diacritical Marks 8 | Greek and Coptic 9 | Cyrillic 10 | Cyrillic Supplement 11 | Hebrew 12 | Thai 13 | Phonetic Extensions 14 | Phonetic Extensions Supplement 15 | Combining Diacritical Marks Supplement 16 | Latin Extended Additional 17 | Greek Extended 18 | General Punctuation 19 | Superscripts and Subscripts 20 | Currency Symbols 21 | Combining Diacritical Marks for Symbols 22 | Letterlike Symbols 23 | Number Forms 24 | Arrows 25 | Mathematical Operators 26 | Miscellaneous Technical 27 | Control Pictures 28 | Enclosed Alphanumerics 29 | Box Drawing 30 | Block Elements 31 | Geometric Shapes 32 | Miscellaneous Symbols 33 | Dingbats 34 | Miscellaneous Mathematical Symbols-A 35 | Supplemental Arrows-B 36 | Miscellaneous Mathematical Symbols-B 37 | Supplemental Mathematical Operators 38 | Miscellaneous Symbols and Arrows 39 | Latin Extended-C 40 | Supplemental Punctuation 41 | CJK Radicals Supplement 42 | Kangxi Radicals 43 | Ideographic Description Characters 44 | CJK Symbols and Punctuation 45 | Hiragana 46 | Katakana 47 | Katakana Phonetic Extensions 48 | Enclosed CJK Letters and Months 49 | CJK Compatibility 50 | CJK Unified Ideographs Extension A 51 | CJK Unified Ideographs 52 | Latin Extended-D 53 | CJK Compatibility Ideographs 54 | Alphabetic Presentation Forms 55 | Vertical Forms 56 | CJK Compatibility Forms 57 | Small Form Variants 58 | Halfwidth and Fullwidth Forms 59 | Kana Supplement 60 | Mathematical Alphanumeric Symbols 61 | Enclosed Alphanumeric Supplement 62 | Enclosed Ideographic Supplement 63 | CJK Unified Ideographs Extension B 64 | CJK Unified Ideographs Extension C 65 | CJK Unified Ideographs Extension D 66 | CJK Compatibility Ideographs Supplement 67 | -------------------------------------------------------------------------------- /docs/unicode-blocks-construction.md: -------------------------------------------------------------------------------- 1 | # 収録する Unicode Block について 2 | 3 | * Unicode Block から収録する文字を個別に決定する 4 | * **太字**は Fullwidth 扱い 5 | * ~~取り消し線~~は固有でも収録しない 6 | 7 | ## 源暎モノゴ固有 8 | * ~~Cyrillic Supplement~~ (JetBrains Mono で合成) 9 | * Hebrew 10 | * Thai 11 | * ~~Phonetic Extensions~~ (JetBrains Mono との整合性) 12 | * ~~Phonetic Extensions Supplement~~ (JetBrains Mono との整合性) 13 | * ~~Combining Diacritical Marks Supplement~~ (JetBrains Mono との整合性) 14 | * ~~Greek Extended~~ (JetBrains Mono で合成) 15 | * ~~Combining Diacritical Marks for Symbols~~ (JetBrains Mono との整合性) 16 | * Enclosed Alphanumerics 17 | * ~~Latin Extended-C~~ (JetBrains Mono で合成) 18 | * ~~Supplemental Punctuation~~ (JetBrains Mono との整合性) 19 | * **CJK Radicals Supplement** 20 | * **Kangxi Radicals** 21 | * **Ideographic Description Characters** 22 | * **CJK Symbols and Punctuation** 23 | * **Hiragana** 24 | * **Katakana** 25 | * **Katakana Phonetic Extensions** 26 | * **Enclosed CJK Letters and Months** 27 | * **CJK Compatibility** 28 | * **CJK Unified Ideographs Extension A** 29 | * **CJK Unified Ideographs** 30 | * ~~Latin Extended-D~~ (JetBrains Mono で合成) 31 | * **CJK Compatibility Ideographs** 32 | * ~~Alphabetic Presentation Forms~~ (JetBrains Mono との整合性) 33 | * ~~Vertical Forms~~ (対応しない) 34 | * **CJK Compatibility Forms** 35 | * ~~Small Form Variants~~ (JetBrains Mono との整合性) 36 | * Halfwidth and **Fullwidth** Forms (幅混合のため詳細にチェック) 37 | * **Kana Supplement** 38 | * Enclosed Alphanumeric Supplement 39 | * **Enclosed Ideographic Supplement** 40 | * **CJK Unified Ideographs Extension B** 41 | * **CJK Unified Ideographs Extension C** 42 | * **CJK Unified Ideographs Extension D** 43 | * **CJK Compatibility Ideographs Supplement** 44 | 45 | ## JetBrains Mono 固有 46 | * Gujarati 47 | * Supplemental Arrows-A 48 | * Private Use Area 49 | * Arabic Presentation Forms-B 50 | * Specials 51 | -------------------------------------------------------------------------------- /scripts/mmtool/data.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | 4 | REPO_ROOT = Path(__file__).parent.parent.parent 5 | REPO_FONTS = REPO_ROOT / "fonts" 6 | REPO_DIST = REPO_ROOT / "dist" 7 | 8 | WEIGHT_VALUES = { 9 | "Regular": 400, 10 | "Bold": 700, 11 | } 12 | 13 | 14 | class Style: 15 | _weight: str | None 16 | _italic: bool 17 | 18 | def __init__(self, weight: str | None, italic: bool): 19 | self._weight = weight 20 | self._italic = italic 21 | 22 | def weight_name(self) -> str: 23 | if self._weight is None: 24 | return "Regular" 25 | else: 26 | return self._weight 27 | 28 | def weight_value(self): 29 | return WEIGHT_VALUES[self.weight_name()] 30 | 31 | def is_italic(self) -> bool: 32 | return self._italic 33 | 34 | def subfamily_name(self) -> str: 35 | name = "Italic" if self._italic else "" 36 | if self._weight is not None: 37 | name = f"{self._weight} {name}" 38 | if name == "": 39 | name = "Regular" 40 | return name 41 | 42 | def subfamily_id(self) -> str: 43 | return self.subfamily_name().replace(" ", "") 44 | 45 | 46 | class Target: 47 | _version: str 48 | _style: Style 49 | _gen_ei_mono_go: str 50 | _jetbrains_mono: str 51 | 52 | def __init__(self, version: str, style: Style, g: str, j: str): 53 | self._version = version 54 | self._style = style 55 | self._gen_ei_mono_go = g 56 | self._jetbrains_mono = j 57 | 58 | def version(self) -> str: 59 | return self._version 60 | 61 | def style(self) -> Style: 62 | return self._style 63 | 64 | def gemg_path(self) -> str: 65 | return str(REPO_FONTS / "gen-ei-mono-go" / self._gen_ei_mono_go) 66 | 67 | def jbm_path(self) -> str: 68 | return str(REPO_FONTS / "jetbrains-mono" / self._jetbrains_mono) 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Momiage Mono 2 | 3 | ![Momiage Mono Example](./docs/MomiageMono.png) 4 | 5 | ## ダウンロード 6 | 現在の最新バージョンは **Build 1.1-20221222** です。 7 | 8 | [Release ページ](https://github.com/kb10uy/MomiageMono/releases)から `MomiageMono-1.1-20221222.zip` をダウンロードしてください。 9 | 10 | ## 概要 11 | Momiage Mono は、以下のフォントを合成した主にコーディングを想定した等幅フォントです。 12 | 13 | - 欧文: [JetBrains Mono](https://www.jetbrains.com/ja-jp/lp/mono/) 14 | - 日本語: [源暎モノゴ](https://okoneya.jp/font/genei-mono-go.html) 15 | - アイコン類: [Nerd Fonts](https://www.nerdfonts.com/) 16 | 17 | ## 特徴 18 | 19 | ### 源暎モノゴ由来の豊富な漢字 20 | ![JIS 第 1,2 水準に含まれない漢字の例](./docs/various-kanji.png) 21 | 22 | 源暎モノゴの源流にある源ノ角ゴシックには、JIS 第 1,2 水準だけでなく豊富な漢字が収録されています。より多様な文章をこのフォントひとつで表示することができます。 23 | 24 | ### JetBrains Mono 由来の記号のリガチャ 25 | ![ソースコード内での記号のリガチャの例](./docs/symbol-ligature.png) 26 | 27 | JetBrains Mono にはコーディング用途に特化した記号類のリガチャが収録されており、 Momiage Mono はこれを継承しています。 28 | 29 | ### Nerd Font 由来の多様なアイコン 30 | ![Tabby でアイコンを表示している例](./docs/nerdfont-icon.png) 31 | 32 | Momiage Mono には Nerd Font の Font Patcher を適用しています。色々なアイコンが表示されてたのしい。 33 | 34 | ### 5:3 の半角グリフに対応した 5:6 相当の全角グリフ 35 | JetBrains Mono のグリフは比率が 5:3 となっているため、一般的な日本語フォントとそのまま合成すると「半角 2 文字と全角 1 文字の幅が一致しない」ということが起こります。エディタならともかく、コンソールアプリなど厳密にこの要件を要求する場面ではこれは致命的といえます。 36 | 37 | JetBrains Mono を合成しているフォントとしては他に [UDEV Gothic](https://github.com/yuru7/udev-gothic) があります。 UDEV Gothic では JetBrains Mono のグリフの横幅を縮小したものと 5:3 のまま合成したバリエーションが用意されています。 38 | 39 | Momiage Mono では、**全角文字が 5:6 となるように拡大・移動する**という方針によって上記の問題を解決しています。この代償として、**源暎モノゴ由来の全角グリフのほとんどはフォントの Ascent より上にはみ出しています**(OS/2 Win Ascent の値よりは下に収まっています)。行が重なってしまう場合、line-height を 1.2~1.3 程度に設定してご利用ください。なお、 JetBrains Mono 由来のグリフは line-height が 1.0 の状態でも重ならないようになっています。 40 | 41 | ### `East_Asian_Width: A` の半角への統一 42 | Unicode には East_Asian_Width という属性があり、かつて東アジア圏で全角として、それ以外の地域では半角として扱われてきた文字については `A (Ambiguous)` が割り当てられています。 43 | 44 | Momiage Mono では、**これらの文字を全て半角に統一しています**。 JetBrains Mono に収録されているグリフはそのまま、源暎モノゴに収録されているグリフは半角に収まるように縮小・移動する処理を施しています。これにより、日本語表示を想定していないターミナルアプリでの表示のずれが軽減されることが期待されます。 45 | 46 | ## 自分でビルドする 47 | 48 | ### 必要なもの 49 | * Python (3.10 以上) 50 | * [FontForge](https://fontforge.org/en-US/) 51 | 52 | ### 手順 53 | 1. 源暎モノゴを上記リンクからダウンロードして、 `dist/gen-ei-mono-go/GenEiMonoGothic_v1.0.zip` に配置 54 | 2. `bash setup.bash` 55 | 3. `bash generate.bash` 56 | 57 | ## ライセンス 58 | - 合成されたフォントは SIL Open Font License 1.1 でライセンスされています。 59 | - 合成用のスクリプトは Apache License 2.0 でライセンスされています。 60 | -------------------------------------------------------------------------------- /scripts/mmtool/font_action.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Callable 2 | from functools import reduce 3 | from .data import Target 4 | import fontforge 5 | import psMat 6 | 7 | 8 | def compose_transforms(transforms: list[tuple]) -> tuple: 9 | return reduce( 10 | lambda composed, next: psMat.compose(next, composed), 11 | transforms, 12 | psMat.identity() 13 | ) 14 | 15 | 16 | def fetch_glyph_names(font: fontforge.font, predicate: Callable[[fontforge.glyph], bool] | None) -> list[str]: 17 | glyph_names = [] 18 | 19 | # font.glyphs() would cause SEGV 20 | font.selection.all() 21 | for glyph in font.selection.byGlyphs: 22 | if predicate is not None and not predicate(glyph): 23 | continue 24 | glyph_names.append(glyph.glyphname) 25 | font.selection.none() 26 | 27 | return glyph_names 28 | 29 | 30 | def create_insufficient_slots(font: fontforge.font, glyph_names: list[str]): 31 | new_slots = 0 32 | for glyph_name in glyph_names: 33 | if font.findEncodingSlot(glyph_name) == -1: 34 | font.createChar(-1, glyph_name) 35 | new_slots += 1 36 | 37 | print(f"==> Created {new_slots} new slots") 38 | 39 | 40 | def copy_glyphs(dest: fontforge.font, src: fontforge.font, glyph_names: list[str]): 41 | for i, glyph_name in enumerate(glyph_names): 42 | if i % 500 == 0: 43 | print(f"==> Copied {i} glyphs") 44 | 45 | src.selection.select(glyph_name) 46 | src.copy() 47 | dest.selection.select(glyph_name) 48 | dest.paste() 49 | 50 | 51 | def set_metrics(font: fontforge.font): 52 | font.ascent = 800 53 | font.descent = 200 54 | font.os2_version = 4 55 | font.os2_use_typo_metrics = False 56 | font.os2_winascent_add = False 57 | font.os2_windescent_add = False 58 | font.os2_typoascent_add = False 59 | font.os2_typodescent_add = False 60 | font.hhea_ascent_add = False 61 | font.hhea_descent_add = False 62 | font.os2_winascent = 1100 63 | font.os2_windescent = 200 64 | font.os2_typoascent = 800 65 | font.os2_typodescent = -200 66 | font.hhea_ascent = 1100 67 | font.hhea_descent = -200 68 | font.em = 2048 69 | 70 | 71 | def set_info(font: fontforge.font, target: Target): 72 | target_style = target.style() 73 | 74 | font.fontname = f"MomiageMono-{target_style.subfamily_id()}" 75 | font.familyname = f"Momiage Mono" 76 | font.weight = target_style.weight_name() 77 | font.italicangle = -9 if target_style.is_italic() else 0 78 | font.os2_weight = target_style.weight_value() 79 | font.gasp_version = 1 80 | font.gasp = _generate_gasp() 81 | font.sfnt_names = _generate_sfnt_names(target) 82 | 83 | 84 | def _generate_gasp() -> tuple: 85 | return ( 86 | (8, ("antialias",)), 87 | (13, ("antialias", "symmetric-smoothing")), 88 | (65535, ("antialias", "symmetric-smoothing")), 89 | ) 90 | 91 | 92 | def _generate_sfnt_names(target: Target) -> tuple: 93 | subfamily_name = target._style.subfamily_name() 94 | subfamily_id = target._style.subfamily_id() 95 | 96 | sfnt_dict = { 97 | "Copyright": "\n".join([ 98 | "Momiage Mono: (C) 2022 kb10uy", 99 | "", 100 | "GenEi Mono Gothic: (C) 2020 おたもん", 101 | "JetBrains Mono: (C) 2020 The JetBrains Mono Project.", 102 | "Nerd Font: (C) 2014 Ryan L McIntyre.", 103 | ]), 104 | "Family": f"Momiage Mono", 105 | "SubFamily": subfamily_name, 106 | "UniqueID": f"{target.version()};MomiageMono-{subfamily_id}", 107 | "Fullname": f"Momiage Mono {subfamily_name}", 108 | "Version": target.version(), 109 | "Vendor URL": "https://github.com/kb10uy/MomiageMono", 110 | } 111 | 112 | sfnt_names = [] 113 | for strid, value in sfnt_dict.items(): 114 | sfnt_names.append(("English (US)", strid, value)) 115 | 116 | return tuple(sfnt_names) 117 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 kb10uy (https://github.com/kb10uy/MomiageMono). 2 | 3 | Copyright 2020 おたもん (http://okoneya.jp/font/), with Reserved Font Name '源暎'. 4 | Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono). 5 | Copyright 2014 Ryan L McIntyre (https://ryanlmcintyre.com). 6 | 7 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 8 | This license is copied below, and is also available with a FAQ at: 9 | https://scripts.sil.org/OFL 10 | 11 | ----------------------------------------------------------- 12 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 13 | ----------------------------------------------------------- 14 | 15 | PREAMBLE 16 | The goals of the Open Font License (OFL) are to stimulate worldwide 17 | development of collaborative font projects, to support the font creation 18 | efforts of academic and linguistic communities, and to provide a free and 19 | open framework in which fonts may be shared and improved in partnership 20 | with others. 21 | 22 | The OFL allows the licensed fonts to be used, studied, modified and 23 | redistributed freely as long as they are not sold by themselves. The 24 | fonts, including any derivative works, can be bundled, embedded, 25 | redistributed and/or sold with any software provided that any reserved 26 | names are not used by derivative works. The fonts and derivatives, 27 | however, cannot be released under any other type of license. The 28 | requirement for fonts to remain under this license does not apply 29 | to any document created using the fonts or their derivatives. 30 | 31 | DEFINITIONS 32 | "Font Software" refers to the set of files released by the Copyright 33 | Holder(s) under this license and clearly marked as such. This may 34 | include source files, build scripts and documentation. 35 | 36 | "Reserved Font Name" refers to any names specified as such after the 37 | copyright statement(s). 38 | 39 | "Original Version" refers to the collection of Font Software components as 40 | distributed by the Copyright Holder(s). 41 | 42 | "Modified Version" refers to any derivative made by adding to, deleting, 43 | or substituting -- in part or in whole -- any of the components of the 44 | Original Version, by changing formats or by porting the Font Software to a 45 | new environment. 46 | 47 | "Author" refers to any designer, engineer, programmer, technical 48 | writer or other person who contributed to the Font Software. 49 | 50 | PERMISSION & CONDITIONS 51 | Permission is hereby granted, free of charge, to any person obtaining 52 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 53 | redistribute, and sell modified and unmodified copies of the Font 54 | Software, subject to the following conditions: 55 | 56 | 1) Neither the Font Software nor any of its individual components, 57 | in Original or Modified Versions, may be sold by itself. 58 | 59 | 2) Original or Modified Versions of the Font Software may be bundled, 60 | redistributed and/or sold with any software, provided that each copy 61 | contains the above copyright notice and this license. These can be 62 | included either as stand-alone text files, human-readable headers or 63 | in the appropriate machine-readable metadata fields within text or 64 | binary files as long as those fields can be easily viewed by the user. 65 | 66 | 3) No Modified Version of the Font Software may use the Reserved Font 67 | Name(s) unless explicit written permission is granted by the corresponding 68 | Copyright Holder. This restriction only applies to the primary font name as 69 | presented to the users. 70 | 71 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 72 | Software shall not be used to promote, endorse or advertise any 73 | Modified Version, except to acknowledge the contribution(s) of the 74 | Copyright Holder(s) and the Author(s) or with their explicit written 75 | permission. 76 | 77 | 5) The Font Software, modified or unmodified, in part or in whole, 78 | must be distributed entirely under this license, and must not be 79 | distributed under any other license. The requirement for fonts to 80 | remain under this license does not apply to any document created 81 | using the Font Software. 82 | 83 | TERMINATION 84 | This license becomes null and void if any of the above conditions are 85 | not met. 86 | 87 | DISCLAIMER 88 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 91 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 92 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 93 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 94 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 95 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 96 | OTHER DEALINGS IN THE FONT SOFTWARE. 97 | -------------------------------------------------------------------------------- /scripts/momiage-mono.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | from math import radians 3 | from pathlib import Path 4 | from mmtool import font_action 5 | from mmtool.data import Style, Target, REPO_DIST 6 | from mmtool.unicode import block_width_of, unicode_block_of, target_width_of 7 | import fontforge 8 | import psMat 9 | 10 | 11 | GENEI_TRANSFORMS: dict[tuple[str, str]] = { 12 | ("uniform", False): font_action.compose_transforms([ 13 | psMat.scale(1.1, 1.1), 14 | psMat.translate(102, 0) 15 | ]), 16 | ("full2half", False): font_action.compose_transforms([ 17 | psMat.scale(0.8, 0.8), 18 | psMat.translate(-205, 0) 19 | ]), 20 | ("half2full", False): font_action.compose_transforms([ 21 | psMat.scale(1.25, 1.25), 22 | psMat.translate(589, 0) 23 | ]), 24 | ("uniform", True): font_action.compose_transforms([ 25 | psMat.scale(1.1, 1.1), 26 | psMat.skew(radians(9)), 27 | psMat.translate(102, 0) 28 | ]), 29 | ("full2half", True): font_action.compose_transforms([ 30 | psMat.scale(0.8, 0.8), 31 | psMat.skew(radians(9)), 32 | psMat.translate(-205, 0) 33 | ]), 34 | ("half2full", True): font_action.compose_transforms([ 35 | psMat.scale(1.25, 1.25), 36 | psMat.skew(radians(9)), 37 | psMat.translate(589, 0) 38 | ]), 39 | } 40 | 41 | VERSION = f"1.1-{date.today()}" 42 | TARGETS = [ 43 | Target( 44 | VERSION, 45 | Style(None, False), 46 | "GenEiMonoGothic-Regular.ttf", 47 | "JetBrainsMono-Regular.ttf" 48 | ), 49 | Target( 50 | VERSION, 51 | Style(None, True), 52 | "GenEiMonoGothic-Regular.ttf", 53 | "JetBrainsMono-Italic.ttf" 54 | ), 55 | Target( 56 | VERSION, 57 | Style("Bold", False), 58 | "GenEiMonoGothic-Bold.ttf", 59 | "JetBrainsMono-Bold.ttf" 60 | ), 61 | Target( 62 | VERSION, 63 | Style("Bold", True), 64 | "GenEiMonoGothic-Bold.ttf", 65 | "JetBrainsMono-BoldItalic.ttf" 66 | ), 67 | ] 68 | 69 | 70 | def _copy_genei_mono_gothic(font: fontforge.font, target: Target): 71 | target_style = target.style() 72 | make_oblique = target_style.is_italic() 73 | 74 | gemg_font = fontforge.open(target.gemg_path()) 75 | gemg_font.em = 2048 76 | 77 | gemg_font.selection.all() 78 | gemg_glyph_names = [] 79 | for glyph in gemg_font.selection.byGlyphs: 80 | if glyph.unicode == -1: 81 | continue 82 | 83 | block = unicode_block_of(glyph.unicode) 84 | block_info = block_width_of(block[2]) 85 | if block_info is None: 86 | continue 87 | 88 | block_font_tag = block_info[1] 89 | if block_font_tag is not None and block_font_tag != "gemg": 90 | continue 91 | 92 | # TODO: deal with zero-width glyph 93 | glyph_width = "full" if glyph.width / 2048 > 0.95 else "half" 94 | target_width = target_width_of(glyph.unicode) 95 | 96 | if glyph_width == target_width: 97 | glyph.transform(GENEI_TRANSFORMS[("uniform", make_oblique)]) 98 | elif glyph_width == "full": 99 | glyph.transform(GENEI_TRANSFORMS[("full2half", make_oblique)]) 100 | elif glyph_width == "half": 101 | glyph.transform(GENEI_TRANSFORMS[("half2full", make_oblique)]) 102 | glyph.width = 2456 if target_width == "full" else 1228 103 | 104 | gemg_glyph_names.append(glyph.glyphname) 105 | 106 | font_action.create_insufficient_slots(font, gemg_glyph_names) 107 | font_action.copy_glyphs(font, gemg_font, gemg_glyph_names) 108 | 109 | gemg_font.close() 110 | 111 | 112 | def _copy_jetbrains_mono(font: fontforge.font, target: Target): 113 | jbm_font = fontforge.open(target.jbm_path()) 114 | jbm_font.em = 2048 115 | 116 | font.importLookups(jbm_font, jbm_font.gsub_lookups) 117 | font.importLookups(jbm_font, jbm_font.gpos_lookups) 118 | 119 | jbm_glyph_names = font_action.fetch_glyph_names(jbm_font, None) 120 | font_action.create_insufficient_slots(font, jbm_glyph_names) 121 | font_action.copy_glyphs(font, jbm_font, jbm_glyph_names) 122 | 123 | jbm_font.close() 124 | 125 | 126 | def generate_momiage_mono(target: Target, filename: Path): 127 | # Momiage Mono 128 | font = fontforge.font() 129 | font.encoding = "UnicodeFull" 130 | font_action.set_metrics(font) 131 | 132 | # GenEi Mono Gothic 133 | print("=> Copying glyphs from GenEi Mono Gothic") 134 | _copy_genei_mono_gothic(font, target) 135 | 136 | # JetBrains Mono 137 | print("=> Copying glyphs from JetBrains Mono") 138 | _copy_jetbrains_mono(font, target) 139 | 140 | # Finalize 141 | font_action.set_info(font, target) 142 | font.generate( 143 | str(filename), 144 | "", 145 | ("short-post", "PfEd-lookups", "opentype") 146 | ) 147 | 148 | 149 | for target in TARGETS: 150 | target_style = target.style() 151 | target_filename = REPO_DIST / \ 152 | f"MomiageMono-{target_style.subfamily_id()}.ttf" 153 | generate_momiage_mono(target, target_filename) 154 | -------------------------------------------------------------------------------- /scripts/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /scripts/mmtool/unicode.py: -------------------------------------------------------------------------------- 1 | from unicodedata import east_asian_width 2 | import bisect 3 | 4 | 5 | UNICODE_BLOCK_NO = (0x0000, 0x1FFFFF, "No_Block") 6 | UNICODE_BLOCKS: list[tuple[int, int, str]] = [ 7 | (0x0000, 0x007F, "Basic Latin"), 8 | (0x0080, 0x00FF, "Latin-1 Supplement"), 9 | (0x0100, 0x017F, "Latin Extended-A"), 10 | (0x0180, 0x024F, "Latin Extended-B"), 11 | (0x0250, 0x02AF, "IPA Extensions"), 12 | (0x02B0, 0x02FF, "Spacing Modifier Letters"), 13 | (0x0300, 0x036F, "Combining Diacritical Marks"), 14 | (0x0370, 0x03FF, "Greek and Coptic"), 15 | (0x0400, 0x04FF, "Cyrillic"), 16 | (0x0500, 0x052F, "Cyrillic Supplement"), 17 | (0x0530, 0x058F, "Armenian"), 18 | (0x0590, 0x05FF, "Hebrew"), 19 | (0x0600, 0x06FF, "Arabic"), 20 | (0x0700, 0x074F, "Syriac"), 21 | (0x0750, 0x077F, "Arabic Supplement"), 22 | (0x0780, 0x07BF, "Thaana"), 23 | (0x07C0, 0x07FF, "NKo"), 24 | (0x0800, 0x083F, "Samaritan"), 25 | (0x0840, 0x085F, "Mandaic"), 26 | (0x0860, 0x086F, "Syriac Supplement"), 27 | (0x0870, 0x089F, "Arabic Extended-B"), 28 | (0x08A0, 0x08FF, "Arabic Extended-A"), 29 | (0x0900, 0x097F, "Devanagari"), 30 | (0x0980, 0x09FF, "Bengali"), 31 | (0x0A00, 0x0A7F, "Gurmukhi"), 32 | (0x0A80, 0x0AFF, "Gujarati"), 33 | (0x0B00, 0x0B7F, "Oriya"), 34 | (0x0B80, 0x0BFF, "Tamil"), 35 | (0x0C00, 0x0C7F, "Telugu"), 36 | (0x0C80, 0x0CFF, "Kannada"), 37 | (0x0D00, 0x0D7F, "Malayalam"), 38 | (0x0D80, 0x0DFF, "Sinhala"), 39 | (0x0E00, 0x0E7F, "Thai"), 40 | (0x0E80, 0x0EFF, "Lao"), 41 | (0x0F00, 0x0FFF, "Tibetan"), 42 | (0x1000, 0x109F, "Myanmar"), 43 | (0x10A0, 0x10FF, "Georgian"), 44 | (0x1100, 0x11FF, "Hangul Jamo"), 45 | (0x1200, 0x137F, "Ethiopic"), 46 | (0x1380, 0x139F, "Ethiopic Supplement"), 47 | (0x13A0, 0x13FF, "Cherokee"), 48 | (0x1400, 0x167F, "Unified Canadian Aboriginal Syllabics"), 49 | (0x1680, 0x169F, "Ogham"), 50 | (0x16A0, 0x16FF, "Runic"), 51 | (0x1700, 0x171F, "Tagalog"), 52 | (0x1720, 0x173F, "Hanunoo"), 53 | (0x1740, 0x175F, "Buhid"), 54 | (0x1760, 0x177F, "Tagbanwa"), 55 | (0x1780, 0x17FF, "Khmer"), 56 | (0x1800, 0x18AF, "Mongolian"), 57 | (0x18B0, 0x18FF, "Unified Canadian Aboriginal Syllabics Extended"), 58 | (0x1900, 0x194F, "Limbu"), 59 | (0x1950, 0x197F, "Tai Le"), 60 | (0x1980, 0x19DF, "New Tai Lue"), 61 | (0x19E0, 0x19FF, "Khmer Symbols"), 62 | (0x1A00, 0x1A1F, "Buginese"), 63 | (0x1A20, 0x1AAF, "Tai Tham"), 64 | (0x1AB0, 0x1AFF, "Combining Diacritical Marks Extended"), 65 | (0x1B00, 0x1B7F, "Balinese"), 66 | (0x1B80, 0x1BBF, "Sundanese"), 67 | (0x1BC0, 0x1BFF, "Batak"), 68 | (0x1C00, 0x1C4F, "Lepcha"), 69 | (0x1C50, 0x1C7F, "Ol Chiki"), 70 | (0x1C80, 0x1C8F, "Cyrillic Extended-C"), 71 | (0x1C90, 0x1CBF, "Georgian Extended"), 72 | (0x1CC0, 0x1CCF, "Sundanese Supplement"), 73 | (0x1CD0, 0x1CFF, "Vedic Extensions"), 74 | (0x1D00, 0x1D7F, "Phonetic Extensions"), 75 | (0x1D80, 0x1DBF, "Phonetic Extensions Supplement"), 76 | (0x1DC0, 0x1DFF, "Combining Diacritical Marks Supplement"), 77 | (0x1E00, 0x1EFF, "Latin Extended Additional"), 78 | (0x1F00, 0x1FFF, "Greek Extended"), 79 | (0x2000, 0x206F, "General Punctuation"), 80 | (0x2070, 0x209F, "Superscripts and Subscripts"), 81 | (0x20A0, 0x20CF, "Currency Symbols"), 82 | (0x20D0, 0x20FF, "Combining Diacritical Marks for Symbols"), 83 | (0x2100, 0x214F, "Letterlike Symbols"), 84 | (0x2150, 0x218F, "Number Forms"), 85 | (0x2190, 0x21FF, "Arrows"), 86 | (0x2200, 0x22FF, "Mathematical Operators"), 87 | (0x2300, 0x23FF, "Miscellaneous Technical"), 88 | (0x2400, 0x243F, "Control Pictures"), 89 | (0x2440, 0x245F, "Optical Character Recognition"), 90 | (0x2460, 0x24FF, "Enclosed Alphanumerics"), 91 | (0x2500, 0x257F, "Box Drawing"), 92 | (0x2580, 0x259F, "Block Elements"), 93 | (0x25A0, 0x25FF, "Geometric Shapes"), 94 | (0x2600, 0x26FF, "Miscellaneous Symbols"), 95 | (0x2700, 0x27BF, "Dingbats"), 96 | (0x27C0, 0x27EF, "Miscellaneous Mathematical Symbols-A"), 97 | (0x27F0, 0x27FF, "Supplemental Arrows-A"), 98 | (0x2800, 0x28FF, "Braille Patterns"), 99 | (0x2900, 0x297F, "Supplemental Arrows-B"), 100 | (0x2980, 0x29FF, "Miscellaneous Mathematical Symbols-B"), 101 | (0x2A00, 0x2AFF, "Supplemental Mathematical Operators"), 102 | (0x2B00, 0x2BFF, "Miscellaneous Symbols and Arrows"), 103 | (0x2C00, 0x2C5F, "Glagolitic"), 104 | (0x2C60, 0x2C7F, "Latin Extended-C"), 105 | (0x2C80, 0x2CFF, "Coptic"), 106 | (0x2D00, 0x2D2F, "Georgian Supplement"), 107 | (0x2D30, 0x2D7F, "Tifinagh"), 108 | (0x2D80, 0x2DDF, "Ethiopic Extended"), 109 | (0x2DE0, 0x2DFF, "Cyrillic Extended-A"), 110 | (0x2E00, 0x2E7F, "Supplemental Punctuation"), 111 | (0x2E80, 0x2EFF, "CJK Radicals Supplement"), 112 | (0x2F00, 0x2FDF, "Kangxi Radicals"), 113 | (0x2FF0, 0x2FFF, "Ideographic Description Characters"), 114 | (0x3000, 0x303F, "CJK Symbols and Punctuation"), 115 | (0x3040, 0x309F, "Hiragana"), 116 | (0x30A0, 0x30FF, "Katakana"), 117 | (0x3100, 0x312F, "Bopomofo"), 118 | (0x3130, 0x318F, "Hangul Compatibility Jamo"), 119 | (0x3190, 0x319F, "Kanbun"), 120 | (0x31A0, 0x31BF, "Bopomofo Extended"), 121 | (0x31C0, 0x31EF, "CJK Strokes"), 122 | (0x31F0, 0x31FF, "Katakana Phonetic Extensions"), 123 | (0x3200, 0x32FF, "Enclosed CJK Letters and Months"), 124 | (0x3300, 0x33FF, "CJK Compatibility"), 125 | (0x3400, 0x4DBF, "CJK Unified Ideographs Extension A"), 126 | (0x4DC0, 0x4DFF, "Yijing Hexagram Symbols"), 127 | (0x4E00, 0x9FFF, "CJK Unified Ideographs"), 128 | (0xA000, 0xA48F, "Yi Syllables"), 129 | (0xA490, 0xA4CF, "Yi Radicals"), 130 | (0xA4D0, 0xA4FF, "Lisu"), 131 | (0xA500, 0xA63F, "Vai"), 132 | (0xA640, 0xA69F, "Cyrillic Extended-B"), 133 | (0xA6A0, 0xA6FF, "Bamum"), 134 | (0xA700, 0xA71F, "Modifier Tone Letters"), 135 | (0xA720, 0xA7FF, "Latin Extended-D"), 136 | (0xA800, 0xA82F, "Syloti Nagri"), 137 | (0xA830, 0xA83F, "Common Indic Number Forms"), 138 | (0xA840, 0xA87F, "Phags-pa"), 139 | (0xA880, 0xA8DF, "Saurashtra"), 140 | (0xA8E0, 0xA8FF, "Devanagari Extended"), 141 | (0xA900, 0xA92F, "Kayah Li"), 142 | (0xA930, 0xA95F, "Rejang"), 143 | (0xA960, 0xA97F, "Hangul Jamo Extended-A"), 144 | (0xA980, 0xA9DF, "Javanese"), 145 | (0xA9E0, 0xA9FF, "Myanmar Extended-B"), 146 | (0xAA00, 0xAA5F, "Cham"), 147 | (0xAA60, 0xAA7F, "Myanmar Extended-A"), 148 | (0xAA80, 0xAADF, "Tai Viet"), 149 | (0xAAE0, 0xAAFF, "Meetei Mayek Extensions"), 150 | (0xAB00, 0xAB2F, "Ethiopic Extended-A"), 151 | (0xAB30, 0xAB6F, "Latin Extended-E"), 152 | (0xAB70, 0xABBF, "Cherokee Supplement"), 153 | (0xABC0, 0xABFF, "Meetei Mayek"), 154 | (0xAC00, 0xD7AF, "Hangul Syllables"), 155 | (0xD7B0, 0xD7FF, "Hangul Jamo Extended-B"), 156 | (0xD800, 0xDB7F, "High Surrogates"), 157 | (0xDB80, 0xDBFF, "High Private Use Surrogates"), 158 | (0xDC00, 0xDFFF, "Low Surrogates"), 159 | (0xE000, 0xF8FF, "Private Use Area"), 160 | (0xF900, 0xFAFF, "CJK Compatibility Ideographs"), 161 | (0xFB00, 0xFB4F, "Alphabetic Presentation Forms"), 162 | (0xFB50, 0xFDFF, "Arabic Presentation Forms-A"), 163 | (0xFE00, 0xFE0F, "Variation Selectors"), 164 | (0xFE10, 0xFE1F, "Vertical Forms"), 165 | (0xFE20, 0xFE2F, "Combining Half Marks"), 166 | (0xFE30, 0xFE4F, "CJK Compatibility Forms"), 167 | (0xFE50, 0xFE6F, "Small Form Variants"), 168 | (0xFE70, 0xFEFF, "Arabic Presentation Forms-B"), 169 | (0xFF00, 0xFFEF, "Halfwidth and Fullwidth Forms"), 170 | (0xFFF0, 0xFFFF, "Specials"), 171 | (0x10000, 0x1007F, "Linear B Syllabary"), 172 | (0x10080, 0x100FF, "Linear B Ideograms"), 173 | (0x10100, 0x1013F, "Aegean Numbers"), 174 | (0x10140, 0x1018F, "Ancient Greek Numbers"), 175 | (0x10190, 0x101CF, "Ancient Symbols"), 176 | (0x101D0, 0x101FF, "Phaistos Disc"), 177 | (0x10280, 0x1029F, "Lycian"), 178 | (0x102A0, 0x102DF, "Carian"), 179 | (0x102E0, 0x102FF, "Coptic Epact Numbers"), 180 | (0x10300, 0x1032F, "Old Italic"), 181 | (0x10330, 0x1034F, "Gothic"), 182 | (0x10350, 0x1037F, "Old Permic"), 183 | (0x10380, 0x1039F, "Ugaritic"), 184 | (0x103A0, 0x103DF, "Old Persian"), 185 | (0x10400, 0x1044F, "Deseret"), 186 | (0x10450, 0x1047F, "Shavian"), 187 | (0x10480, 0x104AF, "Osmanya"), 188 | (0x104B0, 0x104FF, "Osage"), 189 | (0x10500, 0x1052F, "Elbasan"), 190 | (0x10530, 0x1056F, "Caucasian Albanian"), 191 | (0x10570, 0x105BF, "Vithkuqi"), 192 | (0x10600, 0x1077F, "Linear A"), 193 | (0x10780, 0x107BF, "Latin Extended-F"), 194 | (0x10800, 0x1083F, "Cypriot Syllabary"), 195 | (0x10840, 0x1085F, "Imperial Aramaic"), 196 | (0x10860, 0x1087F, "Palmyrene"), 197 | (0x10880, 0x108AF, "Nabataean"), 198 | (0x108E0, 0x108FF, "Hatran"), 199 | (0x10900, 0x1091F, "Phoenician"), 200 | (0x10920, 0x1093F, "Lydian"), 201 | (0x10980, 0x1099F, "Meroitic Hieroglyphs"), 202 | (0x109A0, 0x109FF, "Meroitic Cursive"), 203 | (0x10A00, 0x10A5F, "Kharoshthi"), 204 | (0x10A60, 0x10A7F, "Old South Arabian"), 205 | (0x10A80, 0x10A9F, "Old North Arabian"), 206 | (0x10AC0, 0x10AFF, "Manichaean"), 207 | (0x10B00, 0x10B3F, "Avestan"), 208 | (0x10B40, 0x10B5F, "Inscriptional Parthian"), 209 | (0x10B60, 0x10B7F, "Inscriptional Pahlavi"), 210 | (0x10B80, 0x10BAF, "Psalter Pahlavi"), 211 | (0x10C00, 0x10C4F, "Old Turkic"), 212 | (0x10C80, 0x10CFF, "Old Hungarian"), 213 | (0x10D00, 0x10D3F, "Hanifi Rohingya"), 214 | (0x10E60, 0x10E7F, "Rumi Numeral Symbols"), 215 | (0x10E80, 0x10EBF, "Yezidi"), 216 | (0x10EC0, 0x10EFF, "Arabic Extended-C"), 217 | (0x10F00, 0x10F2F, "Old Sogdian"), 218 | (0x10F30, 0x10F6F, "Sogdian"), 219 | (0x10F70, 0x10FAF, "Old Uyghur"), 220 | (0x10FB0, 0x10FDF, "Chorasmian"), 221 | (0x10FE0, 0x10FFF, "Elymaic"), 222 | (0x11000, 0x1107F, "Brahmi"), 223 | (0x11080, 0x110CF, "Kaithi"), 224 | (0x110D0, 0x110FF, "Sora Sompeng"), 225 | (0x11100, 0x1114F, "Chakma"), 226 | (0x11150, 0x1117F, "Mahajani"), 227 | (0x11180, 0x111DF, "Sharada"), 228 | (0x111E0, 0x111FF, "Sinhala Archaic Numbers"), 229 | (0x11200, 0x1124F, "Khojki"), 230 | (0x11280, 0x112AF, "Multani"), 231 | (0x112B0, 0x112FF, "Khudawadi"), 232 | (0x11300, 0x1137F, "Grantha"), 233 | (0x11400, 0x1147F, "Newa"), 234 | (0x11480, 0x114DF, "Tirhuta"), 235 | (0x11580, 0x115FF, "Siddham"), 236 | (0x11600, 0x1165F, "Modi"), 237 | (0x11660, 0x1167F, "Mongolian Supplement"), 238 | (0x11680, 0x116CF, "Takri"), 239 | (0x11700, 0x1174F, "Ahom"), 240 | (0x11800, 0x1184F, "Dogra"), 241 | (0x118A0, 0x118FF, "Warang Citi"), 242 | (0x11900, 0x1195F, "Dives Akuru"), 243 | (0x119A0, 0x119FF, "Nandinagari"), 244 | (0x11A00, 0x11A4F, "Zanabazar Square"), 245 | (0x11A50, 0x11AAF, "Soyombo"), 246 | (0x11AB0, 0x11ABF, "Unified Canadian Aboriginal Syllabics Extended-A"), 247 | (0x11AC0, 0x11AFF, "Pau Cin Hau"), 248 | (0x11B00, 0x11B5F, "Devanagari Extended-A"), 249 | (0x11C00, 0x11C6F, "Bhaiksuki"), 250 | (0x11C70, 0x11CBF, "Marchen"), 251 | (0x11D00, 0x11D5F, "Masaram Gondi"), 252 | (0x11D60, 0x11DAF, "Gunjala Gondi"), 253 | (0x11EE0, 0x11EFF, "Makasar"), 254 | (0x11F00, 0x11F5F, "Kawi"), 255 | (0x11FB0, 0x11FBF, "Lisu Supplement"), 256 | (0x11FC0, 0x11FFF, "Tamil Supplement"), 257 | (0x12000, 0x123FF, "Cuneiform"), 258 | (0x12400, 0x1247F, "Cuneiform Numbers and Punctuation"), 259 | (0x12480, 0x1254F, "Early Dynastic Cuneiform"), 260 | (0x12F90, 0x12FFF, "Cypro-Minoan"), 261 | (0x13000, 0x1342F, "Egyptian Hieroglyphs"), 262 | (0x13430, 0x1345F, "Egyptian Hieroglyph Format Controls"), 263 | (0x14400, 0x1467F, "Anatolian Hieroglyphs"), 264 | (0x16800, 0x16A3F, "Bamum Supplement"), 265 | (0x16A40, 0x16A6F, "Mro"), 266 | (0x16A70, 0x16ACF, "Tangsa"), 267 | (0x16AD0, 0x16AFF, "Bassa Vah"), 268 | (0x16B00, 0x16B8F, "Pahawh Hmong"), 269 | (0x16E40, 0x16E9F, "Medefaidrin"), 270 | (0x16F00, 0x16F9F, "Miao"), 271 | (0x16FE0, 0x16FFF, "Ideographic Symbols and Punctuation"), 272 | (0x17000, 0x187FF, "Tangut"), 273 | (0x18800, 0x18AFF, "Tangut Components"), 274 | (0x18B00, 0x18CFF, "Khitan Small Script"), 275 | (0x18D00, 0x18D7F, "Tangut Supplement"), 276 | (0x1AFF0, 0x1AFFF, "Kana Extended-B"), 277 | (0x1B000, 0x1B0FF, "Kana Supplement"), 278 | (0x1B100, 0x1B12F, "Kana Extended-A"), 279 | (0x1B130, 0x1B16F, "Small Kana Extension"), 280 | (0x1B170, 0x1B2FF, "Nushu"), 281 | (0x1BC00, 0x1BC9F, "Duployan"), 282 | (0x1BCA0, 0x1BCAF, "Shorthand Format Controls"), 283 | (0x1CF00, 0x1CFCF, "Znamenny Musical Notation"), 284 | (0x1D000, 0x1D0FF, "Byzantine Musical Symbols"), 285 | (0x1D100, 0x1D1FF, "Musical Symbols"), 286 | (0x1D200, 0x1D24F, "Ancient Greek Musical Notation"), 287 | (0x1D2C0, 0x1D2DF, "Kaktovik Numerals"), 288 | (0x1D2E0, 0x1D2FF, "Mayan Numerals"), 289 | (0x1D300, 0x1D35F, "Tai Xuan Jing Symbols"), 290 | (0x1D360, 0x1D37F, "Counting Rod Numerals"), 291 | (0x1D400, 0x1D7FF, "Mathematical Alphanumeric Symbols"), 292 | (0x1D800, 0x1DAAF, "Sutton SignWriting"), 293 | (0x1DF00, 0x1DFFF, "Latin Extended-G"), 294 | (0x1E000, 0x1E02F, "Glagolitic Supplement"), 295 | (0x1E030, 0x1E08F, "Cyrillic Extended-D"), 296 | (0x1E100, 0x1E14F, "Nyiakeng Puachue Hmong"), 297 | (0x1E290, 0x1E2BF, "Toto"), 298 | (0x1E2C0, 0x1E2FF, "Wancho"), 299 | (0x1E4D0, 0x1E4FF, "Nag Mundari"), 300 | (0x1E7E0, 0x1E7FF, "Ethiopic Extended-B"), 301 | (0x1E800, 0x1E8DF, "Mende Kikakui"), 302 | (0x1E900, 0x1E95F, "Adlam"), 303 | (0x1EC70, 0x1ECBF, "Indic Siyaq Numbers"), 304 | (0x1ED00, 0x1ED4F, "Ottoman Siyaq Numbers"), 305 | (0x1EE00, 0x1EEFF, "Arabic Mathematical Alphabetic Symbols"), 306 | (0x1F000, 0x1F02F, "Mahjong Tiles"), 307 | (0x1F030, 0x1F09F, "Domino Tiles"), 308 | (0x1F0A0, 0x1F0FF, "Playing Cards"), 309 | (0x1F100, 0x1F1FF, "Enclosed Alphanumeric Supplement"), 310 | (0x1F200, 0x1F2FF, "Enclosed Ideographic Supplement"), 311 | (0x1F300, 0x1F5FF, "Miscellaneous Symbols and Pictographs"), 312 | (0x1F600, 0x1F64F, "Emoticons"), 313 | (0x1F650, 0x1F67F, "Ornamental Dingbats"), 314 | (0x1F680, 0x1F6FF, "Transport and Map Symbols"), 315 | (0x1F700, 0x1F77F, "Alchemical Symbols"), 316 | (0x1F780, 0x1F7FF, "Geometric Shapes Extended"), 317 | (0x1F800, 0x1F8FF, "Supplemental Arrows-C"), 318 | (0x1F900, 0x1F9FF, "Supplemental Symbols and Pictographs"), 319 | (0x1FA00, 0x1FA6F, "Chess Symbols"), 320 | (0x1FA70, 0x1FAFF, "Symbols and Pictographs Extended-A"), 321 | (0x1FB00, 0x1FBFF, "Symbols for Legacy Computing"), 322 | (0x20000, 0x2A6DF, "CJK Unified Ideographs Extension B"), 323 | (0x2A700, 0x2B73F, "CJK Unified Ideographs Extension C"), 324 | (0x2B740, 0x2B81F, "CJK Unified Ideographs Extension D"), 325 | (0x2B820, 0x2CEAF, "CJK Unified Ideographs Extension E"), 326 | (0x2CEB0, 0x2EBEF, "CJK Unified Ideographs Extension F"), 327 | (0x2F800, 0x2FA1F, "CJK Compatibility Ideographs Supplement"), 328 | (0x30000, 0x3134F, "CJK Unified Ideographs Extension G"), 329 | (0x31350, 0x323AF, "CJK Unified Ideographs Extension H"), 330 | (0xE0000, 0xE007F, "Tags"), 331 | (0xE0100, 0xE01EF, "Variation Selectors Supplement"), 332 | (0xF0000, 0xFFFFF, "Supplementary Private Use Area-A"), 333 | (0x100000, 0x10FFFF, "Supplementary Private Use Area-B"), 334 | ] 335 | 336 | TARGET_BLOCKS: dict[str, tuple[str, str | None]] = { 337 | # Common 338 | "Miscellaneous Symbols": ("complex", None), 339 | "Miscellaneous Symbols and Arrows": ("half", None), 340 | 341 | # JetBrains Mono specific 342 | "Arrows": ("half", "jbm"), 343 | "Basic Latin": ("half", "jbm"), 344 | "Block Elements": ("half", "jbm"), 345 | "Box Drawing": ("half", "jbm"), 346 | "Combining Diacritical Marks": ("half", "jbm"), 347 | "Control Pictures": ("half", "jbm"), 348 | "Currency Symbols": ("half", "jbm"), 349 | "Cyrillic": ("half", "jbm"), 350 | "Dingbats": ("half", "jbm"), 351 | "General Punctuation": ("half", "jbm"), 352 | "Geometric Shapes": ("half", "jbm"), 353 | "Greek and Coptic": ("half", "jbm"), 354 | "IPA Extensions": ("half", "jbm"), 355 | "Latin Extended Additional": ("half", "jbm"), 356 | "Latin Extended-A": ("half", "jbm"), 357 | "Latin Extended-B": ("half", "jbm"), 358 | "Latin-1 Supplement": ("half", "jbm"), 359 | "Letterlike Symbols": ("half", "jbm"), 360 | "Mathematical Alphanumeric Symbols": ("half", "jbm"), 361 | "Mathematical Operators": ("half", "jbm"), 362 | "Miscellaneous Mathematical Symbols-A": ("half", "jbm"), 363 | "Miscellaneous Mathematical Symbols-B": ("half", "jbm"), 364 | "Miscellaneous Technical": ("half", "jbm"), 365 | "Number Forms": ("half", "jbm"), 366 | "Spacing Modifier Letters": ("half", "jbm"), 367 | "Superscripts and Subscripts": ("half", "jbm"), 368 | "Supplemental Arrows-B": ("half", "jbm"), 369 | "Supplemental Mathematical Operators": ("half", "jbm"), 370 | "Gujarati": ("half", "jbm"), 371 | "Supplemental Arrows-A": ("half", "jbm"), 372 | "Private Use Area": ("half", "jbm"), 373 | "Arabic Presentation Forms-B": ("half", "jbm"), 374 | "Specials": ("half", "jbm"), 375 | 376 | # GenEi specific 377 | "Hebrew": ("half", "gemg"), 378 | "Thai": ("half", "gemg"), 379 | "Enclosed Alphanumerics": ("half", "gemg"), 380 | "CJK Radicals Supplement": ("full", "gemg"), 381 | "Kangxi Radicals": ("full", "gemg"), 382 | "Ideographic Description Characters": ("full", "gemg"), 383 | "CJK Symbols and Punctuation": ("full", "gemg"), 384 | "Hiragana": ("full", "gemg"), 385 | "Katakana": ("full", "gemg"), 386 | "Katakana Phonetic Extensions": ("full", "gemg"), 387 | "Enclosed CJK Letters and Months": ("full", "gemg"), 388 | "CJK Compatibility": ("full", "gemg"), 389 | "CJK Unified Ideographs Extension A": ("full", "gemg"), 390 | "CJK Unified Ideographs": ("full", "gemg"), 391 | "CJK Compatibility Ideographs": ("full", "gemg"), 392 | "CJK Compatibility Forms": ("full", "gemg"), 393 | "Halfwidth and Fullwidth Forms": ("complex", "gemg"), 394 | "Kana Supplement": ("full", "gemg"), 395 | "Enclosed Alphanumeric Supplement": ("half", "gemg"), 396 | "Enclosed Ideographic Supplement": ("full", "gemg"), 397 | "CJK Unified Ideographs Extension B": ("full", "gemg"), 398 | "CJK Unified Ideographs Extension C": ("full", "gemg"), 399 | "CJK Unified Ideographs Extension D": ("full", "gemg"), 400 | "CJK Compatibility Ideographs Supplement": ("full", "gemg"), 401 | } 402 | 403 | TARGET_WIDTHS: dict[str, str] = { 404 | "F": "full", 405 | "H": "half", 406 | "W": "full", 407 | "Na": "half", 408 | "A": "half", 409 | "N": "half", 410 | } 411 | 412 | 413 | def unicode_block_of(codepoint: int) -> tuple[int, int, str]: 414 | def extract_end(p: tuple[int, int, str]) -> int: 415 | return p[1] 416 | 417 | block_index = bisect.bisect_left( 418 | UNICODE_BLOCKS, 419 | codepoint, 420 | key=extract_end 421 | ) 422 | if block_index >= len(UNICODE_BLOCKS): 423 | return UNICODE_BLOCK_NO 424 | 425 | block_start, block_end, _ = UNICODE_BLOCKS[block_index] 426 | if block_start <= codepoint and codepoint <= block_end: 427 | return UNICODE_BLOCKS[block_index] 428 | else: 429 | return UNICODE_BLOCK_NO 430 | 431 | 432 | def target_width_of(codepoint: int) -> str: 433 | eaw = east_asian_width(chr(codepoint)) 434 | return TARGET_WIDTHS[eaw] 435 | 436 | 437 | def block_width_of(block_name: str) -> tuple[str, str | None] | None: 438 | return TARGET_BLOCKS.get(block_name, None) 439 | --------------------------------------------------------------------------------