diff --git a/.gitignore b/.gitignore
index 2d0f56f..eb78b88 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@ bak/*
work/
error.log
build/
+release_files/
diff --git a/build.ini b/build.ini
new file mode 100644
index 0000000..347bf07
--- /dev/null
+++ b/build.ini
@@ -0,0 +1,24 @@
+[DEFAULT]
+VERSION = v2.0.0
+FONT_NAME = PlemolJP
+JP_FONT = IBM-Plex-Sans-JP/unhinted/IBMPlexSansJP-{style}.ttf
+ENG_FONT = IBM-Plex-Mono/IBMPlexMono-{style}.ttf
+HACK_FONT = hack/Hack-{style}.ttf
+SOURCE_FONTS_DIR = source
+BUILD_FONTS_DIR = build
+VENDER_NAME = TWR
+FONTFORGE_PREFIX = fontforge_
+FONTTOOLS_PREFIX = fonttools_
+IDEOGRAPHIC_SPACE = AdjustedGlyphs/ideographic_space.sfd
+ADJUST_R = AdjustedGlyphs/r-{style}.sfd
+CONSOLE_STR = Console
+WIDTH_35_STR = 35
+NERD_FONTS_STR = NF
+INVISIBLE_ZENKAKU_SPACE_STR = HS
+EM_ASCENT = 880
+EM_DESCENT = 120
+OS2_ASCENT = 950
+OS2_DESCENT = 225
+HALF_WIDTH_12 = 528
+FULL_WIDTH_35 = 1000
+ITALIC_ANGLE = 9
diff --git a/fontforge_script.py b/fontforge_script.py
new file mode 100644
index 0000000..5e2232b
--- /dev/null
+++ b/fontforge_script.py
@@ -0,0 +1,867 @@
+#!fontforge --lang=py -script
+
+# 2つのフォントを合成する
+
+import configparser
+import math
+import os
+import shutil
+import sys
+import uuid
+from decimal import ROUND_HALF_UP, Decimal
+
+import fontforge
+import psMat
+
+# iniファイルを読み込む
+settings = configparser.ConfigParser()
+settings.read("build.ini", encoding="utf-8")
+
+VERSION = settings.get("DEFAULT", "VERSION")
+FONT_NAME = settings.get("DEFAULT", "FONT_NAME")
+JP_FONT = settings.get("DEFAULT", "JP_FONT")
+ENG_FONT = settings.get("DEFAULT", "ENG_FONT")
+HACK_FONT = settings.get("DEFAULT", "HACK_FONT")
+SOURCE_FONTS_DIR = settings.get("DEFAULT", "SOURCE_FONTS_DIR")
+BUILD_FONTS_DIR = settings.get("DEFAULT", "BUILD_FONTS_DIR")
+VENDER_NAME = settings.get("DEFAULT", "VENDER_NAME")
+FONTFORGE_PREFIX = settings.get("DEFAULT", "FONTFORGE_PREFIX")
+IDEOGRAPHIC_SPACE = settings.get("DEFAULT", "IDEOGRAPHIC_SPACE")
+ADJUST_R = settings.get("DEFAULT", "ADJUST_R")
+CONSOLE_STR = settings.get("DEFAULT", "CONSOLE_STR")
+WIDTH_35_STR = settings.get("DEFAULT", "WIDTH_35_STR")
+INVISIBLE_ZENKAKU_SPACE_STR = settings.get("DEFAULT", "INVISIBLE_ZENKAKU_SPACE_STR")
+NERD_FONTS_STR = settings.get("DEFAULT", "NERD_FONTS_STR")
+EM_ASCENT = int(settings.get("DEFAULT", "EM_ASCENT"))
+EM_DESCENT = int(settings.get("DEFAULT", "EM_DESCENT"))
+OS2_ASCENT = int(settings.get("DEFAULT", "OS2_ASCENT"))
+OS2_DESCENT = int(settings.get("DEFAULT", "OS2_DESCENT"))
+HALF_WIDTH_12 = int(settings.get("DEFAULT", "HALF_WIDTH_12"))
+FULL_WIDTH_35 = int(settings.get("DEFAULT", "FULL_WIDTH_35"))
+ITALIC_ANGLE = int(settings.get("DEFAULT", "ITALIC_ANGLE"))
+
+COPYRIGHT = """[IBM Plex]
+Copyright (c) 2017 IBM Corp. https://github.com/IBM/plex
+
+[Hack]
+Copyright 2018 Source Foundry Authors https://github.com/source-foundry/Hack
+
+[Nerd Fonts]
+Copyright (c) 2014, Ryan L McIntyre https://ryanlmcintyre.com
+
+[PlemolJP]
+Copyright (c) 2021, Yuko Otawara
+""" # noqa: E501
+
+options = {}
+nerd_font = None
+
+
+def main():
+ # オプション判定
+ get_options()
+ if options.get("unknown-option"):
+ usage()
+ return
+
+ # buildディレクトリを作成する
+ if os.path.exists(BUILD_FONTS_DIR) and not options.get("do-not-delete-build-dir"):
+ shutil.rmtree(BUILD_FONTS_DIR)
+ os.mkdir(BUILD_FONTS_DIR)
+ if not os.path.exists(BUILD_FONTS_DIR):
+ os.mkdir(BUILD_FONTS_DIR)
+
+ generate_font(
+ jp_style="Regular",
+ eng_style="Regular",
+ merged_style="Regular",
+ )
+ generate_font(
+ jp_style="Bold",
+ eng_style="Bold",
+ merged_style="Bold",
+ )
+
+ # デバッグモードの場合はここで終了
+ if options.get("debug"):
+ return
+
+ generate_font(
+ jp_style="Thin",
+ eng_style="Thin",
+ merged_style="Thin",
+ )
+ generate_font(
+ jp_style="ExtraLight",
+ eng_style="ExtraLight",
+ merged_style="ExtraLight",
+ )
+ generate_font(
+ jp_style="Light",
+ eng_style="Light",
+ merged_style="Light",
+ )
+ generate_font(
+ jp_style="Text",
+ eng_style="Text",
+ merged_style="Text",
+ )
+ generate_font(
+ jp_style="Medium",
+ eng_style="Medium",
+ merged_style="Medium",
+ )
+ generate_font(
+ jp_style="SemiBold",
+ eng_style="SemiBold",
+ merged_style="SemiBold",
+ )
+
+ generate_font(
+ jp_style="Regular",
+ eng_style="Italic",
+ merged_style="Italic",
+ )
+ generate_font(
+ jp_style="Bold",
+ eng_style="BoldItalic",
+ merged_style="BoldItalic",
+ )
+ generate_font(
+ jp_style="Thin",
+ eng_style="ThinItalic",
+ merged_style="ThinItalic",
+ )
+ generate_font(
+ jp_style="ExtraLight",
+ eng_style="ExtraLightItalic",
+ merged_style="ExtraLightItalic",
+ )
+ generate_font(
+ jp_style="Light",
+ eng_style="LightItalic",
+ merged_style="LightItalic",
+ )
+ generate_font(
+ jp_style="Text",
+ eng_style="TextItalic",
+ merged_style="TextItalic",
+ )
+ generate_font(
+ jp_style="Medium",
+ eng_style="MediumItalic",
+ merged_style="MediumItalic",
+ )
+ generate_font(
+ jp_style="SemiBold",
+ eng_style="SemiBoldItalic",
+ merged_style="SemiBoldItalic",
+ )
+
+
+def usage():
+ print(
+ f"Usage: {sys.argv[0]} "
+ "[--hidden-zenkaku-space] [--35] [--console] [--nerd-font]"
+ )
+
+
+def get_options():
+ """オプションを取得する"""
+
+ global options
+
+ # オプションなしの場合は何もしない
+ if len(sys.argv) == 1:
+ return
+
+ for arg in sys.argv[1:]:
+ # オプション判定
+ if arg == "--do-not-delete-build-dir":
+ options["do-not-delete-build-dir"] = True
+ elif arg == "--debug":
+ options["debug"] = True
+ elif arg == "--hidden-zenkaku-space":
+ options["hidden-zenkaku-space"] = True
+ elif arg == "--35":
+ options["35"] = True
+ elif arg == "--console":
+ options["console"] = True
+ elif arg == "--nerd-font":
+ options["nerd-font"] = True
+ else:
+ options["unknown-option"] = True
+ return
+
+
+def generate_font(jp_style, eng_style, merged_style):
+ print(f"=== Generate {merged_style} ===")
+
+ # 合成するフォントを開く
+ jp_font, eng_font = open_fonts(jp_style, eng_style)
+
+ # フォントのEMを揃える
+ adjust_em(eng_font)
+
+ # Hack フォントをマージする
+ merge_hack(eng_font, merged_style)
+
+ # 重複するグリフを削除する
+ delete_duplicate_glyphs(
+ jp_font, eng_font, is_console=True if options.get("console") else False
+ )
+
+ # いくつかのグリフ形状に調整を加える
+ adjust_some_glyph(jp_font, eng_font, merged_style)
+
+ # 日本語グリフの斜体を生成する
+ if "Italic" in merged_style:
+ transform_italic_glyphs(jp_font)
+
+ # 半角幅か全角幅になるように変換する
+ width_600_or_1000(jp_font)
+
+ if options.get("35"):
+ # eng_fontを3:5幅にする
+ adjust_width_35_eng(eng_font)
+ # jp_fontを3:5幅にする
+ adjust_width_35_jp(jp_font)
+ else:
+ # 1:2 幅にする
+ transform_half_width(jp_font, eng_font)
+
+ # GSUBテーブルを削除する (ひらがな等の全角文字が含まれる行でリガチャが解除される対策)
+ remove_lookups(jp_font)
+
+ # 全角スペースを可視化する
+ if not options.get("hidden-zenkaku-space"):
+ visualize_zenkaku_space(jp_font)
+
+ # Nerd Fontのグリフを追加する
+ if options.get("nerd-font"):
+ add_nerd_font_glyphs(jp_font, eng_font)
+
+ # オプション毎の修飾子を追加する
+ variant = f"{WIDTH_35_STR} " if options.get("35") else " "
+ variant += f"{CONSOLE_STR} " if options.get("console") else ""
+ variant += (
+ INVISIBLE_ZENKAKU_SPACE_STR if options.get("hidden-zenkaku-space") else ""
+ )
+ variant += NERD_FONTS_STR if options.get("nerd-font") else ""
+ variant = variant.strip()
+
+ # macOSでのpostテーブルの使用性エラー対策
+ # 重複するグリフ名を持つグリフをリネームする
+ delete_glyphs_with_duplicate_glyph_names(eng_font)
+ delete_glyphs_with_duplicate_glyph_names(jp_font)
+
+ # メタデータを編集する
+ cap_height = int(
+ Decimal(str(eng_font[0x0048].boundingBox()[3])).quantize(
+ Decimal("0"), ROUND_HALF_UP
+ )
+ )
+ x_height = int(
+ Decimal(str(eng_font[0x0078].boundingBox()[3])).quantize(
+ Decimal("0"), ROUND_HALF_UP
+ )
+ )
+ edit_meta_data(eng_font, merged_style, variant, cap_height, x_height)
+ edit_meta_data(jp_font, merged_style, variant, cap_height, x_height)
+
+ # ttfファイルに保存
+ # ヒンティングが残っていると不具合に繋がりがちなので外す。
+ # ヒンティングはあとで ttfautohint で行う。
+ # flags=("no-hints", "omit-instructions") を使うとヒンティングだけでなく GPOS や GSUB も削除されてしまうので使わない
+ font_name = f"{FONT_NAME}{variant}".replace(" ", "")
+ eng_font.generate(
+ f"{BUILD_FONTS_DIR}/{FONTFORGE_PREFIX}{font_name}-{merged_style}-eng.ttf",
+ )
+ jp_font.generate(
+ f"{BUILD_FONTS_DIR}/{FONTFORGE_PREFIX}{font_name}-{merged_style}-jp.ttf",
+ )
+
+ # ttfを閉じる
+ jp_font.close()
+ eng_font.close()
+
+
+def open_fonts(jp_style: str, eng_style: str):
+ """フォントを開く"""
+ jp_font = fontforge.open(
+ SOURCE_FONTS_DIR + "/" + JP_FONT.replace("{style}", jp_style)
+ )
+ eng_font = fontforge.open(
+ SOURCE_FONTS_DIR + "/" + ENG_FONT.replace("{style}", eng_style)
+ )
+
+ # fonttools merge エラー対処
+ jp_font = altuni_to_entity(jp_font)
+
+ # フォント参照を解除する
+ for glyph in jp_font.glyphs():
+ if glyph.isWorthOutputting():
+ jp_font.selection.select(("more", None), glyph)
+ jp_font.unlinkReferences()
+ for glyph in eng_font.glyphs():
+ if glyph.isWorthOutputting():
+ eng_font.selection.select(("more", None), glyph)
+ eng_font.unlinkReferences()
+ jp_font.selection.none()
+ eng_font.selection.none()
+
+ return jp_font, eng_font
+
+
+def altuni_to_entity(jp_font):
+ """Alternate Unicodeで透過的に参照して表示している箇所を実体のあるグリフに変換する"""
+ for glyph in jp_font.glyphs():
+ if glyph.altuni is not None:
+ # 以下形式のタプルで返ってくる
+ # (unicode-value, variation-selector, reserved-field)
+ # 第3フィールドは常に0なので無視
+ altunis = glyph.altuni
+
+ # variation-selectorがなく (-1)、透過的にグリフを参照しているものは実体のグリフに変換する
+ before_altuni = ""
+ for altuni in altunis:
+ # 直前のaltuniと同じ場合はスキップ
+ if altuni[1] == -1 and before_altuni != ",".join(map(str, altuni)):
+ glyph.altuni = None
+ copy_target_unicode = altuni[0]
+ try:
+ copy_target_glyph = jp_font.createChar(
+ copy_target_unicode,
+ f"uni{hex(copy_target_unicode).replace('0x', '').upper()}copy",
+ )
+ except Exception:
+ copy_target_glyph = jp_font[copy_target_unicode]
+ copy_target_glyph.clear()
+ copy_target_glyph.width = glyph.width
+ # copy_target_glyph.addReference(glyph.glyphname)
+ jp_font.selection.select(glyph.glyphname)
+ jp_font.copy()
+ jp_font.selection.select(copy_target_glyph.glyphname)
+ jp_font.paste()
+ before_altuni = ",".join(map(str, altuni))
+ # エンコーディングの整理のため、開き直す
+ font_path = f"{BUILD_FONTS_DIR}/{jp_font.fullname}_{uuid.uuid4()}.ttf"
+ jp_font.generate(font_path)
+ jp_font.close()
+ reopen_jp_font = fontforge.open(font_path)
+ # 一時ファイルを削除
+ os.remove(font_path)
+ return reopen_jp_font
+
+
+def adjust_some_glyph(jp_font, eng_font, style="Regular"):
+ """いくつかのグリフ形状に調整を加える"""
+ eng_glyph_width = eng_font[0x0020].width
+ full_width = jp_font[0x3042].width
+ if options.get("35"):
+ half_width = eng_glyph_width
+ else:
+ half_width = int(full_width / 2)
+
+ # クォーテーションの拡大
+ eng_font.selection.select(("unicode", None), 0x0060)
+ for glyph in eng_font.selection.byGlyphs:
+ glyph.transform(psMat.rotate(math.radians(-25)))
+ glyph.transform(psMat.scale(1.08, 1.2))
+ glyph.transform(psMat.rotate(math.radians(33)))
+ glyph.transform(psMat.translate(110, -135))
+ glyph.width = eng_glyph_width
+ eng_font.selection.select(("unicode", None), 0x0027)
+ eng_font.selection.select(("unicode", "more"), 0x0022)
+ for glyph in eng_font.selection.byGlyphs:
+ glyph.transform(psMat.scale(1.09, 1.06))
+ glyph.transform(psMat.translate((eng_glyph_width - glyph.width) / 2, 0))
+ glyph.width = eng_glyph_width
+ # ; : , . の拡大
+ eng_font.selection.select(("unicode", None), 0x003A)
+ eng_font.selection.select(("unicode", "more"), 0x003B)
+ eng_font.selection.select(("unicode", "more"), 0x002C)
+ eng_font.selection.select(("unicode", "more"), 0x002E)
+ for glyph in eng_font.selection.byGlyphs:
+ glyph.transform(psMat.scale(1.08, 1.08))
+ glyph.transform(psMat.translate((eng_glyph_width - glyph.width) / 2, 0))
+ glyph.width = eng_glyph_width
+ # Eclipse Pleiades 半角スペース記号 (U+1d1c) 対策
+ eng_font.selection.select(("unicode", None), 0x054D)
+ eng_font.copy()
+ eng_font.selection.select(("unicode", None), 0x1D1C)
+ eng_font.paste()
+ for glyph in eng_font.selection.byGlyphs:
+ glyph.transform(psMat.scale(0.85, 0.6))
+ glyph.transform(psMat.translate((eng_glyph_width - glyph.width) / 2, 0))
+ glyph.width = eng_glyph_width
+
+ # 全角括弧の開きを広くする
+ for glyph_name in [0xFF08, 0xFF3B, 0xFF5B]:
+ glyph = jp_font[glyph_name]
+ glyph.transform(psMat.translate(-180, 0))
+ glyph.width = full_width
+ for glyph_name in [0xFF09, 0xFF3D, 0xFF5D]:
+ glyph = jp_font[glyph_name]
+ glyph.transform(psMat.translate(180, 0))
+ glyph.width = full_width
+ # 全角ピリオド、カンマを拡大する
+ for glyph in jp_font.selection.select(("unicode", None), 0xFF0E).byGlyphs:
+ glyph.transform(psMat.scale(1.45, 1.45))
+ glyph.transform(psMat.translate((full_width - glyph.width) / 2, 0))
+ glyph.width = full_width
+ for glyph in jp_font.selection.select(("unicode", None), 0xFF0C).byGlyphs:
+ glyph.transform(psMat.scale(1.40, 1.40))
+ glyph.transform(psMat.translate((full_width - glyph.width) / 2, 0))
+ glyph.width = full_width
+ # LEFT SINGLE QUOTATION MARK (U+2018) ~ DOUBLE LOW-9 QUOTATION MARK (U+201E) の幅を全角幅にする
+ for glyph in jp_font.selection.select(
+ ("unicode", "ranges"), 0x2018, 0x2019
+ ).byGlyphs:
+ glyph.transform(psMat.scale(1.25, 1.25))
+ glyph.width = full_width
+ for glyph in jp_font.selection.select(
+ ("unicode", "ranges"), 0x201C, 0x201D
+ ).byGlyphs:
+ glyph.transform(psMat.scale(1.25, 1.25))
+ glyph.width = full_width
+
+ # Cent Sign, Pound Sign, Yen Sign は半角記号に IBM Plex Sans JP を使用
+ jp_font.selection.select(("unicode", None), 0x00A2)
+ jp_font.selection.select(("unicode", "more"), 0x00A3)
+ jp_font.selection.select(("unicode", "more"), 0x00A5)
+ for glyph in jp_font.selection.byGlyphs:
+ glyph.transform(psMat.scale(0.83, 1))
+ glyph.transform(psMat.translate((half_width - glyph.width) / 2, 0))
+ glyph.width = half_width
+
+ # r グリフの調整
+ if "Italic" not in style:
+ eng_font[0x0072].clear()
+ eng_font[0x0155].clear()
+ eng_font[0x0157].clear()
+ eng_font[0x0159].clear()
+ eng_font.mergeFonts(f"{SOURCE_FONTS_DIR}/" + ADJUST_R.replace("{style}", style))
+
+ jp_font.selection.none()
+ eng_font.selection.none()
+
+
+def adjust_em(font):
+ """フォントのEMを揃える"""
+ font.em = EM_ASCENT + EM_DESCENT
+
+
+def delete_duplicate_glyphs(jp_font, eng_font, is_console=False):
+ """jp_fontとeng_fontのグリフを比較し、重複するグリフを削除する"""
+
+ if not is_console:
+ delete_not_console_glyphs(eng_font)
+
+ eng_font.selection.none()
+ jp_font.selection.none()
+
+ # IBM Plex Sans JP グリフを使用
+ eng_font[0x00A2].clear() # Cent Sign
+ eng_font[0x00A3].clear() # Pound Sign
+ eng_font[0x00A5].clear() # Yen Sign
+ eng_font[0x3000].clear() # 全角スペース
+ # U+274C (CROSS MARK) を削除 (OSに含まれる絵文字フォントにフォールバックさせるため)
+ eng_font[0x274C].clear()
+
+ for glyph in jp_font.glyphs("encoding"):
+ try:
+ if glyph.isWorthOutputting() and glyph.unicode > 0:
+ eng_font.selection.select(("more", "unicode"), glyph.unicode)
+ except ValueError:
+ # Encoding is out of range のときは継続する
+ continue
+ for glyph in eng_font.selection.byGlyphs:
+ # if glyph.isWorthOutputting():
+ jp_font.selection.select(("more", "unicode"), glyph.unicode)
+ for glyph in jp_font.selection.byGlyphs:
+ glyph.clear()
+
+ jp_font.selection.none()
+ eng_font.selection.none()
+
+
+def delete_not_console_glyphs(eng_font):
+ eng_font.selection.none()
+
+ # 記号
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x00A1, 0x00A5)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x00A7, 0x00FF)
+ eng_font.selection.select(("more", "unicode"), 0x0131)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x0141, 0x0142)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x0152, 0x0153)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x0160, 0x0161)
+ eng_font.selection.select(("more", "unicode"), 0x0178)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x017D, 0x017E)
+ eng_font.selection.select(("more", "unicode"), 0x0192)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x02BB, 0x02BC)
+ eng_font.selection.select(("more", "unicode"), 0x02C6)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x02DA, 0x02DC)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x0300, 0x0308)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x0310, 0x030C)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x0327, 0x0328)
+ eng_font.selection.select(("more", "unicode"), 0x0401)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x0410, 0x044F)
+ eng_font.selection.select(("more", "unicode"), 0x0451)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x2010, 0x2026)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x2030, 0x2044)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x2113, 0x2122)
+ eng_font.selection.select(("more", "unicode"), 0x2202)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x2211, 0x222B)
+ # 矢印
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x2190, 0x2193)
+ eng_font.selection.select(("more", "unicode", "ranges"), 0x21C4, 0x21C6)
+ # 数学記号
+ eng_font.selection.select(("more", "unicode"), 0x2260)
+ # 一部 IBMPlexMono ベースにする
+ # 各エディタの可視化文字対策
+ eng_font.selection.select(("less", "unicode"), 0x2022)
+ eng_font.selection.select(("less", "unicode"), 0x00B7)
+ eng_font.selection.select(("less", "unicode"), 0x2024)
+ eng_font.selection.select(("less", "unicode"), 0x2219)
+ eng_font.selection.select(("less", "unicode"), 0x25D8)
+ eng_font.selection.select(("less", "unicode"), 0x25E6)
+ # 結合文音記号は IBM Plex Mono を適用
+ eng_font.selection.select(("less", "unicode", "ranges"), 0x0300, 0x0328)
+ # IBM Plex Sans JP 等幅化対策 (IBM Plex Mono を適用して半角化)
+ eng_font.selection.select(("less", "unicode"), 0xAB)
+ eng_font.selection.select(("less", "unicode"), 0xBB)
+ # flaction slash
+ eng_font.selection.select(("less", "unicode"), 0x2044)
+ # broken bar
+ eng_font.selection.select(("less", "unicode"), 0x00A6)
+
+ for glyph in eng_font.selection.byGlyphs:
+ glyph.clear()
+
+
+def remove_lookups(font):
+ """GSUB, GPOSテーブルを削除する"""
+ for lookup in list(font.gsub_lookups) + list(font.gpos_lookups):
+ font.removeLookup(lookup)
+
+
+def transform_italic_glyphs(font):
+ # 傾きを設定する
+ font.italicangle = -ITALIC_ANGLE
+ orig_width = font[0x3000].width
+ # 全グリフを斜体に変換
+ for glyph in font.glyphs():
+ glyph.transform(psMat.skew(ITALIC_ANGLE * math.pi / 180))
+ glyph.transform(psMat.translate(-94, 0))
+ glyph.width = orig_width
+
+
+def width_600_or_1000(jp_font):
+ """半角幅か全角幅になるように変換する"""
+ for glyph in jp_font.glyphs():
+ if 0 < glyph.width < 600:
+ # グリフ位置を調整してから幅を設定
+ glyph.transform(psMat.translate((600 - glyph.width) / 2, 0))
+ glyph.width = 600
+ elif 600 < glyph.width < 1000:
+ # グリフ位置を調整してから幅を設定
+ glyph.transform(psMat.translate((1000 - glyph.width) / 2, 0))
+ glyph.width = 1000
+ # 600の場合はそのまま
+
+
+def adjust_width_35_eng(eng_font):
+ """英語フォントを半角3:全角5幅になるように変換する"""
+ original_half_width = eng_font[0x0030].width
+ after_width = int(FULL_WIDTH_35 * 3 / 5)
+ x_scale = after_width / original_half_width
+ for glyph in eng_font.glyphs():
+ if 0 < glyph.width < after_width:
+ # after_width より幅が狭い場合は位置合わせしてから幅を設定
+ glyph.transform(psMat.translate((after_width - glyph.width) / 2, 0))
+ glyph.width = after_width
+ elif after_width < glyph.width <= original_half_width:
+ # after_width より幅が広い、かつ元の半角幅より狭い場合は縮小してから幅を設定
+ glyph.transform(psMat.scale(x_scale, 1))
+ glyph.width = after_width
+ elif original_half_width < glyph.width:
+ # after_width より幅が広い (おそらく全てリガチャ) の場合は倍数にする
+ multiply_number = round(glyph.width / original_half_width)
+ glyph.transform(psMat.scale(x_scale, 1))
+ glyph.width = after_width * multiply_number
+
+
+def adjust_width_35_jp(jp_font):
+ """日本語フォントを半角3:全角5幅になるように変換する"""
+ after_width = int(FULL_WIDTH_35 * 3 / 5)
+ jp_half_width = jp_font[0x3000].width / 2
+ jp_full_width = jp_font[0x3000].width
+ for glyph in jp_font.glyphs():
+ if glyph.width == jp_half_width:
+ glyph.transform(psMat.translate((after_width - glyph.width) / 2, 0))
+ glyph.width = after_width
+ elif glyph.width == jp_full_width:
+ glyph.transform(psMat.translate((FULL_WIDTH_35 - glyph.width) / 2, 0))
+ glyph.width = FULL_WIDTH_35
+
+
+def transform_half_width(jp_font, eng_font):
+ """1:2幅になるように変換する"""
+ before_width_eng = eng_font[0x0030].width
+ after_width_eng = HALF_WIDTH_12
+ # 単純 縮小後幅 / 元の幅 だと狭くなりすりぎるので、倍率を考慮する
+ x_scale = 546 / before_width_eng
+ for glyph in eng_font.glyphs():
+ if glyph.width > 0:
+ # リガチャ考慮
+ after_width_eng_multiply = after_width_eng * round(
+ glyph.width / before_width_eng
+ )
+ # 縮小
+ glyph.transform(psMat.scale(x_scale, 0.97))
+ # 幅を設定
+ glyph.transform(
+ psMat.translate((after_width_eng_multiply - glyph.width) / 2, 0)
+ )
+ glyph.width = after_width_eng_multiply
+
+ for glyph in jp_font.glyphs():
+ if glyph.width == 600:
+ # 英数字グリフと同じ幅にする
+ glyph.transform(psMat.translate((after_width_eng - glyph.width) / 2, 0))
+ glyph.width = after_width_eng
+ elif glyph.width == 1000:
+ # 全角は after_width_eng の倍の幅にする
+ glyph.transform(psMat.translate((after_width_eng * 2 - glyph.width) / 2, 0))
+ glyph.width = after_width_eng * 2
+
+
+def visualize_zenkaku_space(jp_font):
+ """全角スペースを可視化する"""
+ # 全角スペースを差し替え
+ jp_font.selection.select("U+3000")
+ for glyph in jp_font.selection.byGlyphs:
+ glyph.clear()
+ jp_font.mergeFonts(fontforge.open(f"{SOURCE_FONTS_DIR}/{IDEOGRAPHIC_SPACE}"))
+
+
+def merge_hack(eng_font, style):
+ """Hack フォントをマージする"""
+ if "Bold" in style:
+ hack_font = fontforge.open(
+ f"{SOURCE_FONTS_DIR}/" + HACK_FONT.replace("{style}", "Bold")
+ )
+ else:
+ hack_font = fontforge.open(
+ f"{SOURCE_FONTS_DIR}/" + HACK_FONT.replace("{style}", "Regular")
+ )
+ hack_font.em = EM_ASCENT + EM_DESCENT
+ # 既に英語フォント側に存在する場合はhackグリフは削除する
+ for glyph in eng_font.glyphs():
+ if glyph.unicode != -1:
+ try:
+ for g in hack_font.selection.select(
+ ("unicode", None), glyph.unicode
+ ).byGlyphs:
+ g.clear()
+ except Exception:
+ pass
+ # EM 1000 にしたときの幅に合わせて調整
+ half_width = int(FULL_WIDTH_35 * 3 / 5)
+ for glyph in hack_font.glyphs():
+ if glyph.width > 0:
+ glyph.transform(psMat.translate((half_width - glyph.width) / 2, 0))
+ glyph.width = half_width
+ # Hack フォントをオブジェクトとして扱いたくないので、一旦ファイル保存して直接マージする
+ font_path = f"{BUILD_FONTS_DIR}/tmp_hack_{uuid.uuid4()}.ttf"
+ hack_font.generate(font_path)
+ hack_font.close()
+
+ eng_font.mergeFonts(font_path)
+ os.remove(font_path)
+
+
+def add_nerd_font_glyphs(jp_font, eng_font):
+ """Nerd Fontのグリフを追加する"""
+ global nerd_font
+ # Nerd Fontのグリフを追加する
+ if nerd_font is None:
+ nerd_font = fontforge.open(
+ f"{SOURCE_FONTS_DIR}/nerd-fonts/SymbolsNerdFont-Regular.ttf"
+ )
+ nerd_font.em = EM_ASCENT + EM_DESCENT
+ glyph_names = set()
+ for nerd_glyph in nerd_font.glyphs():
+ # Nerd Fontsのグリフ名をユニークにするため接尾辞を付ける
+ nerd_glyph.glyphname = f"{nerd_glyph.glyphname}-nf"
+ # postテーブルでのグリフ名重複対策
+ # fonttools merge で合成した後、MacOSで `'post'テーブルの使用性` エラーが発生することへの対処
+ if nerd_glyph.glyphname in glyph_names:
+ nerd_glyph.glyphname = f"{nerd_glyph.glyphname}-{nerd_glyph.encoding}"
+ glyph_names.add(nerd_glyph.glyphname)
+ # 幅を調整する
+ half_width = eng_font[0x0030].width
+ # Powerline Symbols の調整
+ if 0xE0B0 <= nerd_glyph.unicode <= 0xE0D4:
+ # なぜかズレている右付きグリフの個別調整 (EM 1000 に変更した後を想定して調整)
+ original_width = nerd_glyph.width
+ if nerd_glyph.unicode == 0xE0B2:
+ nerd_glyph.transform(psMat.translate(-353, 0))
+ elif nerd_glyph.unicode == 0xE0B6:
+ nerd_glyph.transform(psMat.translate(-414, 0))
+ elif nerd_glyph.unicode == 0xE0C5:
+ nerd_glyph.transform(psMat.translate(-137, 0))
+ elif nerd_glyph.unicode == 0xE0C7:
+ nerd_glyph.transform(psMat.translate(-214, 0))
+ elif nerd_glyph.unicode == 0xE0D4:
+ nerd_glyph.transform(psMat.translate(-314, 0))
+ nerd_glyph.width = original_width
+ # 位置と幅合わせ
+ if nerd_glyph.width < half_width:
+ nerd_glyph.transform(
+ psMat.translate((half_width - nerd_glyph.width) / 2, 0)
+ )
+ elif nerd_glyph.width > half_width:
+ nerd_glyph.transform(psMat.scale(half_width / nerd_glyph.width, 1))
+ # グリフの高さ・位置を調整する
+ nerd_glyph.transform(psMat.scale(1, 1.14))
+ nerd_glyph.transform(psMat.translate(0, 21))
+ elif nerd_glyph.width < (EM_ASCENT + EM_DESCENT) * 0.6:
+ # 幅が狭いグリフは中央寄せとみなして調整する
+ nerd_glyph.transform(
+ psMat.translate((half_width - nerd_glyph.width) / 2, 0)
+ )
+ # 幅を設定
+ nerd_glyph.width = half_width
+ # 日本語フォントにマージするため、既に存在する場合は削除する
+ for nerd_glyph in nerd_font.glyphs():
+ if nerd_glyph.unicode != -1:
+ # 既に存在する場合は削除する
+ try:
+ for glyph in jp_font.selection.select(
+ ("unicode", None), nerd_glyph.unicode
+ ).byGlyphs:
+ glyph.clear()
+ except Exception:
+ pass
+ try:
+ for glyph in eng_font.selection.select(
+ ("unicode", None), nerd_glyph.unicode
+ ).byGlyphs:
+ glyph.clear()
+ except Exception:
+ pass
+
+ jp_font.mergeFonts(nerd_font)
+
+ jp_font.selection.none()
+ eng_font.selection.none()
+
+
+def delete_glyphs_with_duplicate_glyph_names(font):
+ """重複するグリフ名を持つグリフをリネームする"""
+ glyph_name_set = set()
+ for glyph in font.glyphs():
+ if glyph.glyphname in glyph_name_set:
+ glyph.glyphname = f"{glyph.glyphname}_{glyph.encoding}"
+ else:
+ glyph_name_set.add(glyph.glyphname)
+
+
+def edit_meta_data(font, weight: str, variant: str, cap_height: int, x_height: int):
+ """フォント内のメタデータを編集する"""
+ font.ascent = EM_ASCENT
+ font.descent = EM_DESCENT
+
+ if WIDTH_35_STR in variant and not options.get("nerd-font"):
+ os2_ascent = OS2_ASCENT + 60
+ os2_descent = OS2_DESCENT + 60
+ else:
+ os2_ascent = OS2_ASCENT
+ os2_descent = OS2_DESCENT
+
+ font.os2_typoascent = os2_ascent
+ font.os2_typodescent = -os2_descent
+ font.os2_winascent = os2_ascent
+ font.os2_windescent = os2_descent
+ font.os2_typolinegap = 0
+
+ font.hhea_ascent = os2_ascent
+ font.hhea_descent = -os2_descent
+ font.hhea_linegap = 0
+
+ font.os2_xheight = x_height
+ font.os2_capheight = cap_height
+
+ if "Regular" == weight or "Italic" == weight:
+ font.os2_weight = 400
+ elif "Thin" in weight:
+ font.os2_weight = 100
+ elif "ExtraLight" in weight:
+ font.os2_weight = 200
+ elif "Light" in weight:
+ font.os2_weight = 300
+ elif "Text" in weight:
+ font.os2_weight = 450
+ elif "Medium" in weight:
+ font.os2_weight = 500
+ elif "SemiBold" in weight:
+ font.os2_weight = 600
+ elif "Bold" in weight:
+ font.os2_weight = 700
+
+ font.os2_vendor = VENDER_NAME
+
+ font.sfnt_names = (
+ (
+ "English (US)",
+ "License",
+ """This Font Software is licensed under the SIL Open Font License,
+Version 1.1. This license is available with a FAQ
+at: http://scripts.sil.org/OFL""",
+ ),
+ ("English (US)", "License URL", "http://scripts.sil.org/OFL"),
+ ("English (US)", "Version", VERSION),
+ ("English (US)", "Copyright", COPYRIGHT),
+ )
+
+ # フォント名を設定する
+ if (
+ "Regular" == weight
+ or "Italic" == weight
+ or "Bold" == weight
+ or "BoldItalic" == weight
+ ):
+ font_family = f"{FONT_NAME} {variant}".replace(" 35", "35")
+ font_weight = weight
+ if "Italic" in weight:
+ font_weight = font_weight.replace("Italic", " Italic")
+ font.familyname = font_family
+ # フォントサブファミリー名
+ font.appendSFNTName(0x409, 2, font_weight)
+ font.fontname = f"{font_family}-{font_weight}".replace(" ", "")
+ font.fullname = f"{font_family} {font_weight}"
+ font.weight = font_weight.split(" ")[0]
+ else:
+ font_family = f"{FONT_NAME} {variant}".replace(" 35", "35")
+ font_weight = weight
+ if "Italic" in weight:
+ font_weight = font_weight.replace("Italic", " Italic")
+ font.familyname = f"{font_family} " + font_weight.split(" ")[0]
+ # フォントサブファミリー名
+ if "Italic" in weight:
+ font.appendSFNTName(0x409, 2, "Italic")
+ else:
+ font.appendSFNTName(0x409, 2, "Regular")
+ font.fontname = f"{font_family}-{font_weight}".replace(" ", "")
+ font.fullname = f"{font_family} {font_weight}"
+ font.weight = font_weight.split(" ")[0]
+ # 優先フォントファミリー名
+ font.appendSFNTName(0x409, 16, font_family)
+ # 優先フォントスタイル
+ font.appendSFNTName(0x409, 17, font_weight)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/fonttools_script.py b/fonttools_script.py
new file mode 100644
index 0000000..299280c
--- /dev/null
+++ b/fonttools_script.py
@@ -0,0 +1,281 @@
+#!/bin/env python3
+
+import configparser
+import glob
+import os
+import sys
+import xml.etree.ElementTree as ET
+from pathlib import Path
+
+from fontTools import merge, ttLib, ttx
+from ttfautohint import options, ttfautohint
+
+# iniファイルを読み込む
+settings = configparser.ConfigParser()
+settings.read("build.ini", encoding="utf-8")
+
+FONT_NAME = settings.get("DEFAULT", "FONT_NAME")
+FONTFORGE_PREFIX = settings.get("DEFAULT", "FONTFORGE_PREFIX")
+FONTTOOLS_PREFIX = settings.get("DEFAULT", "FONTTOOLS_PREFIX")
+BUILD_FONTS_DIR = settings.get("DEFAULT", "BUILD_FONTS_DIR")
+HALF_WIDTH_12 = int(settings.get("DEFAULT", "HALF_WIDTH_12"))
+FULL_WIDTH_35 = int(settings.get("DEFAULT", "FULL_WIDTH_35"))
+WIDTH_35_STR = settings.get("DEFAULT", "WIDTH_35_STR")
+CONSOLE_STR = settings.get("DEFAULT", "CONSOLE_STR")
+
+
+def main():
+ # 第一引数を取得
+ # 特定のバリエーションのみを処理するための指定
+ specific_variant = sys.argv[1] if len(sys.argv) > 1 else None
+
+ edit_fonts(specific_variant)
+
+
+def edit_fonts(specific_variant: str):
+ """フォントを編集する"""
+
+ if specific_variant is None:
+ specific_variant = ""
+
+ # ファイルをパターンで指定
+ file_pattern = f"{FONTFORGE_PREFIX}{FONT_NAME}{specific_variant}*-eng.ttf"
+ filenames = glob.glob(f"{BUILD_FONTS_DIR}/{file_pattern}")
+ # ファイルが見つからない場合はエラー
+ if len(filenames) == 0:
+ print(f"Error: {file_pattern} not found")
+ return
+ paths = [Path(f) for f in filenames]
+ for path in paths:
+ print(f"edit {str(path)}")
+ style = path.stem.split("-")[1]
+ variant = path.stem.split("-")[0].replace(f"{FONTFORGE_PREFIX}{FONT_NAME}", "")
+ add_hinting(str(path), str(path).replace(".ttf", "-hinted.ttf"), variant, style)
+ merge_fonts(style, variant)
+ fix_font_tables(style, variant)
+
+ # 一時ファイルを削除
+ # スタイル部分以降はワイルドカードで指定
+ for filename in glob.glob(
+ f"{BUILD_FONTS_DIR}/{FONTTOOLS_PREFIX}{FONT_NAME}{specific_variant}*"
+ ):
+ os.remove(filename)
+ for filename in glob.glob(
+ f"{BUILD_FONTS_DIR}/{FONTFORGE_PREFIX}{FONT_NAME}{specific_variant}*"
+ ):
+ os.remove(filename)
+
+
+def add_hinting(input_font_path, output_font_path, variant, style):
+ """フォントにヒンティングを付ける"""
+ if "Italic" not in style:
+ width_variant = "35" if WIDTH_35_STR in variant else "normal"
+ ctrl_file = [
+ "-m",
+ f"hinting_post_process/{width_variant}-{style}-ctrl.txt",
+ ]
+ else:
+ ctrl_file = []
+
+ args = ctrl_file + [
+ "-l",
+ "6",
+ "-r",
+ "45",
+ "-D",
+ "latn",
+ "-f",
+ "none",
+ "-S",
+ "-W",
+ "-X",
+ "13-",
+ "-I",
+ input_font_path,
+ output_font_path,
+ ]
+ options_ = options.parse_args(args)
+ print("exec hinting", options_)
+ ttfautohint(**options_)
+
+
+def merge_fonts(style, variant):
+ """フォントを結合する"""
+ eng_font_path = f"{BUILD_FONTS_DIR}/{FONTFORGE_PREFIX}{FONT_NAME}{variant}-{style}-eng-hinted.ttf"
+ jp_font_path = (
+ f"{BUILD_FONTS_DIR}/{FONTFORGE_PREFIX}{FONT_NAME}{variant}-{style}-jp.ttf"
+ )
+ # vhea, vmtxテーブルを削除
+ jp_font_object = ttLib.TTFont(jp_font_path)
+ if "vhea" in jp_font_object:
+ del jp_font_object["vhea"]
+ if "vmtx" in jp_font_object:
+ del jp_font_object["vmtx"]
+ jp_font_object.save(jp_font_path)
+ # フォントを結合
+ merger = merge.Merger()
+ merged_font = merger.merge([eng_font_path, jp_font_path])
+ merged_font.save(
+ f"{BUILD_FONTS_DIR}/{FONTTOOLS_PREFIX}{FONT_NAME}{variant}-{style}_merged.ttf"
+ )
+
+
+def fix_font_tables(style, variant):
+ """フォントテーブルを編集する"""
+
+ input_font_name = f"{FONTTOOLS_PREFIX}{FONT_NAME}{variant}-{style}_merged.ttf"
+ output_name_base = f"{FONTTOOLS_PREFIX}{FONT_NAME}{variant}-{style}"
+ completed_name_base = f"{FONT_NAME.replace(' ', '')}{variant}-{style}"
+
+ # OS/2, post テーブルのみのttxファイルを出力
+ xml = dump_ttx(input_font_name, output_name_base)
+ # OS/2 テーブルを編集
+ fix_os2_table(xml, style, flag_35=WIDTH_35_STR in variant)
+ # post テーブルを編集
+ fix_post_table(xml, flag_35=WIDTH_35_STR in variant)
+ # name テーブルを編集
+ fix_name_table(xml)
+
+ # ttxファイルを上書き保存
+ xml.write(
+ f"{BUILD_FONTS_DIR}/{output_name_base}.ttx",
+ encoding="utf-8",
+ xml_declaration=True,
+ )
+
+ # ttxファイルをttfファイルに適用
+ ttx.main(
+ [
+ "-o",
+ f"{BUILD_FONTS_DIR}/{output_name_base}_os2_post.ttf",
+ "-m",
+ f"{BUILD_FONTS_DIR}/{input_font_name}",
+ f"{BUILD_FONTS_DIR}/{output_name_base}.ttx",
+ ]
+ )
+
+ # ファイル名を変更
+ os.rename(
+ f"{BUILD_FONTS_DIR}/{output_name_base}_os2_post.ttf",
+ f"{BUILD_FONTS_DIR}/{completed_name_base}.ttf",
+ )
+
+
+def dump_ttx(input_name_base, output_name_base) -> ET:
+ """OS/2, post テーブルのみのttxファイルを出力"""
+ ttx.main(
+ [
+ "-t",
+ "OS/2",
+ "-t",
+ "post",
+ "-t",
+ "name",
+ "-f",
+ "-o",
+ f"{BUILD_FONTS_DIR}/{output_name_base}.ttx",
+ f"{BUILD_FONTS_DIR}/{input_name_base}",
+ ]
+ )
+
+ return ET.parse(f"{BUILD_FONTS_DIR}/{output_name_base}.ttx")
+
+
+def fix_os2_table(xml: ET, style: str, flag_35: bool = False):
+ """OS/2 テーブルを編集する"""
+ # xAvgCharWidthを編集
+ # タグ形式:
+ if flag_35:
+ x_avg_char_width = FULL_WIDTH_35
+ else:
+ x_avg_char_width = HALF_WIDTH_12
+ xml.find("OS_2/xAvgCharWidth").set("value", str(x_avg_char_width))
+
+ # fsSelectionを編集
+ # タグ形式:
+ # スタイルに応じたビットを立てる
+ fs_selection = None
+ if style == "Regular":
+ fs_selection = "00000001 01000000"
+ elif style == "Italic":
+ fs_selection = "00000001 00000001"
+ elif style == "Bold":
+ fs_selection = "00000001 00100000"
+ elif style == "BoldItalic":
+ fs_selection = "00000001 00100001"
+
+ if fs_selection is not None:
+ xml.find("OS_2/fsSelection").set("value", fs_selection)
+
+ # panoseを編集
+ # タグ形式:
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ if style == "Regular" or style == "Italic":
+ bWeight = 5
+ else:
+ bWeight = 8
+ if flag_35:
+ panose = {
+ "bFamilyType": 2,
+ "bSerifStyle": 11,
+ "bWeight": bWeight,
+ "bProportion": 9,
+ "bContrast": 2,
+ "bStrokeVariation": 2,
+ "bArmStyle": 3,
+ "bLetterForm": 2,
+ "bMidline": 2,
+ "bXHeight": 7,
+ }
+ else:
+ panose = {
+ "bFamilyType": 2,
+ "bSerifStyle": 11,
+ "bWeight": bWeight,
+ "bProportion": 3,
+ "bContrast": 2,
+ "bStrokeVariation": 2,
+ "bArmStyle": 3,
+ "bLetterForm": 2,
+ "bMidline": 2,
+ "bXHeight": 7,
+ }
+
+ for key, value in panose.items():
+ xml.find(f"OS_2/panose/{key}").set("value", str(value))
+
+
+def fix_post_table(xml: ET, flag_35):
+ """post テーブルを編集する"""
+ # isFixedPitchを編集
+ # タグ形式:
+ is_fixed_pitch = 0 if flag_35 else 1
+ xml.find("post/isFixedPitch").set("value", str(is_fixed_pitch))
+
+
+def fix_name_table(xml: ET):
+ """name テーブルを編集する
+ 何故か "alternate lowercase eszett" という文字列の
+ 著作権フィールドが含まれてしまうので、削除する。
+ """
+ # タグ形式: COPYLIGHT
+ parent = xml.find("name")
+ for element in parent.findall("namerecord[@nameID='0']"):
+ if element.text.strip() == "alternate lowercase eszett":
+ print(element.text.strip())
+ parent.remove(element)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/make.ps1 b/make.ps1
new file mode 100644
index 0000000..8ed04a1
--- /dev/null
+++ b/make.ps1
@@ -0,0 +1,66 @@
+# ini から VERSION を取得
+$ini = Get-Content .\build.ini
+$version = ($ini | Select-String -Pattern "VERSION").ToString().Split("=")[1].Trim()
+
+# スクリプトファイルがある場所に移動する
+Set-Location -Path $PSScriptRoot
+# 各ファイルを置くフォルダを作成
+New-Item -ItemType Directory -Force -Path ".\release_files\"
+# ビルドフォルダを削除
+Remove-Item -Path .\build -Recurse -Force
+
+# 並列処理内で、処理が重いNerd Fontsのビルドを優先して処理する
+$option_and_output_folder = @(
+ @("--console --nerd-font", "ConsoleNF-"), # ビルド コンソール用 通常版 + Nerd Fonts
+ @("--console --35 --nerd-font", "35ConsoleNF-"), # ビルド コンソール用 3:5幅版 + Nerd Fonts
+ @("", "-"), # ビルド 通常版
+ @("--35", "35-"), # ビルド 3:5幅版
+ @("--console", "Console-"), # ビルド コンソール用 通常版
+ @("--console --35", "35Console-"), # ビルド コンソール用 1:2幅版
+ @("--hidden-zenkaku-space ", "HS-"), # ビルド 通常版 全角スペース不可視
+ @("--hidden-zenkaku-space --35", "35HS-"), # ビルド 3:5幅版 全角スペース不可視
+ @("--hidden-zenkaku-space --console", "ConsoleHS-"), # ビルド コンソール用 通常版 全角スペース不可視
+ @("--hidden-zenkaku-space --console --35", "35ConsoleHS-") # ビルド コンソール用 1:2幅版 全角スペース不可視
+)
+
+$option_and_output_folder | Foreach-Object -ThrottleLimit 4 -Parallel {
+ Write-Host "fontforge script start. option: `"$($_[0])`""
+ Invoke-Expression "& `"C:\Program Files (x86)\FontForgeBuilds\bin\ffpython.exe`" .\fontforge_script.py --do-not-delete-build-dir $($_[0])" `
+ && Write-Host "fonttools script start. option: `"$($_[1])`"" `
+ && python fonttools_script.py $_[1]
+}
+
+$move_file_src_dest = @(
+ @("PlemolJP*NF*-*.ttf", "PlemolJP_NF_$version", "NF"),
+ @("PlemolJP*HS*-*.ttf", "PlemolJP_HS_$version", "HS"),
+ @("PlemolJP*-*.ttf", "PlemolJP_$version", "")
+)
+
+$timestamp = Get-Date -Format "yyyyMMddHHmmss"
+$move_dir = ".\release_files\build_$timestamp"
+
+$move_file_src_dest | Foreach-Object {
+ $folder_path = "$move_dir\$($_[1])"
+ New-Item -ItemType Directory -Force -Path $folder_path
+ Move-Item -Path ".\build\$($_[0])" -Destination $folder_path -Force
+
+ $variant = ""
+ if ($_[2] -ne "") {
+ $variant = "_$($_[2])"
+ }
+ @(
+ @("*35Console*.ttf", "PlemolJP35Console$($variant)"),
+ @("*Console*.ttf", "PlemolJPConsole$($variant)"),
+ @("*35*.ttf", "PlemolJP35$($variant)"),
+ @("*.ttf", "PlemolJP$($variant)")
+ ) | Foreach-Object {
+ $individual_folder_path = "$folder_path\$($_[1])"
+ # ファイル件数が0件の場合はフォルダを作成しない
+ if ((Get-ChildItem -Path $folder_path\$($_[0])).Count -eq 0) {
+ return
+ }
+ New-Item -ItemType Directory -Force -Path $individual_folder_path
+ Move-Item -Path $folder_path\$($_[0]) -Destination $individual_folder_path -Force
+ }
+}
+
diff --git a/source/hack/Hack-Bold.ttf b/source/hack/Hack-Bold.ttf
new file mode 100644
index 0000000..7ff4975
Binary files /dev/null and b/source/hack/Hack-Bold.ttf differ
diff --git a/source/hack/Hack-Regular.ttf b/source/hack/Hack-Regular.ttf
new file mode 100644
index 0000000..92a90cb
Binary files /dev/null and b/source/hack/Hack-Regular.ttf differ
diff --git a/source/hack/LICENSE b/source/hack/LICENSE
new file mode 100644
index 0000000..08927e5
--- /dev/null
+++ b/source/hack/LICENSE
@@ -0,0 +1,45 @@
+The work in the Hack project is Copyright 2018 Source Foundry Authors and licensed under the MIT License
+
+The work in the DejaVu project was committed to the public domain.
+
+Bitstream Vera Sans Mono Copyright 2003 Bitstream Inc. and licensed under the Bitstream Vera License with Reserved Font Names "Bitstream" and "Vera"
+
+### MIT License
+
+Copyright (c) 2018 Source Foundry Authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+### BITSTREAM VERA LICENSE
+
+Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions:
+
+The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.
+
+The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera".
+
+This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names.
+
+The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
+
+Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.
diff --git a/source/nerd-fonts/LICENSE b/source/nerd-fonts/LICENSE
new file mode 100644
index 0000000..06eb073
--- /dev/null
+++ b/source/nerd-fonts/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Ryan L McIntyre
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/source/nerd-fonts/SymbolsNerdFont-Regular.ttf b/source/nerd-fonts/SymbolsNerdFont-Regular.ttf
new file mode 100644
index 0000000..e7ce229
Binary files /dev/null and b/source/nerd-fonts/SymbolsNerdFont-Regular.ttf differ