Qtプログラミング:QFont::insertSubstitution()でフォント置換をマスターする方法

2025-06-06

void QFont::insertSubstitution(const QString &familyName, const QString &substituteName)とは

QtのQFontクラスは、テキストを描画するために使用されるフォントを指定します。システム上に特定のフォントが見つからない場合や、特定の文字がそのフォントに含まれていない場合に備えて、フォントの置換(substitution)を設定する機能があります。

insertSubstitution()は、あるフォントファミリーの代わりに別のフォントファミリーを使用するようにQtに指示するための静的メソッドです。

具体的には、以下の引数を取ります。

  • const QString &substituteName: familyNameの代わりに使われるフォントファミリー名(例: "IPA Mincho")。
  • const QString &familyName: 置換対象となるフォントファミリー名(例: "Times New Roman")。

このメソッドはstaticなので、QFontクラスのインスタンスを作成せずに直接呼び出すことができます。

// 例: "MyCustomFont" が見つからない場合、代わりに "Arial" を使用するように設定
QFont::insertSubstitution("MyCustomFont", "Arial");

なぜこれが必要なのか?

このような場合、Qtは自動的に利用可能な最も近いフォントを探して代替しようとしますが、insertSubstitution()を使うことで、開発者が意図する代替フォントを明示的に指定することができます。これにより、フォントが見つからない場合の表示の信頼性と一貫性を高めることができます。

例えば、あなたのアプリケーションが「オリジナルのフォント」という名前のフォントを使用しているとします。しかし、このフォントがユーザーのシステムにインストールされていない場合に、代わりに「代替フォントA」を使ってほしい、さらに「代替フォントA」もなければ「代替フォントB」を使ってほしい、といった優先順位をつけたいとします。

insertSubstitution() を使って設定すると、Qtは指定されたフォントファミリー(familyName)を探し、もし見つからなければ、設定されたsubstituteNameのフォントを使用しようとします。

より複雑な置換を設定したい場合は、void QFont::insertSubstitutions(const QString &familyName, const QStringList &substituteNames)という、複数の代替フォントをリストで指定できるオーバーロードも利用できます。



QFont::insertSubstitution()はフォントの置換ルールを設定する便利な機能ですが、期待通りに動作しない場合や、意図しない挙動を示す場合があります。以下に、よくある問題とそのトラブルシューティングのポイントを挙げます。

置換が適用されない(フォントが期待通りに表示されない)

原因

  • 文字(グリフ)の欠落
    指定されたフォントや代替フォントにも、表示したい特定の文字(グリフ)が含まれていない場合があります。この場合、Qtは自動的に別のフォントを探そうとしますが、それが意図しないフォントになることがあります。
  • フォントファイルが存在しない/アクセスできない
    substituteNameで指定したフォントがシステムにインストールされていない、またはアプリケーションがそのフォントファイルにアクセスできない場合、置換は成功しません。
  • Qtのフォント解決メカニズムの優先順位
    Qtには独自の複雑なフォント解決メカニズムがあります。insertSubstitution()は一つのヒントに過ぎず、他の要因(例: QFont::setStyleHint()QApplication::setFont()、Qtスタイルシート、システムのフォント設定)が優先される場合があります。
  • 置換ルールの適用タイミング
    insertSubstitution()はアプリケーションのライフサイクルの早い段階(通常はQApplicationが作成された直後、またはUIが構築される前)に呼び出す必要があります。フォントがすでにロードされて表示されているウィジェットに対して後から置換ルールを設定しても、その変更がすぐに反映されないことがあります。
  • フォント名が正しくない
    familyName または substituteName に指定したフォント名が、システムにインストールされているフォントの正式な名前と一致していない可能性があります。フォントの表示名と内部名が異なることがあります。

トラブルシューティング

  • スタイルシートとの競合
    Qtスタイルシート(QWidget::setStyleSheet())でフォントが設定されている場合、insertSubstitution()を含むC++コードでのフォント設定よりも優先されることがあります。スタイルシートでフォントを設定している場合は、そちらの記述も確認してください。
  • フォントファイルが存在するか確認
    substituteNameに指定したフォントが実際にシステムにインストールされていることを確認します。アプリケーションにバンドルするカスタムフォントの場合は、QFontDatabase::addApplicationFont()で正しくロードされていることを確認してください。
  • QFont::substitute()とQFont::substitutes()の利用
    • QFont::substitute("familyName")を呼び出すことで、設定した置換ルールに基づいて、そのフォントファミリーの最初の代替フォント名(または代替がなければ元のフォント名)を取得できます。
    • QFont::substitutes("familyName")で、設定されている代替フォントのリスト全体を取得できます。
    • これにより、insertSubstitution()が正しく登録されているかを確認できます。
  • QFontInfoの使用
    • 実際に使用されているフォントの情報を取得するためにQFontInfoを使用します。これにより、Qtがどのフォントを最終的に選択したかを確認できます。
    • 例:
      QFont myFont("MyCustomFont"); // "MyCustomFont" を要求
      QFontInfo fontInfo(myFont);
      qDebug() << "Requested font family:" << myFont.family();
      qDebug() << "Actual font family used:" << fontInfo.family();
      qDebug() << "Is exact match:" << fontInfo.exactMatch();
      
  • フォント名の確認
    • システムにインストールされているフォントの正確な「ファミリー名」を確認してください。Windowsであればコントロールパネルのフォント設定、macOSであればFont Book、Linuxであればfc-listなどのコマンドで確認できます。
    • フォント名の後に[ foundry ]のような情報が付いている場合もありますが、通常はファミリー名だけで十分です。

大文字・小文字の区別

原因

  • Qtのフォントシステムは通常、フォントファミリー名の大文字・小文字を区別しませんが、システムによっては挙動が異なる場合があります。

トラブルシューティング

  • フォント名を正確に、システムに登録されている通りの大文字・小文字で指定することが最も安全です。

複数の置換ルールと優先順位

原因

  • 同じfamilyNameに対して複数のinsertSubstitution()を呼び出した場合、最後に呼び出されたものが有効になる可能性があります。

トラブルシューティング

  • 複数の代替フォントを順に試したい場合は、void QFont::insertSubstitutions(const QString &familyName, const QStringList &substituteNames)を使用することを検討してください。これは、指定された順序で代替フォントを試行します。

アプリケーションのパフォーマンスへの影響

insertSubstitution()自体がパフォーマンスに大きな影響を与えることは通常ありませんが、あまりにも多くの置換ルールを設定したり、存在しないフォント名を頻繁に参照したりすると、フォント解決プロセスにわずかなオーバーヘッドが生じる可能性があります。これは通常、ごく稀なケースです。

クロスプラットフォームでの挙動の違い

原因

  • フォントのインストール状況やフォントレンダリングエンジンはOSによって異なります。あるOSで意図通りに動作しても、別のOSで同じフォントが利用できない場合や、代替の挙動が異なる場合があります。
  • 主要なターゲットプラットフォームで十分にテストを行うことが重要です。


QFont::insertSubstitution()は、アプリケーション起動時の早い段階で設定することが一般的です。これにより、アプリケーション全体でフォントの置換ルールが適用されます。

例1: 単一のフォント置換

この例では、「MyCompanyFont」というカスタムフォントがユーザーのシステムにインストールされていない場合に、代わりに「Arial」フォントを使用するように設定します。

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QFontInfo> // 実際に使用されたフォント情報を確認するために使用

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // --- ここが重要: UI要素が作成される前に置換を設定 ---
    // "MyCompanyFont" が見つからない場合、代わりに "Arial" を使用する
    QFont::insertSubstitution("MyCompanyFont", "Arial");

    // "JapaneseCompanyFont" が見つからない場合、代わりに "IPA Gothic" を使用する
    // IPA Gothicは、多くのLinuxディストリビューションやWindowsの標準フォントとして利用可能な日本語フォントです。
    // macOSでは"Hiragino Kaku Gothic ProN"などが代替候補になりえます。
    QFont::insertSubstitution("JapaneseCompanyFont", "IPA Gothic");

    // --- UIの作成 ---
    QLabel label1("Hello World with MyCompanyFont!");
    QFont font1("MyCompanyFont", 24); // 要求するフォント
    label1.setFont(font1);
    label1.show();

    // 実際に使用されたフォント情報を取得してデバッグ出力
    QFontInfo fontInfo1(label1.font());
    qDebug() << "Label 1 - Requested font:" << font1.family();
    qDebug() << "Label 1 - Actual font used:" << fontInfo1.family();
    qDebug() << "Label 1 - Exact match:" << fontInfo1.exactMatch();

    QLabel label2("日本語のテキスト表示(JapaneseCompanyFontを使用)");
    QFont font2("JapaneseCompanyFont", 20); // 要求する日本語フォント
    label2.setFont(font2);
    label2.show();

    QFontInfo fontInfo2(label2.font());
    qDebug() << "Label 2 - Requested font:" << font2.family();
    qDebug() << "Label 2 - Actual font used:" << fontInfo2.family();
    qDebug() << "Label 2 - Exact match:" << fontInfo2.exactMatch();

    return app.exec();
}

解説

  1. #include <QFontInfo>: これは、QFontが実際にシステム上でどのフォントに解決されたかを確認するために使います。トラブルシューティングに非常に役立ちます。
  2. QApplication app(argc, argv);: Qtアプリケーションを初期化します。QFont関連の操作を行う前に、QApplicationインスタンスが存在する必要があります。
  3. QFont::insertSubstitution("MyCompanyFont", "Arial");: この行がフォント置換ルールを設定しています。
    • もしシステムに「MyCompanyFont」という名前のフォントがインストールされていれば、それが使われます。
    • インストールされていなければ、Qtは「Arial」を探して使用します。
  4. QLabelsetFont(): QLabelウィジェットを作成し、setFont()でフォントを設定しています。この時点で、Qtのフォント解決メカニズムが働き、設定された置換ルールが考慮されます。

例2: 複数の代替フォントを指定する insertSubstitutions()

insertSubstitutions()のオーバーロードを使用すると、一つのフォントファミリーに対して複数の代替フォントを優先順位をつけて指定できます。

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QFontInfo>
#include <QStringList> // 複数の代替フォント名を格納するために必要

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // --- 複数の代替フォントを設定 ---
    QStringList preferredJapaneseFonts;
    preferredJapaneseFonts << "BIZ UDGothic" << "IPA Gothic" << "Yu Gothic" << "Meiryo" << "Noto Sans CJK JP";

    // 「MySpecialJapaneseFont」が見つからない場合、上記リストの順序で代替を試す
    QFont::insertSubstitutions("MySpecialJapaneseFont", preferredJapaneseFonts);

    // 「AwesomeCustomFont」が見つからない場合、"Verdana"、なければ "Liberation Sans" を試す
    QStringList customFontAlternatives;
    customFontAlternatives << "Verdana" << "Liberation Sans";
    QFont::insertSubstitutions("AwesomeCustomFont", customFontAlternatives);


    // --- UIの作成 ---
    QLabel label3("こちらは特別日本語フォントのテストです。");
    QFont font3("MySpecialJapaneseFont", 22);
    label3.setFont(font3);
    label3.show();

    QFontInfo fontInfo3(label3.font());
    qDebug() << "Label 3 - Requested font:" << font3.family();
    qDebug() << "Label 3 - Actual font used:" << fontInfo3.family();
    qDebug() << "Label 3 - Exact match:" << fontInfo3.exactMatch();
    qDebug() << "Label 3 - Substitutes for 'MySpecialJapaneseFont':" << QFont::substitutes("MySpecialJapaneseFont");

    QLabel label4("Awesome Custom Font Text!");
    QFont font4("AwesomeCustomFont", 18);
    label4.setFont(font4);
    label4.show();

    QFontInfo fontInfo4(label4.font());
    qDebug() << "Label 4 - Requested font:" << font4.family();
    qDebug() << "Label 4 - Actual font used:" << fontInfo4.family();
    qDebug() << "Label 4 - Exact match:" << fontInfo4.exactMatch();
    qDebug() << "Label 4 - Substitutes for 'AwesomeCustomFont':" << QFont::substitutes("AwesomeCustomFont");


    return app.exec();
}
  1. QStringList: QStringListを使って、代替フォント名をリストとして作成します。
  2. QFont::substitutes("familyName"): 設定されている代替フォントのリストをQStringListとして取得できる静的メソッドです。設定が正しく行われたかを確認するのに便利です。


QFont::insertSubstitution()の代替方法

QFontDatabase::addApplicationFont() / addApplicationFontFromData() を使用してアプリケーションにフォントをバンドルする

これは、特定のフォントがユーザーのシステムにインストールされていることを期待できない場合に、最も堅牢な方法の一つです。アプリケーションの実行可能ファイル自体にフォントファイルを組み込み、実行時にロードします。

特徴

  • 用途
    カスタムフォント、特殊なアイコンフォント(例: Font Awesome)、または特定のデザイン要件がある場合。
  • デメリット
    アプリケーションのサイズが大きくなります。フォントライセンスに注意が必要です。
  • メリット
    ユーザーのシステム環境に依存せず、必要なフォントが常に利用可能であることを保証できます。クロスプラットフォームでのフォントの一貫性を高めるのに非常に有効です。

コード例

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QFontDatabase> // 必須

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // アプリケーションにフォントをロード (リソースファイルから)
    // 例えば、プロジェクトの.proファイルに 'RESOURCES += fonts.qrc' を追加し、
    // fonts.qrcファイルに <file>fonts/MyCustomFont.ttf</file> を含める
    int fontId = QFontDatabase::addApplicationFont(":/fonts/MyCustomFont.ttf");
    QStringList fontFamilies = QFontDatabase::applicationFontFamilies(fontId);

    // ロードしたフォントファミリー名を取得
    QString customFontFamily;
    if (!fontFamilies.isEmpty()) {
        customFontFamily = fontFamilies.at(0); // 最初のフォントファミリー名を取得
        qDebug() << "Custom font loaded:" << customFontFamily;
    } else {
        qWarning() << "Failed to load custom font.";
        customFontFamily = "Arial"; // フォントロード失敗時のフォールバック
    }

    // ロードしたフォントを使用
    QLabel label("Hello from Custom Font!");
    QFont font(customFontFamily, 24);
    label.setFont(font);
    label.show();

    // 別のラベルでシステムフォントを使用(比較用)
    QLabel label2("Hello from System Font!");
    QFont font2("Times New Roman", 24);
    label2.setFont(font2);
    label2.move(0, 50); // 位置をずらす
    label2.show();

    return app.exec();
}

QApplication::setFont() を使用してアプリケーション全体のデフォルトフォントを設定する

これは、アプリケーション全体で使用されるデフォルトのフォントを設定する最も簡単な方法です。個々のウィジェットでフォントが明示的に設定されていない限り、この設定が適用されます。

特徴

  • 用途
    アプリケーション全体に特定のルック&フィールを与えたい場合。
  • デメリット
    個々のウィジェットのフォントをオーバーライドすることはできません。置換ルールとは異なり、指定したフォントが見つからなければ、システムが自動的に代替フォントを探します(特定の代替フォントを指定する機能はありません)。
  • メリット
    アプリケーション全体のフォントを統一的に変更できます。

コード例

#include <QApplication>
#include <QLabel>
#include <QFont>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // アプリケーション全体のデフォルトフォントを設定
    // "Roboto" が存在しない場合、システムが代替を探します。
    // その際、QFont::insertSubstitution() で設定されたルールが考慮される可能性があります。
    QFont defaultAppFont("Roboto", 12);
    app.setFont(defaultAppFont);

    QLabel label1("This text uses the application's default font.");
    // label1.setFont()を呼び出さないので、app.setFont()が適用される
    label1.show();

    QLabel label2("This text has its own explicit font.");
    QFont specificFont("Times New Roman", 14);
    label2.setFont(specificFont); // これはアプリケーションのデフォルト設定を上書きする
    label2.move(0, 50);
    label2.show();

    return app.exec();
}

Qt スタイルシート (QWidget::setStyleSheet()) を使用する

CSSのような構文で、ウィジェットのスタイル(フォント、色、背景など)を柔軟に設定できます。フォントファミリーも指定可能です。

特徴

  • 用途
    特定のウィジェットやアプリケーション全体にテーマを適用する場合。
  • デメリット
    パフォーマンスにわずかなオーバーヘッドがある場合があります。フォントが見つからない場合の代替フォントの指定は、CSSのフォールバックリスト(例: font-family: "My Font", Arial, sans-serif;)に依存します。
  • メリット
    UIの見た目をC++コードから分離し、デザイナーでも扱いやすくなります。柔軟なスタイル設定が可能です。

コード例

#include <QApplication>
#include <QPushButton>
#include <QFont>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QPushButton button1("Button with specific font via Style Sheet");
    // スタイルシートでフォントを設定
    // "MyWebFont" が見つからなければ "Georgia"、それもなければOSのデフォルトserifフォント
    button1.setStyleSheet("font-family: 'MyWebFont', Georgia, serif; font-size: 16px;");
    button1.show();

    QPushButton button2("Button with default font");
    button2.move(0, 50);
    button2.show();

    return app.exec();
}

QFont::setStyleHint() / QFont::setWeight() など、フォントのヒントや属性を利用する

特徴

  • 用途
    コードエディタで等幅フォントを使いたいが、特定のフォントに限定したくない場合など。
  • デメリット
    具体的なフォント名を指定するわけではないため、選択されるフォントはシステムに依存します。
  • メリット
    特定のフォント名に依存せず、フォントの種類(例: 等幅フォントなら何でも良い)を指定したい場合に有効です。クロスプラットフォームで似たようなフォントを選択させやすくなります。
#include <QApplication>
#include <QTextEdit>
#include <QFont>
#include <QFontInfo>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QTextEdit textEdit;
    QFont font("Any Monospace Font", 12);
    // 等幅フォントであることをヒントとしてQtに伝える
    // これにより、Qtはシステム上の等幅フォントの中から最適なものを選択しようとする
    font.setStyleHint(QFont::Monospace);
    textEdit.setFont(font);
    textEdit.setText("This is some code:\n"
                     "int main() {\n"
                     "    return 0;\n"
                     "}");
    textEdit.show();

    QFontInfo fontInfo(textEdit.font());
    qDebug() << "TextEdit font family (requested):" << font.family();
    qDebug() << "TextEdit font family (actual):" << fontInfo.family();
    qDebug() << "TextEdit font is monospace:" << fontInfo.fixedPitch();

    return app.exec();
}
  • 特定のフォント名にこだわらず、フォントの一般的な種類(等幅など)を指定したい
    QFont::setStyleHint()
  • UIのスタイルとしてフォントを定義したい、CSSのように記述したい
    Qtスタイルシート
  • アプリケーション全体のデフォルトフォントを一括で設定したい
    QApplication::setFont()
  • フォントが見つからない場合に、特定の代替フォントを指定したい
    QFont::insertSubstitution()
  • 特定のフォントを必ず使いたい、ユーザー環境に依存したくない
    QFontDatabase::addApplicationFont()(フォントのバンドル)