Qt QFont::kerning()徹底解説:文字詰め(カーニング)で美しいテキスト表示を実現

2025-06-06

QFont::kerning() とは

QFont::kerning() は、Qt の QFont クラスのメソッドの一つで、このフォントでテキストを描画する際にカーニング(文字詰め)を適用するかどうかを返します。

カーニングとは何か?

まず、「カーニング」とは何かを理解することが重要です。

タイポグラフィ(活字デザイン)において、カーニング (kerning) とは、プロポーショナルフォントにおいて、隣り合う特定の文字の組み合わせ(例:「AV」や「To」)によって文字間隔を調整し、見た目のバランスを整える処理のことです。

通常、文字はそれぞれが持つ幅に基づいて等間隔に配置されますが、これだと文字の形によっては、隣り合う文字との間に不自然な空白ができてしまうことがあります。例えば、「A」と「V」のように斜めの線を持つ文字が並ぶ場合、そのままでは間に隙間ができてしまい、読みにくくなることがあります。

カーニングは、このような特定の文字の組み合わせに対して、手動または自動で文字間隔を狭めたり広げたりすることで、文字の並び全体がより自然で読みやすく、視覚的に美しいものになるように調整する技術です。

QFont::kerning() の役割

QFont::kerning() メソッドは、このカーニング機能が現在の QFont オブジェクトで有効になっているかどうかを bool 型で返します。

  • false を返す場合
    カーニングは適用されず、文字はそれぞれのデフォルトの幅に基づいて配置されます。
  • true を返す場合
    そのフォントでテキストを描画する際に、フォントが持つカーニング情報に基づいて文字間隔が調整されます。

この設定は、QFont::setKerning(bool enable) メソッドを使って変更することができます。

カーニングは、特に見出しやロゴなど、大きなサイズのテキストでその効果が顕著に現れます。適切にカーニングされたテキストは、視覚的に統一感があり、プロフェッショナルな印象を与えます。



QFont::kerning()は、テキストの見た目を美しくするために重要な機能ですが、期待通りに機能しない場合があります。ここでは、よくある問題とその解決策について説明します。

カーニングが全く適用されない

考えられる原因とトラブルシューティング

  • レンダリング環境の問題(特にQPainter/QPrinter使用時)
    QPainterを使用してPDFなどの印刷デバイスに描画する場合、画面表示とは異なるレンダリングエンジンが使用されることがあり、カーニングが期待通りに適用されないことがあります。

    • 確認
      画面上の QLabelQTextEdit などで表示した場合と、QPrinter を使用してPDFに出力した場合で挙動が異なるか確認します。
    • 解決策
      QPrinter の設定(例えば、解像度やフォントの埋め込み設定)を確認します。場合によっては、PDF出力のためのフォント設定を別途調整する必要があるかもしれません。
  • フォントサイズが極端に小さい/大きい
    非常に小さいフォントサイズでは、カーニングの効果が視覚的に認識しにくい場合があります。また、極端に大きなフォントサイズでは、レンダリングの問題が発生することがあります。

    • 確認
      テキストのフォントサイズを中程度(例:12pt~24pt)に設定して試してみてください。
    • 解決策
      フォントサイズを調整し、適切なレンダリングが行われるか確認します。
  • QtがOpenType GPOSベースのカーニングを完全にサポートしていない場合がある
    Qtのバージョンやプラットフォームによっては、OpenTypeフォントの高度なカーニング機能(GPOSテーブルによるもの)が完全にサポートされていない場合があります。この場合、フォントにカーニング情報があっても、Qtがそれを利用できないことがあります。

    • 確認
      Qtの公式ドキュメントやフォーラムで、使用しているQtのバージョンとプラットフォームにおけるフォントレンダリングの制限について調べてみてください。
    • 解決策
      特定のフォントやプラットフォームで問題が解決しない場合、Qtのバージョンアップを検討したり、よりシンプルでカーニングに依存しないフォントを使用したりする必要があるかもしれません。
  • 使用しているフォントがカーニング情報を持っていない
    すべてのフォントがカーニング情報を持っているわけではありません。特に古いフォントや一部のフリーフォントでは、カーニング情報が埋め込まれていないことがあります。

    • 確認
      別のフォント(例えば、OSに標準で含まれるArialやTimes New Romanなどの一般的なフォント)で試してみて、カーニングが適用されるか確認します。
    • 解決策
      カーニング情報を内蔵している高品質なフォントを使用することを検討してください。
  • QFont::setKerning(false) が設定されている
    QFont::setKerning(bool enable) メソッドで明示的にfalseが設定されている場合、カーニングは無効になります。

    • 確認
      コードで font.setKerning(false); のような記述がないか確認してください。
    • 解決策
      カーニングを有効にするには、font.setKerning(true); を呼び出します。デフォルトはtrueなので、特に設定していなければこの問題は発生しにくいです。

カーニングが意図しない形で適用される/見た目が悪い

考えられる原因とトラブルシューティング

  • ピクセル単位での丸め誤差
    特に小さいフォントサイズや、高DPI環境でレンダリングする場合、サブピクセルレンダリングの有無やピクセル単位での丸め誤差によって、カーニングの見た目が意図せず変化することがあります。

    • 確認
      異なるDPI設定やディスプレイで表示を試します。
    • 解決策
      Qtは通常、これらの問題を自動的に処理しますが、場合によってはアプリケーションのDPI設定やアンチエイリアシング設定を調整することで改善されることがあります。
  • QTextLayoutなど、より高度なテキストレイアウトを使用している場合
    QPainter::drawText() のようなシンプルな描画ではなく、QTextLayoutQTextDocument を使用して複雑なテキストレイアウトを行っている場合、それらのレイアウトエンジンの設定がカーニングに影響を与える可能性があります。

    • 確認
      QTextLayoutQTextDocument のドキュメントを確認し、カーニングに関連する設定がないか調べます。
    • 解決策
      レイアウトエンジンの設定を調整するか、必要であれば QFont::setKerning(false) でカーニングを無効にし、手動で文字間隔を調整することを検討します(ただし、これは手間がかかります)。
  • フォントのカーニング情報自体に問題がある
    フォントファイル自体が不適切なカーニング情報を持っている場合があります。これは、フォントデザイナーによる問題や、フォントの破損が原因であることがあります。

    • 確認
      別のアプリケーション(Word, Photoshopなど)で同じフォントを使ってテキストを表示し、カーニングの挙動が同様におかしいか確認します。
    • 解決策
      フォントを再インストールするか、別のバージョンのフォントを探す、またはフォントの提供元に問い合わせることを検討します。

一般的なトラブルシューティングのヒント

  • デバッグ出力の活用
    qDebug() を使用して、設定しているフォントのプロパティ(font.kerning() の戻り値など)をコンソールに出力し、期待通りの値が設定されているか確認します。

  • フォントキャッシュのクリア
    Qtはフォント情報をキャッシュすることがあります。稀に、キャッシュが原因で古い情報が使用されることがあります。

    • 解決策
      アプリケーションを再起動したり、開発環境によってはQtのキャッシュをクリアしたりすることで解決する場合があります。
  • シンプルなコードで再現テスト
    問題が発生しているコードから、QFontQPainterによるテキスト描画の部分だけを抜き出し、最小限のコードでカーニングの挙動を確認します。これにより、問題の範囲を特定しやすくなります。



QFont::kerning() と関連するプログラミング例 (Qt)

QFont::kerning() は主に、設定されているフォントがカーニングを有効にしているかどうかを確認するために使用されます。通常、カーニングを有効にするには QFont::setKerning(true) を使用します。

ここでは、カーニングの有効/無効を切り替えてテキスト描画がどのように変化するかを示す例をいくつか紹介します。

例1:カーニングの有効/無効を切り替えてテキストを描画する基本的な例

この例では、QWidget にテキストを描画し、ボタンを押すことでカーニングのオン/オフを切り替えます。

#include <QtWidgets>

class KerningExampleWidget : public QWidget
{
    Q_OBJECT

public:
    KerningExampleWidget(QWidget *parent = nullptr) : QWidget(parent)
    {
        // レイアウトの設定
        QVBoxLayout *layout = new QVBoxLayout(this);
        toggleButton = new QPushButton("Toggle Kerning", this);
        layout->addWidget(toggleButton);

        // フォントの初期設定
        currentFont = QFont("Times New Roman", 36); // カーニング情報を持つフォントを推奨
        currentFont.setKerning(true); // 初期状態ではカーニングを有効にする
        
        kerningEnabled = true;

        connect(toggleButton, &QPushButton::clicked, this, &KerningExampleWidget::toggleKerning);

        setWindowTitle("QFont Kerning Example");
        resize(400, 200);
    }

protected:
    void paintEvent(QPaintEvent *event) override
    {
        Q_UNUSED(event);

        QPainter painter(this);
        painter.setFont(currentFont);
        painter.setPen(Qt::black);

        // カーニングの状態を表示
        QString kerningStatus = QString("Kerning: %1").arg(currentFont.kerning() ? "Enabled" : "Disabled");
        painter.drawText(20, 50, kerningStatus);

        // カーニングの効果を確認するためのテキスト
        // 「AV」や「To」など、カーニングが効きやすい文字を使うと分かりやすい
        painter.drawText(20, 120, "AVANT GARDE Text");
        painter.drawText(20, 160, "Toaster");
    }

private slots:
    void toggleKerning()
    {
        kerningEnabled = !kerningEnabled;
        currentFont.setKerning(kerningEnabled);
        update(); // 再描画を要求
    }

private:
    QFont currentFont;
    bool kerningEnabled;
    QPushButton *toggleButton;
};

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

    KerningExampleWidget widget;
    widget.show();

    return app.exec();
}

#include "main.moc" // mocファイルをインクルードすることを忘れないでください

このコードのポイント

  • "AVANT GARDE Text""Toaster" のようなテキストは、カーニングが適用されると文字間が詰まって見えるはずです。
  • toggleKerning() スロットでボタンが押されるたびに setKerning() を呼び出して状態を切り替え、update() で再描画を促します。
  • currentFont.kerning() で現在のカーニングの状態(有効/無効)を取得し、表示しています。
  • currentFont.setKerning(true); でカーニングを有効にします。

例2:QLabel でカーニングの影響を見る

QLabel は内部でテキストレンダリングを行うため、明示的に QPainter を使う必要はありません。QLabel に設定するフォントのカーニング設定が反映されます。

#include <QtWidgets>

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

    QWidget window;
    QVBoxLayout *layout = new QVBoxLayout(&window);

    // カーニング有効のQLabel
    QLabel *labelWithKerning = new QLabel("AVANT GARDE Text (Kerning Enabled)", &window);
    QFont fontKerning = QFont("Times New Roman", 24);
    fontKerning.setKerning(true);
    labelWithKerning.setFont(fontKerning);
    layout->addWidget(labelWithKerning);

    // カーニング無効のQLabel
    QLabel *labelWithoutKerning = new QLabel("AVANT GARDE Text (Kerning Disabled)", &window);
    QFont fontNoKerning = QFont("Times New Roman", 24);
    fontNoKerning.setKerning(false);
    labelWithoutKerning.setFont(fontNoKerning);
    layout->addWidget(labelWithoutKerning);

    window.setWindowTitle("QLabel Kerning Example");
    window.resize(500, 200);
    window.show();

    return app.exec();
}

このコードのポイント

  • 同じテキストを表示することで、カーニングの有無による見た目の違いを直接比較できます。
  • 2つの QLabel を作成し、それぞれにカーニングが有効なフォントと無効なフォントを設定しています。

例3:QTextDocument/QTextEdit でカーニングの影響を見る

QTextDocument やそれを使用する QTextEdit もフォントのカーニング設定を尊重します。

#include <QtWidgets>

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

    QTextEdit textEdit;
    
    // カーニング有効なフォントを設定
    QFont fontKerning = QFont("Arial", 28);
    fontKerning.setKerning(true);
    textEdit.setFont(fontKerning);
    
    textEdit.setPlainText("This is AVANT GARDE text with kerning enabled.\n"
                          "Watch how the \"AV\" and \"To\" pairs are handled.\n"
                          "Toaster Oven");

    // カーニングを無効にした状態で別のテキストを追加 (appendで新しい段落)
    // QTextEdit全体にフォント設定が適用されるため、直接比較は難しいですが、
    // QTextCharFormatで部分的にフォントを設定する例として示します。
    // 一般的には、異なる QTextDocument を使うか、別の QTextEdit を使う方が比較しやすいです。
    
    // カーニング無効のテキストを追加する場合の擬似コード (QTextEdit全体に影響するため直接的な比較は困難)
    // QFont fontNoKerning = QFont("Arial", 28);
    // fontNoKerning.setKerning(false);
    // QTextCharFormat formatNoKerning;
    // formatNoKerning.setFont(fontNoKerning);
    // QTextCursor cursor = textEdit.textCursor();
    // cursor.insertText("\n\nThis is AVANT GARDE text with kerning disabled (simulated).\n", formatNoKerning);

    textEdit.setWindowTitle("QTextEdit Kerning Example");
    textEdit.resize(600, 300);
    textEdit.show();

    return app.exec();
}

このコードのポイント

  • QTextDocument はより複雑なテキストレイアウトを扱うため、フォントのカーニング情報も適切に利用します。
  • QTextEdit は、設定された QFont のカーニング設定を適用してテキストをレンダリングします。

重要な注意点

  • フォントサイズ
    小さすぎるフォントサイズでは、カーニングの効果が視覚的にわかりにくいことがあります。ある程度大きなサイズ(例:24pt以上)で試すと、違いがより明確になります。
  • フォントの選択
    カーニングの効果が目に見えるためには、使用するフォントがカーニング情報(OpenTypeのGPOSテーブルなど)を内蔵している必要があります。「Times New Roman」「Arial」「Helvetica」などの一般的なフォントは通常カーニング情報を持っていますが、すべてのフォントがそうであるとは限りません。特に、一部のフリーフォントや非常にシンプルなフォントでは、カーニング情報がない場合があります。

Qt における QFont::kerning() 関連のプログラミング例

QFont::kerning() は、フォントのカーニング設定の状態を取得し、QFont::setKerning() はその設定を変更するために使用されます。以下に、これらのメソッドを使った基本的なプログラミング例を示します。

この例では、QWidget を継承したカスタムウィジェットを作成し、その paintEvent で異なるカーニング設定のテキストを描画します。

ヘッダーファイル (mywidget.h)

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include <QFont>
#include <QPainter>

class MyWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MyWidget(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    QFont defaultFont;
    QFont kernedFont;
    QFont unkernedFont;
};

#endif // MYWIDGET_H

実装ファイル (mywidget.cpp)

#include "mywidget.h"
#include <QDebug> // デバッグ出力用

MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent)
{
    // ウィジェットのサイズを設定
    setFixedSize(400, 250);

    // デフォルトのフォントを設定
    defaultFont.setFamily("Arial"); // 適切なフォントファミリーを指定
    defaultFont.setPointSize(24);
    // QFontのデフォルトではkerningはtrueです。
    qDebug() << "Default font kerning enabled:" << defaultFont.kerning(); // trueが出力されるはず

    // カーニングを有効にしたフォント(デフォルトと同じになるはず)
    kernedFont = defaultFont;
    kernedFont.setKerning(true);
    qDebug() << "Kerned font kerning enabled:" << kernedFont.kerning(); // trueが出力されるはず

    // カーニングを無効にしたフォント
    unkernedFont = defaultFont;
    unkernedFont.setKerning(false);
    qDebug() << "Unkerned font kerning enabled:" << unkernedFont.kerning(); // falseが出力されるはず
}

void MyWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event); // 未使用の引数をマーク

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing); // アンチエイリアシングを有効にする

    // 1. デフォルトのカーニング設定でテキストを描画
    painter.setFont(defaultFont);
    painter.drawText(50, 50, "AVATAR (Default Kerning)");

    // 2. カーニングを明示的に有効にしたテキストを描画
    painter.setFont(kernedFont);
    painter.drawText(50, 100, "AVATAR (Explicitly Kerned)");

    // 3. カーニングを無効にしたテキストを描画
    painter.setFont(unkernedFont);
    painter.drawText(50, 150, "AVATAR (Unkerned)");

    // QFont::kerning() の戻り値を確認するテキスト
    QFont statusFont = defaultFont;
    statusFont.setPointSize(10);
    painter.setFont(statusFont);
    painter.drawText(50, 200, QString("Default Font Kerning: %1").arg(defaultFont.kerning() ? "Enabled" : "Disabled"));
    painter.drawText(50, 215, QString("Explicitly Kerned Font Kerning: %1").arg(kernedFont.kerning() ? "Enabled" : "Disabled"));
    painter.drawText(50, 230, QString("Unkerned Font Kerning: %1").arg(unkernedFont.kerning() ? "Enabled" : "Disabled"));
}

main.cpp ファイル

#include <QApplication>
#include "mywidget.h"

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

    MyWidget w;
    w.setWindowTitle("QFont Kerning Example");
    w.show();

    return a.exec();
}

プロジェクトの設定 (.pro ファイル)

Qt Creatorを使用している場合、新しいQt Widgets Applicationプロジェクトを作成すれば、これらは自動的に設定されます。手動でビルドする場合は、.pro ファイルに以下を追加します。

QT       += widgets

SOURCES += \
    main.cpp \
    mywidget.cpp

HEADERS += \
    mywidget.h

コードの説明

  1. MyWidget クラス
    • defaultFont: QFont オブジェクトを初期化する際に、特にsetKerning()を呼び出していませんが、QFontのデフォルトではカーニングが有効になっています。
    • kernedFont: setKerning(true) を明示的に呼び出し、カーニングを有効にしています。これはデフォルトの挙動と同じになるはずです。
    • unkernedFont: setKerning(false) を呼び出し、カーニングを無効にしています。
    • コンストラクタ内で qDebug() を使って、各フォントの kerning() メソッドの戻り値を出力し、設定が正しく反映されていることを確認しています。
    • paintEvent() メソッド内で、それぞれのフォントを使って同じ文字列 "AVATAR" を描画しています。この文字列は「AV」という文字の組み合わせが含まれており、カーニングの効果が視覚的にわかりやすいです。
    • 最後に、各フォントの kerning() メソッドの現在の状態を示すテキストも描画しています。

このプログラムを実行すると、3つの「AVATAR」というテキストが表示されます。

  • "AVATAR (Unkerned)" は、カーニングが適用されないため、「AV」の間にわずかな不自然な隙間があるように見えるはずです。
  • "AVATAR (Default Kerning)""AVATAR (Explicitly Kerned)" は、通常カーニングが適用され、「AV」の間の隙間が詰まっているように見えます。

注記
カーニングの効果は、使用するフォントやOSのフォントレンダリングエンジンの設定によって大きく異なります。特に、古いフォントやカーニング情報が埋め込まれていないフォントでは、setKerning(true) にしても視覚的な変化が見られない場合があります。この例では「Arial」を使用していますが、他のプロポーショナルフォント(例: Times New Roman, Calibriなど)でも試してみてください。



QFont::kerning() は、フォントに埋め込まれたカーニング情報に基づいて文字間を調整する、最も推奨される標準的な方法です。しかし、これが期待通りに機能しない場合や、より細かな制御が必要な場合に、代替手段を検討することがあります。

主な代替手段は以下の通りです。

  1. QTextLayout クラスの使用 (より高度なテキストレイアウト)
  2. QRawFont と QFontMetricsF を組み合わせた手動計算
  3. QPainter::drawText のフラグ調整 (一部限定的)
  4. 外部ライブラリの利用 (HarfBuzzなど)

それぞれの方法について詳しく見ていきましょう。

QTextLayout クラスの使用 (より高度なテキストレイアウト)

QTextLayout は、より複雑なテキストレイアウトとレンダリングを扱うためのクラスです。QFont::kerning() がオフになっていても、QTextLayout はフォントの他の高度なタイポグラフィ機能(リガチャ、コンテキストに応じた字形置換など)を考慮に入れることができます。

特徴

  • QPainter::drawText() よりも柔軟な制御が可能。
  • カーニングを含む、フォントのOpenType機能にアクセスできる場合がある(ただし、Qtのレンダリングエンジンとフォントによって依存)。
  • テキストのセグメント化、行分割、位置決めなど、高度なレイアウト処理が可能。

使用例

#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QTextLayout>
#include <QFontMetrics>
#include <QDebug>

class TextLayoutWidget : public QWidget
{
public:
    TextLayoutWidget(QWidget *parent = nullptr) : QWidget(parent)
    {
        setFixedSize(500, 300);
    }

protected:
    void paintEvent(QPaintEvent *event) override
    {
        Q_UNUSED(event);
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);

        QFont font("Arial", 30);
        // QFont::kerning() がtrueでもfalseでも、QTextLayoutはフォントの情報をより深く解析する可能性があります
        // 明示的にfalseに設定しても、一部の高度なフォント機能はQTextLayoutによって考慮されることがあります。
        font.setKerning(true); // 通常はtrueが推奨されます
        painter.setFont(font);

        QString text = "AVATAR with QTextLayout";

        // --- QTextLayout を使用した描画 ---
        QTextLayout textLayout(text, font);
        textLayout.beginLayout();
        QTextLine line = textLayout.createLine();
        line.setLineWidth(width()); // ウィジェットの幅に合わせて行幅を設定
        textLayout.endLayout();

        // テキストの描画位置を調整
        qreal x = 50;
        qreal y = 50 + font.pointSize(); // 基準線を調整

        painter.drawText(x, y - textLayout.boundingRect().height() + textLayout.boundingRect().top(), text);
        // QTextLayout の draw() メソッドを使用する方がより正確
        // textLayout.draw(&painter, QPointF(x, y - textLayout.boundingRect().height() + textLayout.boundingRect().top()));


        // --- QPainter::drawText で比較 ---
        painter.setFont(font);
        painter.drawText(50, 150, "AVATAR with QPainter::drawText (for comparison)");

        // カーニングを無効にして比較
        QFont unkernedFont = font;
        unkernedFont.setKerning(false);
        painter.setFont(unkernedFont);
        painter.drawText(50, 200, "AVATAR with QPainter::drawText (Unkerned)");
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TextLayoutWidget widget;
    widget.setWindowTitle("QTextLayout Example");
    widget.show();
    return a.exec();
}

利点

  • OpenTypeの高度な機能(リガチャ、代替字形など)をより活用できる可能性がある。
  • リッチテキスト(HTMLなど)のレンダリングにも対応。
  • より正確なテキストメトリクスとレイアウトを提供。

欠点

  • カーニングの効果は、やはりフォントの品質とQtのフォントレンダリングエンジンに依存する。
  • QPainter::drawText() に比べてコードが複雑になる。

QRawFont と QFontMetricsF を組み合わせた手動計算

これは最も低レベルで、非常に手間のかかる方法です。QRawFont はフォントファイルから直接グリフ情報にアクセスでき、QFontMetricsF はフォントのメトリクス(文字の幅、高さなど)を浮動小数点精度で取得できます。これらを組み合わせることで、各グリフの位置を自分で計算し、描画することができます。

特徴

  • フォントの生のデータにアクセスするため、特定のグリフの正確な幅などを取得できる。
  • フォントに埋め込まれたグリフ間の間隔(カーニングペア、OpenType GPOSなど)を独自に読み込み、適用できる。
  • 究極の制御が可能。

使用例 (概念的な説明)

// これは概念的なコードであり、複雑なカーニング計算を完全にカバーしていません
// 実際のOpenType GPOSテーブル解析は非常に複雑です。
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QRawFont>
#include <QFontDatabase> // フォントパスの取得に役立つ

class ManualKerningWidget : public QWidget
{
public:
    ManualKerningWidget(QWidget *parent = nullptr) : QWidget(parent)
    {
        setFixedSize(500, 200);
        // ここでは簡単な例として、特定のフォントをロードします
        // 実際のアプリケーションでは、QFontDatabaseなどを使ってフォントを探します
        rawFont.setFileName(":/fonts/arial.ttf"); // リソースファイルにフォントを埋め込む場合
        // または、システムフォントから直接取得
        // rawFont = QFontDatabase::addApplicationFont("C:/Windows/Fonts/Arial.ttf"); // Windowsの場合

        // QRawFont::load() が成功したか確認する
        if (!rawFont.isValid()) {
            qWarning() << "Failed to load raw font!";
            return;
        }

        // QRawFont から QFont を作成し、フォントサイズを設定
        QFont font("Arial", 30); // QRawFontのグリフはQPainterで描画する際にQFontが必要
        setFont(font); // 親ウィジェットのフォントを設定

        // デバッグ情報
        qDebug() << "Raw font loaded:" << rawFont.familyName();
    }

protected:
    void paintEvent(QPaintEvent *event) override
    {
        Q_UNUSED(event);
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);

        if (!rawFont.isValid()) {
            painter.drawText(50, 50, "Raw font not loaded!");
            return;
        }

        QString text = "AVATAR";
        qreal currentX = 50;
        qreal currentY = 100;
        QFontMetricsF metrics(font()); // ウィジェットのフォントを使用

        // 各文字を描画し、手動でカーニングを適用 (非常に簡易的な例)
        for (int i = 0; i < text.length(); ++i) {
            QChar ch = text.at(i);
            quint32 glyphIndex = rawFont.glyphIndexesForChars(QList<QChar>() << ch).value(0);

            // QRawFontで直接グリフを描画 (QPainter::drawGlyph を使用)
            // drawGlyphはQRawFontで提供されているが、QPainter::drawText(QPointF, QRawFont, glyphIndex) のようなオーバーロードはない
            // そのため、実際には QPainter::drawText(QPointF, QString) を使うか、
            // 各グリフをQPainterPathに変換して描画する必要がある。
            // ここでは簡易的にQFontMetricsFで幅を取得し、QPainter::drawTextで描画する。

            painter.drawText(currentX, currentY, QString(ch));

            qreal advance = metrics.horizontalAdvance(ch);

            // 非常に簡易的な手動カーニングの例
            // 例えば 'A' の後に 'V' が続く場合、少し詰める
            if (i < text.length() - 1) {
                QChar nextCh = text.at(i + 1);
                if (ch == 'A' && nextCh == 'V') {
                    advance -= 10; // 適当な値で詰める (ピクセル単位)
                }
            }
            currentX += advance;
        }

        // 比較のために通常の描画も行う
        painter.setFont(font());
        painter.drawText(50, 150, "AVATAR (Standard drawText)");

        QFont unkernedFont = font();
        unkernedFont.setKerning(false);
        painter.setFont(unkernedFont);
        painter.drawText(50, 180, "AVATAR (Standard drawText Unkerned)");
    }

private:
    QRawFont rawFont;
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ManualKerningWidget widget;
    widget.setWindowTitle("Manual Kerning Example (Concept)");
    widget.show();
    return a.exec();
}

利点

  • 非常に特殊なレイアウト要件に対応できる。
  • フォントの最も詳細な情報にアクセスし、独自のレンダリングロジックを実装できる。

欠点

  • ほとんどのアプリケーションではここまでの制御は不要。
  • パフォーマンスが低下する可能性が高い。

QPainter::drawText のフラグ調整 (一部限定的)

QPainter::drawText() のオーバーロードには、描画オプションを指定するための Qt::TextFlags 引数があります。これ自体が直接カーニングを調整するものではありませんが、テキストの配置やトリミングに影響を与え、結果として見た目の間隔に影響を与える可能性がゼロではありません。

例えば、Qt::AlignJustify はテキストを両端揃えにするため、文字間や単語間が調整されますが、これはカーニングとは異なる目的です。

使用例 (カーニングとは異なるが、間隔調整の例)

#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QFont>

class TextFlagsWidget : public QWidget
{
public:
    TextFlagsWidget(QWidget *parent = nullptr) : QWidget(parent)
    {
        setFixedSize(400, 200);
    }

protected:
    void paintEvent(QPaintEvent *event) override
    {
        Q_UNUSED(event);
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);

        QFont font("Arial", 20);
        painter.setFont(font);

        QString text = "This is a justified text example.";
        QRect rect(50, 50, 300, 50); // 描画領域

        painter.drawRect(rect); // 描画領域を表示

        // テキストを両端揃えにする
        painter.drawText(rect, text, Qt::AlignJustify | Qt::TextWordWrap);

        // 通常の描画(比較用)
        painter.drawText(50, 150, "This is a normal text example.");
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TextFlagsWidget widget;
    widget.setWindowTitle("Qt::TextFlags Example");
    widget.show();
    return a.exec();
}

利点

  • 既存の drawText の使い方を大きく変更する必要がない。

欠点

  • カーニング(特定の文字間の調整)ではなく、行全体の配置(両端揃えなど)に使われるため、QFont::kerning() の代替としては非常に限定的。

外部ライブラリの利用 (HarfBuzzなど)

Qt の標準的なテキストレンダリング機能が、特定の要件を満たさない場合(特に複雑なスクリプト言語のレンダリングや、最新のOpenType機能の完全なサポートが必要な場合)、HarfBuzz のような外部のテキストシェーピングライブラリをQtプロジェクトに統合することが考えられます。

HarfBuzzとは
HarfBuzzは、OpenTypeフォントの複雑なテキストレイアウト機能を処理するためのオープンソースライブラリです。リガチャ、カーニング、結合文字、字形置換など、フォントに埋め込まれたタイポグラフィ情報を正確に適用できます。

特徴

  • 最新のOpenType機能にフル対応。
  • 様々な言語(アラビア語、デーヴァナーガリーなど)の複雑なテキスト表示に対応。
  • 最も高度で正確なテキストシェーピング(文字整形)が可能。

使用例 (概念的な説明)

HarfBuzzをQtプロジェクトに統合し、HarfBuzzで文字整形を行った後、結果として得られるグリフと位置情報をQPainterで描画するという流れになります。これは、Qtの内部レンダリングを完全に置き換えるか、その一部を強化する高度な方法です。

// これはHarfBuzzとQtの統合の概念的な説明です。
// 実際のコードはHarfBuzzとQtのAPIを詳細に理解する必要があります。
// 非常に複雑なため、具体的な実行可能コードは省略します。

#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QFont>
#include <QGlyphRun> // Qt 5.10以降で利用可能

// HarfBuzz のヘッダー (別途インストールとビルドが必要)
// #include <hb-ft.h>
// #include <hb.h>

class HarfBuzzWidget : public QWidget
{
public:
    HarfBuzzWidget(QWidget *parent = nullptr) : QWidget(parent)
    {
        setFixedSize(500, 200);
        setFont(QFont("Arial", 30));
    }

protected:
    void paintEvent(QPaintEvent *event) override
    {
        Q_UNUSED(event);
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);

        QString text = "AVATAR with HarfBuzz (Concept)";
        QPointF textPos(50, 100);

        // ここにHarfBuzzを使ってテキストをシェーピングするロジックが入ります。
        // 1. QFontからフォントデータを取得(QRawFontなどを経由)
        // 2. HarfBuzzのバッファを作成し、テキストとスクリプトを設定
        // 3. HarfBuzzでhb_shape() を呼び出し、グリフと位置情報を取得
        // 4. 取得したグリフと位置情報を QGlyphRun に変換
        // 5. QPainter::drawGlyphRun() で描画

        // 例: QGlyphRun を作成する部分(HarfBuzzの結果を模倣)
        QGlyphRun glyphRun;
        // 実際にはHarfBuzzから得られたグリフIDと位置を設定
        glyphRun.setRawFont(font()); // QRawFontから作成したQRawFontオブジェクトを設定
        glyphRun.setGlyphIndexes(QVector<quint32>{100, 101, 102, 103, 104, 105}); // 仮のグリフID
        glyphRun.setGlyphPositions(QVector<QPointF>{
            QPointF(0, 0), QPointF(20, 0), QPointF(40, 0), QPointF(60, 0), QPointF(80, 0), QPointF(100, 0)
        }); // 仮の位置情報(カーニングが適用された後)

        // QPainterでQGlyphRunを描画
        // painter.drawGlyphRun(textPos, glyphRun); // QPainterにdrawGlyphRun()は存在しない

        // QPainterで個々のグリフを描画するか、QTextLayoutでQGlyphRunを利用する
        // QPainter::drawText(QPointF, QRawFont, const QVector<quint32> &glyphIndexes, const QVector<QPointF> &glyphPositions) のようなAPIは直接提供されていない
        // そのため、QTextLayoutを使うか、自力で各グリフをパスに変換して描画する必要がある
        painter.setFont(font()); // フォントを設定して通常描画で代用
        painter.drawText(textPos, text);

        painter.drawText(50, 150, "This is a conceptual example only.");
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    HarfBuzzWidget widget;
    widget.setWindowTitle("HarfBuzz Integration (Conceptual)");
    widget.show();
    return a.exec();
}

利点

  • 将来的なフォント技術や言語サポートへの対応。
  • テキストレンダリングの最高レベルの精度と機能性。

欠点

  • ほとんどの一般的なアプリケーションでは過剰な解決策。
  • ビルドプロセスが複雑になり、依存関係が増える。
  • 非常に高度で複雑
    Qtのテキストレンダリングパイプラインの深い理解と、HarfBuzzライブラリ自体の知識が必要。

ほとんどのQtアプリケーションでは、QFont::setKerning(true) を使用し、カーニング情報が適切に埋め込まれた高品質なフォントを選択することが、最も簡単で効果的な方法です。

もし、それで解決しない場合は、以下の順序で検討することをお勧めします。

  1. QTextLayout の使用
    QPainter::drawText() よりも複雑なレイアウトや、OpenTypeフォントのより高度な機能を活用したい場合に検討します。これにより、カーニングの問題が改善される可能性があります。
  2. フォントの確認と変更
    根本的な問題がフォント側にある場合が多いです。別のフォントで試すか、より高品質なフォントの導入を検討してください。
  3. 手動計算や外部ライブラリ
    これらは最後の手段であり、非常に特殊なタイポグラフィ要件を持つアプリケーション(例: 印刷DTPソフトウェア、高度なテキストエディタなど)以外では、その複雑さに見合うメリットは少ないでしょう。