あでぃ工房 のカバー画像

Site icon image あでぃ工房

🖍️ 複数のMacでフォントを自動同期するCLIツールを作った話

9分で読めます

動機

MacBook AirとMac miniの2台体制で作業していると、地味に困ることがあります。それは、フォントの同期

Adobe Fontsのように自動で同期してくれるものは問題ないのですが、自分で購入したりダウンロードしたフォントは、それぞれのマシンで手動インストールが必要なんですよね。フォント管理ソフトもありますが、多くは有料。クラウドアプリでフォントファイル自体を同期しても、結局インストールは手動で面倒です。

しかも僕にはフォント収集癖があって、フォントバンドルを衝動買いしちゃったりするタイプときました。なので、気づいたら「あれ、このフォント家のMacには入れたけど、外のMacBook Airには入れてなかった...」みたいなことがしょっちゅうおきてしまって作業が嫌になるみたいなことを繰り返していました。

ttfやotfファイルをひとつひとつダブルクリックしてインストールするのも、どれをインストール済みか覚えてないからどこまで何をやったかの記憶力もない。終わってます。

有料にするほど何かの制作を仕事として請け負っているわけではないし、ということで自分がミニマムに欲しい機能だけにフォーカスしてツールを作ってみることにしました。

実際に開発していく

今回はCursorを利用して、基本的にはすべてのコードを開発してもらいました。

要件

要件はシンプルにします。

  1. クラウド共有(Google Drive, Dropboxなど)のフォルダにフォントを入れる
  2. コマンドで各Macに同期される
  3. 新規・更新・削除を自動で判別

これだけできれば僕の悩みは解決しそうです。ゆくゆくはもっと細かいブラッシュアップもしても良さそうですが、一旦今の悩みを解決することを優先しましょう。

実装

1. 基本設計

macOSのフォント管理の仕組みは以下の通りです。

  • ユーザーフォントは ~/Library/Fonts/ に保存
  • .otfや.ttfのファイルをこのディレクトリにコピーすれば認識される
  • ファイルのハッシュ値で変更検出できる

ということで、以下のような構成にしました:

# 設定ファイル (~/.fontsync/config.json)
{
    "sync_folder": "~/Dropbox/shared-fonts/",
    "installed_fonts": {
        "MyFont.otf": {
            "hash": "sha256_hash_value",
            "installed_at": "2024-01-01T00:00:00"
        }
    }
}
2. CLIの実装

CLIフレームワークにはTyperを採用。直感的でいい感じのCLIが簡単に作れるみたいです。

import typer
from pathlib import Path
from rich.console import Console
from rich.progress import track

app = typer.Typer()
console = Console()

@app.command()
def sync():
    """フォントを同期する"""
    config = load_config()
    sync_folder = Path(config["sync_folder"]).expanduser()

    # フォントファイルをスキャン
    font_files = list(sync_folder.glob("*.otf")) + list(sync_folder.glob("*.ttf"))

    # 差分を検出
    to_install = []
    for font_file in track(font_files, description="差分を確認中..."):
        if needs_update(font_file, config):
            to_install.append(font_file)

    # インストール
    for font_file in track(to_install, description="インストール中..."):
        install_font(font_file)

    console.print(f"✓ {len(to_install)}個のフォントを同期しました", style="green")
3. 差分同期の仕組み
import hashlib

def calculate_hash(file_path: Path) -> str:
    """ファイルのSHA256ハッシュを計算"""
    hash_sha256 = hashlib.sha256()
    with open(file_path, "rb") as f:
        # メモリ効率のため、チャンクごとに読み込み
        for chunk in iter(lambda: f.read(4096), b""):
            hash_sha256.update(chunk)
    return hash_sha256.hexdigest()

def needs_update(font_file: Path, config: dict) -> bool:
    """フォントの更新が必要か判定"""
    font_name = font_file.name
    current_hash = calculate_hash(font_file)

    # 新規フォント
    if font_name not in config["installed_fonts"]:
        return True

    # ハッシュが異なる = 更新されている
    return config["installed_fonts"][font_name]["hash"] != current_hash
4. 美しい表示

Richライブラリを使って、見やすい出力にしてもらいました。CLIツールのわかりやすい表示っていいですね。

from rich.table import Table

def list_fonts():
    """フォント一覧を表示"""
    table = Table(title="フォント一覧")
    table.add_column("状態", style="cyan", width=6)
    table.add_column("フォント名", style="magenta")
    table.add_column("サイズ", justify="right")
    table.add_column("更新日時")

    for font_file in font_files:
        status = "✓" if is_installed(font_file) else "✗"
        size = humanize.naturalsize(font_file.stat().st_size)
        modified = font_file.stat().st_mtime

        table.add_row(status, font_file.name, size, format_datetime(modified))

    console.print(table)

という形で開発が完了します。

驚くべきことにここまで1時間もかからずに開発が終わりました。

実際に使ってみる

セットアップ

まず、共有フォルダを設定します。

$ font-sync init

font-syncの初期設定を開始します。

同期元フォルダのパスを入力してください。
: ~/Dropbox/shared-fonts/
同期元フォルダのパス [~/Dropbox/shared-fonts/]: ~/Dropbox/MyFonts/

✓ 設定を保存しました。

普段の使い方

新しいフォントを買ったら、共有フォルダにフォントファイルを放り込んで、以下のように実行

$ font-sync sync

同期元フォルダ: ~/Dropbox/MyFonts/23個のフォントファイルが見つかりました。

  差分を確認中... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%3個のフォントを正常に同期しました。

これで終わりです。もう片方のMacでも同じコマンドを実行すれば、同じフォント環境が手に入ります。

それでは、Cursor(Claude 4)ちゃんに技術的なアピールポイントを語って頂きましょう!

技術的なポイント(by Cursor)

1. macOS専用にした理由

最初はクロスプラットフォーム対応も考えたんですが:

  • Windowsは C:\\Windows\\Fonts\\ で権限周りが複雑
  • Linuxはディストリビューションごとに場所が違う
  • フォントキャッシュの扱いも異なる

結局、自分が使うmacOSに特化することで、シンプルで確実な実装にしました。

2. なぜCLIなのか

GUIも考えましたが:

  • サクッと作れる(週末プロジェクトなので)
  • 自動化しやすい(cronやlaunchdで定期実行可能)
  • リモートでも使える(SSH経由でも同期できる)

3. Pythonを選んだ理由

  • macOSに標準でインストールされている
  • ファイル操作が得意
  • RichやTyperなど、CLIツール作成に便利なライブラリが豊富

今後の展望

現在は基本機能のみですが、以下の機能を追加予定:

  • 自動同期: フォルダを監視して自動で同期
  • フォントのプレビュー: ターミナル上でフォントの見た目を確認
  • タグ機能: フォントをカテゴリ分けして管理
  • GUI版: より使いやすいデスクトップアプリ版(有料予定)

ということでした。GUIは有料で売るらしい。そしたら買ってね!

自動同期についてはあると便利そう。コマンド叩くの忘れちゃいそうだから、なにかをフックに確認できるといいな。まあでもAdobeソフトを起動してフォントないって言われたら気づくか。ひとまずこれで、僕の悩みが解消されました888ってところです。

まとめ

「フォントの同期めんどくさいな〜小規模な趣味なのにそんなにお金も払いたくないな〜」という悩みから始まったプロジェクトですが、意外とちゃんとしたツールになりました。(AIのトークンにお金を払ってるじゃないかとかは言わない約束)

せっかく公開したので、同じような悩みを持つ方がいたら、ぜひ使ってみてください。GitHubリポジトリで公開しているので、気が向いたらスター⭐️もしてね 😆

前後の記事

ENRO電気式窯焼マスターを買いました。自宅で450℃の本格ピザ!
◀︎ 次の記事

ENRO電気式窯焼マスターを買いました。自宅で450℃の本格ピザ!

astro-notion-blogに目次機能を追加してみた
前の記事 ▶︎

astro-notion-blogに目次機能を追加してみた