Help us understand the problem. What is going on with this article?

Variable Fontにまつわるフォントテクノロジー

はじめに

Variable Fontは2016年頃よりグラフィックデザインの現場、とりわけWebフロント方面を中心に話題になりつつありますが、本トピックではmacOSおよびiOSのネイティブ環境でVariable Fontを実装するための技術を紹介します。また、それに先立って私が調べた範囲でのVariable Fontにまつわる歴史を簡単ではありますがご紹介します。
内容はAppleのテクノロジーの視点に寄ります。

Variable Fontとフォント技術の歴史

Variable Fontにまつわるフォント技術の歴史について、私が調査した範囲でまとめます。「Variable Fontを…するAPI」などの方法論だけ捉えるよりも、いろいろな背景が見えてくると思います。

OpenType Variable Font

昨今よく耳にするVariable Font技術は、正確には “OpenType Variable Fonts” と表現するのがおそらくは正しいのでしょう。OpenType 1.8 に実装された同機能はざっくり言うと、フォントファミリーで複数のスタイルをひとまとめにし、それらを動的に切り替えられるようにする仕組みです。例えば一つの.otfファイルのみで複数のウェイト、字幅、字間など各種スタイルを扱い、描画エンジン側からパラメータを変更することができます。

OpenType Variable Fontに関する概要は、こちらの記事(英語)をまず読んでみてください。仕組みや経緯などが詳しく紹介されています。
Introducing OpenType Variable Fonts – John Hudson

また、OpenTypeの仕組みはMicrosoft Typographyで詳しく解説されています。OpenTypeはMicrosoftとAdobeが開発したテクノロジーであることから、商標も含めMicrosoftに多くの情報が集約されています。OpenTypeに限らず、フォント技術に関する情報を求めるならばMicrosoft Typographyをチェックするのがオススメです。

Microsoft Typography – Microsoft

image.png
引用:https://docs.microsoft.com/en-us/typography/opentype/spec/otvaroverview

TrueType と Apple Advanced Typography

TrueTypeはデジタルフォントの主要形式の一つとして広く普及しています。1990年代初頭、当時のAdobe Systemsが持っていたPostScript Type1テクノロジーへの対抗として、Apple Computerが開発したフォント技術です。背景にはAdobeに対して発生するロイヤリティの回避があったようです。AppleはTrueTypeをMicrosoftにもライセンスしたので、Windowsにおいても標準的なフォント技術の一つとして採用されています。

Appleは現在、TrueTypeをベースとしたApple Advanced Typography (AAT) テクノロジーをmacOSやiOSのフォントレンダリングに採用していますが、アプリケーションのデベロッパーは何か特殊な事情でも無い限りはAATの存在自体をあまり認知することはないでしょう。同技術に触れるには一般にCore Text APIを経由します。
AppleオリジナルのSan Franciscoフォントの配布ページ Fonts for Apple Platforms にはAATへの言及があります。

Fonts for Apple Platformsページのイメージ
引用:Fonts for Apple Platforms

QuickDraw GX / TrueType GX

AATは、Classic Mac OSの時代に使われていたQuickDraw GX / TrueType GXの後継技術に当たり、Mac OS 8.5から正式に採用されています。QuickDraw GXは当時のMac OS向けグラフィックスアーキテクチャですが、その一部としてTrueType技術を拡張するTrueType GXテクノロジーが後に追加されたとのことです。

Apple included full TrueType support in its Macintosh operating system, System 7, in May 1991. Its more recent development efforts include TrueType GX, which extends the TrueType format as part of the new graphics architecture QuickDraw GX for the MacOS. TrueType GX includes some Apple-only extensions to the font format, supporting Style Variations and the Line Layout Manager.
A brief history of TrueType – Microsoft Typography

(Appleは、1991年5月にMacintoshのオペレーティングシステムであるSystem 7に完全なTrueTypeサポートを組み込みました。その後の開発努力には、Mac OS用の新しいグラフィックアーキテクチャQuickDraw GXの一部としてTrueType形式を拡張するTrueType GXがありました。 TrueType GXには、フォント形式に対するApple専用の拡張機能がいくつか含まれており、スタイルバリエーションと行レイアウトマネージャーをサポートしています。)


QuickDraw GX was eventually "killed" with the purchase of NeXT and the eventual adoption of the Quartz imaging model in Mac OS X, but many of its component features lived on and are now standard in the current Macintosh platform; TrueType GX in particular has, with a few tweaks, become a broadly used modern standard in the form of OpenType Variable Fonts.
QuickDraw GX – Wikipedia

(QuickDraw GXは、AppleによるNeXT買収とMac OS XがQuartzアーキテクチャを採用したことにより最終的には殺されましたが、そのコンポーネントの多くは現在のmacOSにも健在です。 特にTrueType GXは、いくつかの調整が加えられ、OpenType Variable Fontsとして広く使われる標準技術になりました。

QuickDraw GXはMac OS X以降はQuartzを中心としたグラフィックスアーキテクチャに置き換わっていますが、TrueTypeやAATなど、当時の設計思想はそのまま受け継がれている部分も見られます。現代のOpenType Variable FontはTrueType GXの設計思想をベースに標準化された技術です。

Font Variation

Font Variation(フォントバリエーション)はVariable Fontを支える技術です。この技術を理解するには、Axis(軸)と呼ばれる概念を把握する必要があります。TrueTypeやOpenTypeではウェイトや文字幅などにバリエーションを持たせることができ、この一つのバリエーションのことをAxisとして数えることになります。ただし細かい仕様に関して、歴史的にはOpenTypeはTrueType GXの発展であるため、Appleのオリジナルの仕様とOpenTypeの仕様とでは似ているようで大きく異なる部分もあるため、注意が必要です。

Weight Axis
出典:https://docs.microsoft.com/ja-jp/typography/opentype/spec/otvaroverview

Variation Axis Tag

OpenTypeのFont Variationで使われる「登録されたVariation Axis Tag」は次の通りです。

Variation Axis tag 名前 意味
wght Weight ウェイト
wdth Width 文字幅
ital Italic イタリック
opsz Optical size オプティカル
slnt Slant Oblique

Registered axis tags – Microsoft Typography

Variable Fontではこれらのタグを使ってパラメータを操作することになります。Variation Axis Tag名を直接、あるいは対応する定数名を指定して、適切なAPIを呼び出してください。AppleプラットフォームのCore Textを使ったVariation Axisの指定方法はこの後で紹介します。

Microsoftによると、OpenTypeにおける登録されたVariation Axis Tagは基本的に上記のものを使うようにし、もしもカスタムのタグを定義する必要があるならばそれも可能とされています。また、カスタムタグの登録を検討することも勧めています。

登録されたAxis Tagのいくつかは、オリジナルであるTrueTypeのVariation Axisから継承されているタグ名のようです。
Font Variations Table – Apple Developer

いくつかのVariation Axis Tagについては、Mozillaのこのページでも詳しく知ることができます。
Variable フォントガイド – MDN

Core Textの力を借り、macOSでVariable Fontを描画

Variable-Font-Mac.gif

ここからは、macOSネイティブアプリケーションやiOSネイティブアプリケーションでVariable Fontを実現するためのCore Text APIを紹介します。今回は上記のgifアニメーションの具合にVariable FontをmacOSネイティブアプリケーションで描画してみます。iOSのサンプルコードは用意できなかったのですが、基幹技術はすべてCore Textに備わっているので、iOSでも同じようなコードで実現できるはずです。
このアプリケーションのソースコードは次のリポジトリで公開しているでよろしければ参考にしてみてください。

https://github.com/usagimaru/Variable-Font-Mac

フォント選び

そもそもフォントがVariable Fontに対応していなければ話になりませんが、今回は技術検証目的であったのであまりここは深掘りしませんでした。サンプルでは次の書体で検証を行いました。

  • システムフォント (San Francisco) …NSFont.systemFont(ofSize: ) で得られます。
  • SFRounded (.SFNSRounded-Regular) …macOSシステムフォントのSFRoundedを直呼び出ししています。あまり良くない方法です。
  • Montserrat
  • Recursive Sans

Variable Font with Hiragino

システムフォントはVariable Fontに対応しています。試してみたところSFRoundedも対応していました。例のgifアニメーションで使っている書体はSFRoundedです。日本語はヒラギノがW0からW9まで揃っているので、システムフォントからのフォールバックでウェイトが連動してくれるようでした。ただ少しだけ描画がおかしな部分も見られました。(私の実装が問題なのかもしれません)
そのほかにもWebで無料配布されていた二つの書体も試していますが、これらも大きな問題なく描画されました。

CTFontCopyVariationAxes(_:)

フォントからVariation Axisに関する情報を取得して、それらを元にして新しいパラメータの値を載せてから、それを再度フォントに反映させるという方針をとります。

Core TextのCTFontCopyVariationAxesからは、フォントが備えるVariation AxisのIdentifierや名前、デフォルト値、最大値、最小値等に関する情報を取得できます。

public func CTFontCopyVariationAxes(_ font: CTFont) -> CFArray?

// Swift では [[String : Any]] にキャストしておくと楽かも

このメソッドは、引数としてCTFontオブジェクトを与えると、そのフォント(ファイル)が持つVariation Axisの情報を次のような形式で返してくれます。型はCFArray(配列)です。Core TextなのでCFやCTのプレフィックスを多く見かけますが、適宜キャストすることでSwiftでも扱いやすくなります。

CTFontCopyVariationAxesで返るデータのダンプ
(
    {
        NSCTVariationAxisDefaultValue = 400;
        NSCTVariationAxisIdentifier = 1196572996;
        NSCTVariationAxisMaximumValue = 1000;
        NSCTVariationAxisMinimumValue = 400;
        NSCTVariationAxisName = GRAD;
    },
    {
        NSCTVariationAxisDefaultValue = 400;
        NSCTVariationAxisIdentifier = 2003265652;
        NSCTVariationAxisMaximumValue = 1000;
        NSCTVariationAxisMinimumValue = 1;
        NSCTVariationAxisName = Weight;
    }
)

NSCTVariationAxis… が並びますが、これらはドキュメントには載っていない値なので次の定数 (CFString) を使ってください。

  • kCTFontVariationAxisIdentifierKey : Axisの固有ID、フォントが違っても同じ意味のAxisなら同じ値になるようです。
  • kCTFontVariationAxisDefaultValueKey : このAxisのデフォルト値です。
  • kCTFontVariationAxisMinimumValueKey : Axisの最小値です。ウェイトなら一番細い値。
  • kCTFontVariationAxisMaximumValueKey : Axisの最大値です。
  • kCTFontVariationAxisNameKey : Axis名です。“Weight”など。
  • kCTFontVariationAxisHiddenKey : AxisをアプリケーションのUIに見せたくない場合に、フォントデザイナーがtrueを指定することがあります。(オプショナル)

Font Variation Axis Dictionary Keys – Apple Developer

Sample1
if let axes = CTFontCopyVariationAxes(font as CTFont) as? [[String : Any]] {
    for axis in axes {
        let axisIdentifier = axis[kCTFontVariationAxisIdentifierKey as String] as? Int
        let axisName = axis[kCTFontVariationAxisNameKey as String] as? String
        let axisDefaultValue = axis[kCTFontVariationAxisDefaultValueKey as String] as? CGFloat
        let axisMinimumValue = axis[kCTFontVariationAxisMinimumValueKey as String] as? CGFloat
        let axisMaximumValue = axis[kCTFontVariationAxisMaximumValueKey as String] as? CGFloat
        let axisHidden = axis[kCTFontVariationAxisHiddenKey as String] as? Bool
    }
}

CTFontDescriptorCreateCopyWithVariation

カスタマイズしたVariation Axisをフォントに反映するには、CTFontDescriptorCreateCopyWithVariationを使います。このメソッドは、元となるフォントデスクリプタ (CTFontDescriptor) に対し、変更したいVariation AxisのIdentifer変更したいVariation Axisに反映する値をそれぞれ引数で与えます。そうすると変更された新たなフォントデスクリプタのコピーが得られます。

public func CTFontDescriptorCreateCopyWithVariation(_ original: CTFontDescriptor, _ variationIdentifier: CFNumber, _ variationValue: CGFloat) -> CTFontDescriptor

このメソッドでは一度に変更できるVariation Axisの数は一つですから、同時に複数のVariation Axisを変更したい場合は、例えば次の具合にチェーン形式に処理する必要がありそうです。

Sample1
let originFontDescriptor: CTFontDescriptor =  // 元となるフォントデスクリプタ
let fontAxes =  // Variation Axisごとに変更値を収めた連想配列
var nextFontDescriptor: CTFontDescriptor = originFontDescriptor

// Axisを更新
for fontAxisKey in fontAxes.keys {
    guard let fontAxis = fontAxes[fontAxisKey] else { continue }

    let axisIdentifier = NSNumber(value: fontAxis.identifier) as CFNumber
    let axisValue = fontAxis.customValue
    nextFontDescriptor = CTFontDescriptorCreateCopyWithVariation(nextFontDescriptor,
                                                                 axisIdentifier,
                                                                 axisValue)
}

if let newFont = NSFont(descriptor: nextFontDescriptor, size: self.fontSize) {
    // NSFontを更新(iOSではUIFont)
    self.textView.font = newFont
}

このようにしてフォントから抽出したVariation Axisのデフォルト値をもとに、適当なパラメータを反映させてから CTFontDescriptorCreateCopyWithVariation で変更をフォントに反映すれば、めでたくmacOS / iOSネイティブアプリケーションでVariable Fontを描画することができます。最終的に画面描画される部分はNSFont (UIFont) を更新する処理となるため、グラフィックスコンテクストをいじったりなど特殊な描画処理を書く必要はありません。
(ただ、フォントに都度変更を加える処理がどれだけパフォーマンスに影響を与えるのかまでは深く検証できていないため、場合によってはグラフィックスの描画処理を見直す必要があるかもしれません。)

少し概念が特殊ではあるのですが、それほど難しいものでもないため、案外簡単にネイティブアプリケーションでVariable Fontを実現できてしまいます。使いどころはそう多くないとは思いますけれども、何かユースケースがあれば検討してみてください。そのためだけにWebViewを使うみたいな方法を取らずに済むでしょう。

参考資料

usagimaru
ソフトウェアデザイン
https://interactionmania.com
goodpatch
Goodpatch(株式会社グッドパッチ)は東京、ベルリン、台北にあるWeb / iOS / AndroidなどマルチデバイスアプリケーションのUIデザイン会社です。サービスやプロダクトの企画設計から関わりコンセプトメイキング、UX設計、プロトタイピング、UIデザイン、実装までワンストップで提供しています。
https://goodpatch.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした