Google絵文字フォントポリシーのサポート

GoogleはAndroidを導入しました:Android絵文字ポリシーは、アプリ開発者にUnicode絵文字の最新バージョンをサポートすることを強制します。このポリシーでは、次のように述べています:

サードパーティのライブラリによって提供されるものを含め、カスタム絵文字実装を持つアプリは、新しいUnicode絵文字がリリースされてから4ヶ月以内にAndroid 12+上で実行する場合、最新のUnicodeバージョンを完全にサポートする必要があります。

このガイドでは、絵文字フォントをバンドルするか、Androidを使用することによって、このポリシーをサポートする方法を示します:Googleダウンロード可能フォント

絵文字フォントのバンドル VS Googleダウンロード可能フォント

最新の絵文字をサポートするために、両方の方法にはいくつかの利点と欠点があります。ここでは、2つの方法のメリットとデメリットをご紹介します:

バンドルフォントの利点

  • フォントの読み込みが速い
  • インターネットがない環境でも利用可能
  • すべてのオペレーティングシステムで動作
  • 独立(Qt以外の依存関係がない)
  • よりシンプルなソリューション

フォントをバンドルするデメリット

  • アプリケーションのサイズが大きくなる(NotoColorEmojiは10MB程度)
  • 新しいリリースでフォントを更新する必要がある。
  • 古いアプリでは絵文字が自動的に更新されない

Googleダウンロードフォントの利点

  • アプリケーションのサイズを変更しない
  • 自動更新
  • 関係のない複数のアプリが同じフォントを共有する

Googleダウンロードフォントのデメリット

  • Googleモバイルサービスに依存
  • アンドロイドのみ
  • キャッシュされていないフォントはダウンロードされる
  • 事前にキャッシュされていない場合、インターネットなしでは動作しない
  • フォントをバンドルするよりも複雑

フォントをバンドルする方法

フォントを入手してバンドルし、後でQMLまたはC++を使って読み込む必要があります。

フォントの入手

このガイドでは、GoogleNotoColorEmojiフォントを使用します。NotoColorEmojiはSIL OPEN FONT LICENSEによってライセンスされたフォントです。

注意: リポジトリからダウンロードする場合は、NotoColorEmoji.ttfの代わりにNotoColorEmoji_WindowsCompatible.ttfフォントをダウンロードしてください。NotoColorEmoji.ttf は異なるフォーマットで内部的にビルドされており、Android/Chrome/Chromium OS でのみサポートされています。Qtは他のプラットフォームでも動作するため、Qtのフォントローダーは標準的な形式のTrueType/OpenTypeフォントを必要とします。

フォントの追加

フォントをバンドルする適切な方法は、Qt Resource Systemファイルに追加することです。例えば、NotoColorEmoji_WindowsCompatible.ttfを含むフォント用のリソースファイル "font.qrc "を作成します。新しいリソースファイルを埋め込むには、CMakeLists.txt に以下のコードを記述します:

qt_add_big_resources(PROJECT_SOURCES font.qrc)

バンドルされているフォントを C++ で読み込む

C++ を使っ て フ ォ ン ト を読み込むには、QFontDatabase を使います。

// Loading NotoColorEmoji bundled using C++ QFontDatabase
QFontDatabase::addApplicationFont(QStringLiteral(":/NotoColorEmoji_WindowsCompatible.ttf"));

注: 上記のコードは、QQmlApplicationEngine が QML をロードする前に使用する必要があります。そうすれば、QML がロードされたときにフォントはすでに存在し、使用する準備ができています。

バンドルされているフォントを QML で読み込む

QML でフォントを読み込むには、FontLoader を使います:

// Loading NotoColorEmoji using QML FontLoader
FontLoader {
   source:"NotoColorEmoji_WindowsCompatible.ttf"
}

Googleダウンロードフォントを使う

絵文字フォントにGoogleダウンロードフォントを使用することで、アプリケーションのサイズを大きくすることなく、自動的に絵文字フォントを更新することができます。ダウンロード可能なフォント機能を使ってフォントをダウンロードする手順は、Androidで詳しく見ることができます:ダウンロード可能なフォントのプロセス

このガイドでは、以下のプロセスを説明します:

  1. C++コードの開始
  2. C++がJava関数を呼び出す
  3. JavaがGDFを呼び出してフォントを取得
  4. JavaがフォントURIを開く
  5. Javaがファイル記述子をC++に返す
  6. C++がQFontDatabase

設定

Google Downloadable FontsはAPIレベル26(Android 8.0)で利用可能です。しかし、アプリがAndroidXを使用している場合、API 14以前のAPIに対応することも可能です。

注: Androidのドキュメントでは、AndroidX.Support Libraryではなく、Android.SupportLibraryを参照しています注:Androidのドキュメントでは、AndroidXではなくAndroid:Support Libraryを参照しています。しかし、サポート・ライブラリのメンテナンスは終了しており、AndroidXに取って代わられているため、Googleの推奨に従ってAndroidXを使用することにしました。

Androidパッケージ・テンプレートのカスタマイズ

まず、Androidパッケージのテンプレートをカスタマイズする必要があります。Qt CreatorのProjectsタブで、Build Settingsの "Build Android APK "を検索してください。Build Steps "の中にあるはずなので、詳細を展開すると "Create Templates "というボタンが表示されます。

"Create Templates"

Create templates "をクリックし、ウィザードに従うと、Android用の設定ファイルがいくつか入ったフォルダが作成される。デフォルトでは、プロジェクト・ディレクトリ内のandroid というフォルダになります。

qmakeを使ってandroidテンプレートをカスタマイズする方法については、Android Package Templatesを参照してください。

このガイドのようにCMakeとQt 6を使用している場合は、QT_ANDROID_PACKAGE_SOURCE_DIRプロパティを設定する必要があります。例

set_property(TARGET emojiremotefont PROPERTY
         QT_ANDROID_PACKAGE_SOURCE_DIR
         ${CMAKE_CURRENT_SOURCE_DIR}/android)

AndroidXの追加

AndroidXを追加するには、上で追加したQT_ANDROID_PACKAGE_SOURCE_DIRフォルダ内のbuild.gradle ファイルを開き、そこに依存関係を追加します:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
    implementation 'androidx.appcompat:appcompat:1.4.1'
}

AndroidXを使用するには、フラグを設定する必要があります。そのために、QT_ANDROID_PACKAGE_SOURCE_DIR内にgradle.properties という名前のファイルを作成し、次の行を追加します:

android.useAndroidX=true

フォント・プロバイダ証明書の追加

AndroidXを使うので、もう1つ必要な設定があります:フォント・プロバイダ証明書を追加します。GMSフォント・プロバイダを使用するには、Android.GMSフォント・プロバイダ証明書をダウンロードしてください:GMSフォント・プロバイダ証明書をダウンロードしてください。他のフォントプロバイダーを使用する場合は、プロバイダー自身から証明書を取得する必要があります。

ファイルをダウンロードしたら、android templatesフォルダ内のvalues フォルダにコピーして、Android Resources(Qtリソース・システムではない)に追加します。以下の画像は(1)の正しいフォルダを示しています:

"Android Templates Folder"

Javaコード

さて、コードを掘り下げましょう!

Java/KotlinコードをAndroidテンプレートに追加する必要があります。アンドロイド・テンプレート・フォルダーのsrc フォルダーの下に置いてください。src フォルダーと java ファイル用のフォルダー構造を作成する必要があるかもしれません。このフォルダ構造は、前節の(2)のAndroid Templates Folderの画像で確認できます。

C++でフォントを取得するには、Javaのコードで以下のことを行う必要があります:

  • フォント要求を作成する
  • フ ォ ン ト 要求を使っ て FontsContractCompat か ら フ ォ ン ト を取得。
  • フ ォ ン ト 情報 と フ ォ ン ト URI (内容ス キーマ フ ァ イ ル) を取得。
  • URI を開いてファイル記述子を取得する。
  • ファイル記述子をC++コードに返す

フ ォ ン ト 要求を作成す る には、 フ ォ ン ト プ ロ バ イ ダー情報 (権限、 パ ッ ケージ、 証明書) と フ ォ ン ト の検索 ク エ リ が必要です。証明書については、前に Android リソースに追加した GMS Font Provider Certificates ファイルfonts_cert.xml を使います。

// GMS fonts provider data
private static final String PROVIDER_AUTHORITY = "com.google.android.gms.fonts";
private static final String PROVIDER_PACKAGE = "com.google.android.gms";

// Emoji font search query (copied from EmojiCompat source)
private static final String EMOJI_QUERY = "emojicompat-emoji-font";

// Font Certificates resources strings (from fonts_certs.xml)
private static final String FONT_CERTIFICATE_ID = "com_google_android_gms_fonts_certs";
private static final String FONT_CERTIFICATE_TYPE = "array";

(...)

// obtain id for the font_certs.xml
int certificateId = context.getResources().getIdentifier(
                              FONT_CERTIFICATE_ID,
                              FONT_CERTIFICATE_TYPE,
                              context.getPackageName());

// creating the request
FontRequest request = new FontRequest(
                              PROVIDER_AUTHORITY,
                              PROVIDER_PACKAGE,
                              EMOJI_QUERY,
                              certificateId);

さて、先ほど作成したリクエストを使ってフォントを取得します:

// fetch the font
FontsContractCompat.FontFamilyResult result =
     FontsContractCompat.fetchFonts(context, null, request);

FontInfo と URI を取得:

final FontsContractCompat.FontInfo[] fontInfos = result.getFonts();
final Uri emojiFontUri = fontInfos[0].getUri();

FontInfo と URI を取得 : URI か ら 新規ネ イ テ ィ ブ フ ァ イ ル記述子を開 く :

final ContentResolver resolver = context.getContentResolver();
// in this case the Font URI is always a content scheme file, made
// so the app requesting it has permissions to open
final ParcelFileDescriptor fileDescriptor =
            resolver.openFileDescriptor(fontInfos[0].getUri(), "r");

// the detachFd will return a native file descriptor that we must close
// later in C++ code
int fd = fileDescriptor.detachFd();

// return fd to C++

注: Javaでコーディングされたものはすべて、JNIを使ってC++で行うことができます。このガイドで紹介するコードは簡略化されています。本番用のコードでは、例外のキャッチなどをチェックする必要があります。

C++コード

OK、Java側ではすべて終わりました。C++側に行きましょう。

C++はJavaコードを呼び出し、ファイル記述子を使用してフォントをQtにロードする役割を担います。

Qt 6におけるC++とJava間の通信がどのように行われるかをより深く理解するには、Qt Android Notifierのサンプルをご覧ください。

Java コードからファイル記述子を取得した後、ファイル記述子をQFile クラスにラップし、QFontDatabase を使ってフォントファイルを読み込みます:

QFile file;
file.open(fd, QFile::OpenModeFlag::ReadOnly, QFile::FileHandleFlag::AutoCloseHandle);

QFontDatabase::addApplicationFontFromData(file->readAll());

2024The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。