しろもじメモランダム

文字についてあれこれと。

fontTools のペンを使ってグリフのアウトラインを取得する

先日 Twitter で、「グリフのアウトラインの座標列を取りたいんだけど」「それ pen protocol でできるよ!」というやりとりをしました。この記事では、pen protocol に対応したペンでアウトラインを取得する方法について、具体的に解説してみます。

pen protocol とペン

さて、そもそも pen protocol とは何でしょう? 今回はプロトコルの詳細には触れず、概観だけ説明します。

フォント界隈では Python が共通言語になっています。しかし、グリフを表すためのオブジェクトは、フォントエディタ(Glyphs, RoboFont, FontLab, ...)やライブラリ(fontTools, ufoLib, defcon, fontParts, ...)ごとにそれぞれ独自に定義されています。ざっくり言ってしまうと、「どんなグリフオブジェクトであれ、共通のインターフェイスでアウトラインを読み書きできると楽だよね」「SVG や PDF やいろんな描画 API にも対応したいよね」というのが pen protocol の発想です。pen protocol によってグリフからアウトラインを得たり、アウトラインを描いたりするためのオブジェクトをペン(pen)と呼びます。

pen protocol が広まった結果、現在までにいろいろなペンが実装されてきました。以下にいくつか例を挙げますが、これ以外にも存在しています。

なお、pen protocol には segment pen protocolpoint pen protocol の2系統があり、前者に対応したペンは HogePen、後者は HogePointPen のようなクラス名になっているのが通例です。この記事では、前者 segment pen protocol のペンをとり上げます*1

フォントを読み込んでグリフを準備する

以下、環境は Python 3.6.3, fontTools 3.20.1 です。fontTools は $ pip install fonttools でインストールできます。

ペンのことは一旦あと回しにして、まずはフォントファイルを読み込みます。今回は例として、源ノ角ゴシック Regular SourceHanSans-Regular.otf を使いました。

from fontTools.ttLib import TTFont
font = TTFont('SourceHanSans-Regular.otf')

次に、glyphSet と呼ばれる辞書様オブジェクトと、cmap(文字コードとグリフ名の対応)を取得しておきます。後者に関しては、従来 cmap テーブル font['cmap'] からサブテーブルを選んで辿っていく必要がありましたが、最近追加されたお手軽便利メソッド getBestCmap() でいい感じに取得できます。

glyph_set = font.getGlyphSet()  # {グリフ名: グリフ} っぽいオブジェクト
cmap = font.getBestCmap()       # {Unicode: グリフ名}

これらを使って、文字 char に対応したグリフオブジェクトを返す関数を作ります。

def get_glyph(glyph_set, cmap, char):
    glyph_name = cmap[ord(char)]
    return glyph_set[glyph_name]

例として、文字 "L" に対応するグリフオブジェクトを、 L としておきましょう。

L = get_glyph(glyph_set, cmap, 'L')

これでグリフの準備はできました。

RecordingPen でアウトラインの内容を得る

さて、アウトラインの内容を取得するためには、fontTools に同梱されている RecordingPen というペンを使います。まずはペンのインスタンスを作成します。

from fontTools.pens.recordingPen import RecordingPen
recording_pen = RecordingPen()

次に、このペンをグリフ上で動かします。pen protocol に対応したグリフオブジェクトは draw() メソッドを持っていますので、これにペンを渡して実行します。先ほどの "L" のグリフ L を使ってみましょう。

L.draw(recording_pen)

ペンで "draw" と言われると、どこかに描く・書き込むのかと思ってしまいがちですが、ここではグリフのアウトラインを「なぞる」行為のことだと捉えてください。この RecordingPen では value 属性になぞった結果が入っていますので、見てみましょう。

print(recording_pen.value)
[('moveTo', ((100, 0),)),
 ('lineTo', ((513, 0),)),
 ('lineTo', ((513, 79),)),
 ('lineTo', ((193, 79),)),
 ('lineTo', ((193, 733),)),
 ('lineTo', ((100, 733),)),
 ('closePath', ())]

目的どおりアウトラインの内容が出てきました。"L" の左下の点からスタートし、反時計回りにパスが構成されているのが分かります。

もうひとつ、"い" のグリフで試してみるとこうなります。

い = get_glyph(glyph_set, cmap, 'い')
recording_pen = RecordingPen()
い.draw(recording_pen)
print(recording_pen.value)
[('moveTo', ((226, 696),)),
 ('lineTo', ((130, 698),)),
 ('curveTo', ((135, 674), (136, 633), (136, 610))),
 ('curveTo', ((136, 552), (137, 432), (147, 346))),
 ('curveTo', ((174, 89), (264, -4), (357, -4))),
 ('curveTo', ((425, -4), (486, 53), (545, 221))),
 ('lineTo', ((482, 293),)),
 ('curveTo', ((456, 193), (410, 91), (359, 91))),
 ('curveTo', ((289, 91), (241, 200), (225, 366))),
 ('curveTo', ((218, 447), (217, 538), (218, 600))),
 ('curveTo', ((219, 626), (222, 672), (226, 696))),
 ('closePath', ()),
 ('moveTo', ((742, 669),)),
 ('lineTo', ((664, 642),)),
 ('curveTo', ((758, 526), (818, 330), (835, 152))),
 ('lineTo', ((916, 184),)),
 ('curveTo', ((902, 351), (831, 554), (742, 669))),
 ('closePath', ())]

曲線を中心とした、2つのパスで構成されています。

SVGPathPen を利用してSVGを作成する

今度は応用として、SVGPathPen というペンを使ってみましょう。このペンは、SVGパスデータ文字列を組み立ててくれます。SVGPathPen のコンストラクタは引数に glyphSet をとりますので、最初の方で用意した glyph_set を渡します。

from fontTools.pens.svgPathPen import SVGPathPen
svg_path_pen = SVGPathPen(glyph_set)

グリフ L をなぞった後、getCommands() メソッドでパスデータ文字列を取得します。

L.draw(svg_path_pen)
print(svg_path_pen.getCommands())
M100 0H513V79H193V733H100Z

パスデータ文字列が表示されました。左下の点 100 0 からスタートし、水平線 H と垂直線 V でパスが構成されています。

以下のようにガワを手書きして、独立したSVGファイルを作ります。

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
    <path d="M100 0H513V79H193V733H100Z"/>
</svg>

このSVGファイルをブラウザで表示してみると、こんな感じです。

一応「L」のグリフが表示できました。が、上下逆さです。OpenType の座標系では y 軸が上方向に延びていますが、SVG では下方向に延びているため、そのままだと上下がひっくり返ってしまいます。transform 属性で上下を逆にし、viewBox 属性も調整しましょう。

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -1000 1000 1000">
    <path d="M100 0H513V79H193V733H100Z" transform="scale(1, -1)"/>
</svg>

これで正立しました。

最後に、もうちょっといい感じのSVGファイルを生成する関数を定義してみます。

from textwrap import dedent

def save_as_svg(font, char, output_path):
    '''TTFont オブジェクトを受け取り、指定した文字のグリフを SVG として保存する'''
    
    glyph_set = font.getGlyphSet()
    cmap = font.getBestCmap()
    
    # グリフのアウトラインを SVGPathPen でなぞる
    glyph = get_glyph(glyph_set, cmap, char)
    svg_path_pen = SVGPathPen(glyph_set)
    glyph.draw(svg_path_pen)

    # メトリクスを取得
    ascender = font['OS/2'].sTypoAscender
    descender = font['OS/2'].sTypoDescender
    width = glyph.width
    height = ascender - descender
    
    content = dedent(f'''\
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 {-ascender} {width} {height}">
            <g transform="scale(1, -1)">
                <!-- ボディの枠 -->
                <rect x="0" y="{descender}" width="{width}" height="{height}"
                    stroke="cyan" fill="none"/>
                <!-- グリフ座標系の原点 -->
                <circle cx="0" cy="0" r="5" fill="blue"/>
                <!-- グリフのアウトライン -->
                <path d="{svg_path_pen.getCommands()}"/>
            </g>
        </svg>
    ''')
    
    with open(output_path, 'w') as f:
        f.write(content)

"L" と "い" で実行してみます。

save_as_svg(font, 'L', 'L.svg')
save_as_svg(font, 'い', 'い.svg')

こんな感じの SVG ファイルができました。めでたしめでたし。

おわりに

この記事では pen protocol の概要を説明し、ペンを使ってアウトラインを取得したり、SVG として表示する方法について見てきました。一方、今回触れなかった話題としては、

  • pen protocol の詳細とペンの定義方法
  • グリフへの書き込み
  • ペンを利用したアウトラインの加工
  • さまざまなペンの紹介

などがあります。これらに関しては、またいずれ気が向いたときに記事を書くかもしれません。

なお、今回のファイルは以下のリポジトリにまとめておきました。どうぞご利用ください。

github.com

*1:若干紛らわしいのですが、segment pen protocol は単に pen protocol と呼ばれることがあります。

TTXによるフォントのチラ見をちょっといい感じにする設定

github.com

仕事場/プライベート問わずに大活躍の fontTools ですが、その中でも頻出なのが「フォントファイルを TTX でダンプして中身をチラ見したい」という場面です。

$ ttx -o out.ttx [オプション] font.otf
$ less out.ttx

つい最近まで、上のようにしてごく普通に中身を見ていたのですが、何度もやっているといい加減めんどくさくなってきます。そこで、.zshrc(適宜 .bashrc などに読み替えてください)に簡単なシェル関数を定義してみました。

tl() {
    ttx -q -o - $* | source-highlight -s xml -f esc | less
}

関数名はなんでもいいのですが、ttxless なので短く tl にしておきました。途中の source-highlightGNU Source-highlight というシンタックスハイライトの定番ツールです。こうしておけば、

$ tl [オプション] font.otf

とするだけでちょっといい感じに中身がチラ見できるようになります。べんり。

f:id:mashabow:20170111232302g:plain

OpenType の仕様策定の議論をのぞくには

OpenType は1997年に MicrosoftAdobe が策定したフォントフォーマットで、現在のデファクトスタンダードになっています*1。仕様は Microsoft のサイトで公開されており、興味があればだれでもすぐに読むことができます。

この OpenType の仕様はときたま改定されており、今のところは2015年3月に発表された v1.7 が最新バーションです。

ところで、この仕様の改定はどこで議論されているんでしょう? UCS/Unicode や各種 Web 標準などは策定プロセスがオープンになっていますが、OpenType の仕様はどこで議論が進んでいるのか、ぱっと見ただけではよくわかりません。日本語の情報もほとんど見つかりません。というわけで、この記事でその一端を紹介したいと思います。

OpenType メーリングリスト

実は、Microsoft のサイトにこのメーリングリスト(ML)の案内がちゃんと載っています。

An e-mail based discussion forum has been set up specifically to help people working on the development of OpenType fonts and associated technologies.

載っているのですが、indx.co.uk という馴染みのないドメインだったり、登録前に ML のアーカイブが見れなかったり、ページのフッタに This page was last updated 9 August 2004. と書いてあったりと、登録に躊躇してしまう要素が満載です。

が、これでも現役でちゃんと生きている公式 ML です。to help people working on the development of OpenType fonts and associated technologies と書かれていてスコープが少しぼんやりとしていますが、OpenType の仕様に関する話題が現在中心になっています。

この ML へ登録するためには、subscribe-opentype@indx.co.uk にメールを送ります*2。このとき空メールではだめで、メールの本文が必要です。自己紹介や「登録したいんだけど」みたいなことを1, 2文書いておけば大丈夫なはずです。ML 管理者の承認が降りれば、数日後に通知のメールが来ますので、これで登録完了です。過去のアーカイブは、ML 登録完了後にこのページからたどれます。ただしシステムが古いようで、使い勝手は残念な感じです。

なお、この ML から配信されるメールは、件名の頭に [OpenType] がついています。

mpeg-OTspec メーリングリスト

世の中には ISO/IEC 14496-22 Open Font Format (OFF) という規格があります。といっても OpenType と中身は同じ…というか、OpenType をそのまま公的な国際規格にしたのがこの OFF です。デジュール標準というやつです。ISO が規格票を無料で公開しており、以下のページからPDFのダウンロードが可能です。ちなみに、ISO/IEC 14496-22:2015 が OpenType v1.7 に対応します。

そして、この規格について議論するための ML が米 Yahoo! Groups に用意されています。これが mpeg-OTspec ML です。

こちらの ML は mpeg-otspec-subscribe@yahoogroups.com に空メールを送信するだけですぐに参加できるので、OpenType ML と比べるととっつきやすいかもしれません。過去のアーカイブや添付ファイルの閲覧には米 Yahoo! のアカウントが必要になりますので、アカウントを持っていなければ作っておきましょう。

なお、この ML から配信されるメールは、件名の頭に [mpeg-OTspec] がついています。

ちなみに、mpeg-OTspec ML の近年の流量は以下のとおりで、話題があるときとないときでムラがあります。これは OpenType ML の方も同様です。

f:id:mashabow:20160621000505p:plain

また、両 ML 間では転送やクロスポストが多く、両方とも登録していると若干カオスです。

おまけ

最後におまけとして、以下のリポジトリを紹介しておきます。位置づけがまだよくわかりませんが、OpenType レイアウトに関する追加仕様のドラフトのようです。

github.com

*1:ややこしいので詳細は省きますが、今どきの TrueType は OpenType に含まれます。また、Web フォントで使われる WOFF は、ざっくり言えば OpenType を zlib 圧縮したものです。

*2:一時期は opentype-migration-sub@indx.co.uk というアドレスだったようです(参考)。

二号明朝活字書体見本(明治26年)

TL;DR

東京築地活版製造所『二号明朝活字書体見本』(明治26年/1893年)の画像を Flickr で公開しました。

二号明朝活字書体見本

PDF 版がほしい方は下のページから。

いきさつ

先月末、Twitter でこんなやりとりをしていました。

この『二号明朝活字書体見本 全』というのは、2年半ほど前にたまたま運良く入手した活字見本帳です。自分の本棚に古い見本帳があるというのは、もちろん飛び上がるほど嬉しいわけですが、それをそのまましまっておくのもどうももったいない。どうせなら、文字に興味を持ったいろいろな人に見てもらいたい*1。……などということを以前から考えていました。

そんな中でこのやりとりがあり、チャンスだと思って(ずうずうしくも)撮影をお願いしてみました。この内田(@uakira2)さんは以前、築地五号の見本帳のスキャンや、『光をかかぐる人々』の撮影をされています。ありがたいことに、今回快く撮影を引き受けてくださり、こうして『二号明朝活字書体見本 全』の画像を公開することができました。下の Filckr アルバムで全ページ閲覧できますので、どうぞご覧ください。表紙を含めて画像は88枚あります。

二号明朝活字書体見本

いつものように PDF 版も用意してあります。

見本帳概観

二号明朝活字書体見本_16

この見本帳の8割以上は、漢字のページで占められています。字種数はまだ数えていませんが、総数見本帳だけあってさまざまな字種が載っています。その分、整ったデザインの活字(頻出する漢字)から、その場しのぎっぽいバランス悪い活字(あまり出てこない漢字)まで、クオリティもさまざまです。

二号明朝活字書体見本_76

部首順に並んだ漢字が終わると、次は分合活字(この見本帳では「分合文字」とよぶ)のページに移ります。横幅が1/3, 2/3の2種類の活字があり、この両者を偏旁として組み合わせ、漢字1字を組み立てるようになっています。

二号明朝活字書体見本_81

その次はかな類が続きます。カタカナ・ひらがなともに、非常に洗練された細身のデザインです。上のツイートにあるように、自分の Twitter のヘッダ画像にはこのひらがな部分の写真を使っていますが、プロフィールページを開くたびについつい見とれてしまいます。

あとは記号類が少し並んで終わりです。全84ページ。奥付は以下のようになっています。

明治廿六年六月六日印刷
明治廿六年六月十日出版

   日本東京市京橋區築地二丁目拾七番地
     印刷者  曲田 成
     印刷所  東京築地活版製造所

というわけで、どうぞご活用ください。

*1:というかそもそも自分自身、ほかの人が持っている見本帳をいろいろ見たくてしょうがなかった

同型類字集 明朝体

前回の記事「同型類字集 等線体」のつづきです。

『同型類字集 等線体』の方は昭和17年(1942年)12月発行でしたが、こちらの『同型類字集 明朝体』は3ヶ月後の昭和18年(1943年)3月発行となっています。等線体が明朝体になっている以外は、まったく同じ体裁・内容のようです。

同型類字集 明朝体

上の画像をクリックすると、Flickr でファイルが閲覧できます。例によって PDF にまとめたものも用意しましたので、必要な方は下のページからどうぞ。

例示されている明朝体を見てみると、やや直線的で整理された感じの雰囲気になっています。字体の省略や簡略化も見られますが、この処理は等線体のものと完全に一致しているようで、字体の整理・統一への意識が読み取れます。

同型類字集_明朝体_02

他の明朝体と比べて特徴的なのは、撥ねの処理でしょうか。上の画像でわかるように、「力」や「巾」の撥ねが省略され、終筆を止めています。その一方で、「刂」や「寸」の撥ねはそのままの形で残っており、ちょっと不統一な感じもします。この撥ねる・撥ねないの間に、なにか法則性はあるんでしょうか…? わかる方いましたらぜひ教えてください。

同型類字集_明朝体_01

さて、上の画像の左ページは表紙裏にあたる箇所ですが、ここにはこんな注意書きが書かれています。

一、一般字典ノ例ニ拠ルコトナク結体又ハ点画ヲ同シウスルモノニ分類編纂シ以テ練習ニ便ナラシム

二、本所載以外ノ文字ヲ必要トスル場合ハ末尾ニ添付シアル扁旁部首ニヨリ所要ノ扁旁冠脚等ニ供シ得ル文字ヲ選出シ適宜組成スルモノトス
煩字及略字ハ別ニ示ス

備考
本類字集ノ字画ハ普通大ノ註記ニ適用スル為メ定メタルモノニシテ字高大ナル註記ニ在リテハ字典ヲ参照シ点画ヲ適宜粉飾スルコトヲ得

といっても、個人的に気になるのはその内容ではなく、印刷されている文字の方です。地図や海図の製作には早くから写植が導入されていたようですが、このページの文字も写植で打たれており*1、写真植字機研究所(のちの写研)の明朝体のように見えます。しかしよく見てみると、2行目の「習」や3行目の「場」など、普通の明朝体とはちょっと変わっていませんか…?

f:id:mashabow:20151206142130j:plain

どうもこれは、この『同型類字集 明朝体』で示されているとおりに、写植の明朝体を修整しているようなのです。さらによく見てみると、1行目の「據」や2行目「編」などの撥ねも、しっかり取り除かれていることがわかります。写植で普通に印字したものを器用に修整したんでしょうか。それとも独自仕様の明朝体の文字盤なんてものがあったんでしょうか。いずれにせよ、なかなかのこだわりです。

この『同型類字集』と関係あるのかないのか、『地図用文字』なる本があると知ったので、こちらも気になる今日このごろです。

*1:巻末の「扁旁部首」もそうです。

同型類字集 等線体

何ヶ月か前に、『同型類字集 等線体』『同型類字集 明朝体』という小冊子を入手しました。 陸地測量部が戦時中に作ったもので、地図に書き入れる文字のレタリング見本・字体見本のようです。

冊子を綴じてあったホチキスが錆びていたこともあり、せっかくなのでスキャンして公開してみることにしました。今日はまず、昭和17年(1942年)12月発行の『同型類字集 等線体』です。部屋にある家庭用複合機(ブラザー DCP-J940N)を使い、600 dpi のグレースケールでスキャンしました。

同型類字集 等線体

上の画像をクリックすると、Flickr でファイルが閲覧できます。なお、入手時点で冊子末尾の数ページが欠落していたため、「扁旁部首」の6画の途中で終わっています。PDF にまとめたものも用意しましたので、下のページからダウンロードしてください。

この等線体は見ての通り、ポキポキとした直線的な感じの書体になっています。フトコロも広く、払いがぐっと左右外側に向かっているのも特徴的です。昔のレタリング文字ではよく見ますが、そういえば現代のフォントにはこういうデザインあまりないですね。字体に関していえば、常用漢字ほどではありませんが、ちょこちょこと省略・簡略化がみられます。違いがさりげなさすぎて見落としそうなものも……。

次回は『同型類字集 明朝体』の予定です。(→ 書きました

はてなブログに引っ越してきました

いままでこのブログははてなダイアリーというサービスを使っていましたが、同社の後継サービスであるはてなブログの機能が充実してきたようなので、こちらに引っ越してきました。URL は以下のように変更になります。

過去記事はもちろんのこと、記事へ寄せていただいたコメントやはてブはてなスターも、一緒に移行されているはずです。 また、旧URLへアクセスすると、自動的に新URLにリダイレクトされるようになっています。 昔の記事については、いま読み返してみるといろいろとはずかしいことが書いてあったりしそうですが(読み返せてない)、恥を忍んでそのまま晒しておくことにします。 リンク切れや古い情報もそのままになっていますので、ご注意ください。

引っ越してきたのでこれからバリバリ記事書きます!!などというわけでもないのですが、よろしくお願いいたします。