iOSにフォントをいっぱいインストールしたい

# 概要
　たくさんのフォントをiOSにインストールするためのスクリプトを書いたよ。

# iOSにオリジナルのフォントをインストールするための選択肢
* iOS Appを使う(たとえば[AnyFont](https://itunes.apple.com/jp/app/anyfont/id821560738))
* [Apple Configurator 2](https://itunes.apple.com/jp/app/apple-configurator-2/id1037126344)を使う
* その他？

# 実際にやってみる
　Apple Configurator 2は、Apple公式のApp且つ無料ということで使ってみる。
「ファイル」＞「新規プロファイル」で、あとはフォントを追加するだけ…
![MyFonts.mobileconfig.png](https://qiita-image-store.s3.amazonaws.com/0/116905/9e442408-23d8-e10e-0ee8-90cd246017cf.png)
…あれ？1つずつしかフォントファイルを追加できないんですが…[^1]。
[^1]: バージョン2.4 (3E45)現在
　1つのプロファイルに複数のフォントを埋め込むことはできるけど、ファイル選択ダイアログで複数のフォントファイルを同時に選択できない。1つや2つならいいけど、たくさんのフォントを埋め込みたかったら、ファイル選択ダイアログを開いてファイルを選んで…ということをフォントの数だけ繰り返さなくてはいけない。これは面倒。

# あなたがプログラマーなら
　繰り返す単純作業は機械に任せますよね？

## .mobileconfigってどんなファイル？
　さて、構成プロファイルは、`*.mobileconfig`というファイルとして保存されます。…そんなファイル形式知らない？
　では、調べてみましょう。「[構成プロファイルリファレンス](https://developer.apple.com/jp/documentation/featuredarticles/iPhoneConfigurationProfileRef/Introduction/Introduction.html)」という記事を読むと…なんと、中身は単なるXMLファイル(もっと言えばmacOSではお馴染みのproperty list)だとのこと。それならなんとかなりそうですね。macOS以外のOSで作成することも可能です。

## .mobileconfigにフォントを埋め込むには

`MyFont-001.TTF`と`MyFont-002.TTF`を埋め込んだ一例：

````xml:MyFont.mobileconfig
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>PayloadContent</key>
	<array>
		<dict>
			<key>Font</key>
			<data>
			[略]<!-- Base64エンコードされたフォントファイルのデータ -->
			</data>
			<key>Name</key>
			<string>MyFont-001.TTF</string>
			<key>PayloadDescription</key>
			<string>Configures Font settings</string>
			<key>PayloadDisplayName</key>
			<string>Font</string>
			<key>PayloadIdentifier</key>
			<string>com.apple.font.some-uuid-001</string>
			<key>PayloadType</key>
			<string>com.apple.font</string>
			<key>PayloadUUID</key>
			<string>some-uuid-001</string>
			<key>PayloadVersion</key>
			<integer>1</integer>
		</dict>
		<dict>
			<key>Font</key>
			<data>
			[略]<!-- Base64エンコードされたフォントファイルのデータ -->
			</data>
			<key>Name</key>
			<string>MyFont-002.TTF</string>
			<key>PayloadDescription</key>
			<string>Configures Font settings</string>
			<key>PayloadDisplayName</key>
			<string>Font</string>
			<key>PayloadIdentifier</key>
			<string>com.apple.font.some-uuid-002</string>
			<key>PayloadType</key>
			<string>com.apple.font</string>
			<key>PayloadUUID</key>
			<string>some-uuid-002</string>
			<key>PayloadVersion</key>
			<integer>1</integer>
		</dict>
	</array>
	<key>PayloadDescription</key>
	<string>Yes, My Fonts</string>
	<key>PayloadDisplayName</key>
	<string>My Fonts</string>
	<key>PayloadIdentifier</key>
	<string>com.example.fonts.my-fonts</string>
	<key>PayloadOrganization</key>
	<string>my company</string>
	<key>PayloadRemovalDisallowed</key>
	<false/>
	<key>PayloadType</key>
	<string>Configuration</string>
	<key>PayloadUUID</key>
	<string>some-payload-uuid</string>
	<key>PayloadVersion</key>
	<integer>1</integer>
</dict>
</plist>

````

## .mobileconfigにフォントをいっぱい埋め込むには
　さて、プログラミングのお時間です。バイナリデータ(フォント)の読み込み・Base 64エンコード・テキストファイル(XMLファイル)への書き出しができればいいので、実装はなんでもいいでしょう。私は[Ruby](https://www.ruby-lang.org/)を選びました。
　そんなに難しいことはないので、ソースだけどーん：

````ruby
#!/usr/bin/env ruby

require 'pathname'
require 'base64'

filenames = ARGV.map {|filename| Pathname(filename) }

if filenames.size < 1
  $stderr.puts("No font files specified...")
  exit(false)
end

Directory = Pathname("~/Library/Mobile Documents/iCloud~com~apple~configurator~ui/Documents").expand_path

puts("Enter filename:")
output = $stdin.gets.chomp
output += '.mobileconfig' if output !~ /\.mobileconfig$/i
output = Directory + Pathname(output)
puts("-> Data will be written to '#{output.to_s}'.")

puts("Enter display name: (e.g. My Fonts)")
displayName = $stdin.gets.chomp

puts("Enter description: (e.g. These are my fonts.)")
description = $stdin.gets.chomp

puts("Enter identifier: (e.g. com.example.font.my-font)")
identifier = $stdin.gets.chomp

puts("Enter organization: (e.g. My Company)")
organization = $stdin.gets.chomp

puts("-----")
puts("Creating #{output.basename.to_s} ")

output.open('w'){|mobileconfig|
  mobileconfig.puts('<?xml version="1.0" encoding="UTF-8"?>')
  mobileconfig.puts('<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">')
  mobileconfig.puts('<plist version="1.0">')
  mobileconfig.puts('<dict>')
  mobileconfig.puts('	<key>PayloadContent</key>')
  mobileconfig.puts('	<array>')
  
  # Read, convert, and write fonts data.
  filenames.each {|filename|
    font_uuid = `uuidgen`.chomp
    
    mobileconfig.puts('		<dict>')
    mobileconfig.puts('			<key>Font</key>')
    mobileconfig.puts('			<data>')
    
    filename.open('rb') {|font|
      # Read data 52 * 6 bits (39 bytes) at a time.
      buffer = "\0" * 39
      while !font.read(39, buffer).nil?
        mobileconfig.puts("			#{Base64.strict_encode64(buffer)}")
      end
    }
    
    mobileconfig.puts('			</data>')
    mobileconfig.puts('			<key>Name</key>')
    mobileconfig.puts("			<string>#{filename.basename.to_s.encode(Encoding::UTF_8, :xml => :text)}</string>")
    mobileconfig.puts('			<key>PayloadDescription</key>')
    mobileconfig.puts('			<string>Configures Font settings</string>')
    mobileconfig.puts('			<key>PayloadDisplayName</key>')
    mobileconfig.puts('			<string>Font</string>')
    mobileconfig.puts('			<key>PayloadIdentifier</key>')
    mobileconfig.puts("			<string>com.apple.font.#{font_uuid}</string>")
    mobileconfig.puts('			<key>PayloadType</key>')
    mobileconfig.puts('			<string>com.apple.font</string>')
    mobileconfig.puts('			<key>PayloadUUID</key>')
    mobileconfig.puts("			<string>#{font_uuid}</string>")
    mobileconfig.puts('			<key>PayloadVersion</key>')
    mobileconfig.puts('			<integer>1</integer>')
    mobileconfig.puts('		</dict>')
  }
  
  
  mobileconfig.puts('	</array>')
  mobileconfig.puts('	<key>PayloadDescription</key>')
  mobileconfig.puts("	<string>#{description.encode(Encoding::UTF_8, :xml => :text)}</string>")
  mobileconfig.puts('	<key>PayloadDisplayName</key>')
  mobileconfig.puts("	<string>#{displayName.encode(Encoding::UTF_8, :xml => :text)}</string>")
  mobileconfig.puts('	<key>PayloadIdentifier</key>')
  mobileconfig.puts("	<string>#{identifier.encode(Encoding::UTF_8, :xml => :text)}</string>")
  mobileconfig.puts('	<key>PayloadOrganization</key>')
  mobileconfig.puts("	<string>#{organization.encode(Encoding::UTF_8, :xml => :text)}</string>")
  mobileconfig.puts('	<key>PayloadRemovalDisallowed</key>')
  mobileconfig.puts('	<false/>')
  mobileconfig.puts('	<key>PayloadType</key>')
  mobileconfig.puts('	<string>Configuration</string>')
  mobileconfig.puts('	<key>PayloadUUID</key>')
  mobileconfig.puts("	<string>" + `uuidgen`.chomp + "</string>")
  mobileconfig.puts('	<key>PayloadVersion</key>')
  mobileconfig.puts('	<integer>1</integer>')
  mobileconfig.puts('</dict>')
  mobileconfig.puts('</plist>')
}

puts('-- done.')
````

　コマンドライン引数としてフォントファイルのパスを複数受け取って処理するだけのスクリプトです。構成プロファイルはApple Configurator 2のiCloud Driveのフォルダに作成されるようになっています。
　個人用途なのでエラー処理とかはしていませんが、気になる人はしてみてもいいでしょう。

## あとは出来上がった構成プロファイルを端末にインストールするだけ
　前述の「[構成プロファイルリファレンス](https://developer.apple.com/jp/documentation/featuredarticles/iPhoneConfigurationProfileRef/Introduction/Introduction.html)」には、こう書かれています：
> 構成プロファイルの配布方法は5通りあります。
> * App Storeから入手可能なApple Configurator 2を使用
> * 電子メールで配布
> * ウェブページ上で配布
> * 『無線経由のプロファイル配信と構成』で説明したワイヤレス構成を利用
> * Mobile Device Management Serverを利用（無線）

# 注意点・問題点
## フォントのライセンス
　あなたがiOSにインストールしたいと思っているフォントは、本当にiOSにインストールしても大丈夫ですか？
　他人への配布？？そんなのもってのほかではないですか？
　フォントのライセンスには気をつけましょう。

## iOSのあらゆるAppでフォントを変えられる訳ではない
　これは仕方ありません。Appが対応している必要があります。
　“メモ”やPages, Numbers, KeynoteなどのApple公式のAppはもちろん、Word, ExcelなどMicrosoftのAppも対応しているようです。

## .mobileconfigのファイルサイズが大きいとApple Configurator 2で転送できない
　ファイルサイズが20-30MBを越えるとApple Configurator 2で端末に転送できないようです[^2]。その場合は、多少面倒でも電子メールやWebページ経由でやるしかありません。
[^2]: バージョン2.4 (3E45)現在

## .ttc, .otc, .dfontなどは使えない
　複数のフォントを収納した`.ttc`(TrueType Collection)は使えないようです[^3]。`.otc`, `.dfont`も同じ。一旦、複数の`.ttf`ファイルや`.otf`ファイルに分割してから構成プロファイルに埋め込む必要があります。
[^3]: iOS 10.3.2現在