Technical Article

Delphiでのテキスト読み上げ機能を備えたアクセシブルなPDFビューアの構築

アクセシビリティは、現代のソフトウェア開発においてもはやオプションの機能ではありません。ADA(障害者差別禁止法)やEU Webアクセシビリティ指令などの規制の下では、ドキュメントを処理するソフトウェアは支援技術を提供する必要があります。PDFビューアの場合、これは堅牢なテキスト読み上げ(TTS)とスクリーンリーダーの統合を実装することを意味します。

この記事では、DelphiのPDFiumを使用してPDFからセマンティックテキストストリームを抽出し、そのテキストをWindows Speech API (SAPI)に供給して完全にアクセシブルなPDFビューアを構築する方法について説明します。

PDFテキスト抽出の課題

PDFは基本的に描画命令のキャンバスです。「段落」や「列」が何であるかを本質的に知っているわけではなく、特定のX/Y座標にグリフを配置するだけです。ドキュメントを論理的な順序で読み上げるには、パーサーが読み取り順序を再構築する必要があります。

PDFiumはFPDFText_* APIファミリを通じてこれを処理し、グリフの空間的関係を分析して一貫したテキストストリームを出力します。

ステップ 1: PDFiumを使用したテキストの抽出

テキストを読み上げる前に、それを抽出する必要があります。以下のDelphiコードは、テキストページを初期化し、そのコンテンツを標準の文字列に抽出する方法を示しています。

uses
  System.SysUtils, pdfium_lib;

function ExtractPageText(Doc: FPDF_DOCUMENT; PageIndex: Integer): string;
var
  Page: FPDF_PAGE;
  TextPage: FPDF_TEXTPAGE;
  CharCount: Integer;
  Buffer: array of WideChar;
begin
  Result := '';
  Page := FPDF_LoadPage(Doc, PageIndex);
  if Page = nil then Exit;
  
  try
    // Initialize the text extraction engine for this page
    TextPage := FPDFText_LoadPage(Page);
    if TextPage <> nil then
    begin
      try
        CharCount := FPDFText_CountChars(TextPage);
        if CharCount > 0 then
        begin
          SetLength(Buffer, CharCount + 1);
          // Extract the text into the wide string buffer
          FPDFText_GetText(TextPage, 0, CharCount, @Buffer[0]);
          Result := WideCharToString(@Buffer[0]);
        end;
      finally
        FPDFText_ClosePage(TextPage);
      end;
    end;
  finally
    FPDF_ClosePage(Page);
  end;
end;

ステップ 2: Windows Speech API (SAPI) の統合

セマンティックテキストを取得したら、それをWindows Speech APIに渡すことができます。SAPIはSpVoice COMインターフェースを提供し、これにより非同期の音声合成、音声の選択、および発話速度の制御が可能になります。

uses
  System.Win.ComObj, Winapi.ActiveX;

const
  SVSFlagsAsync = 1;

procedure SpeakText(const TextToSpeak: string);
var
  SpVoice: OLEVariant;
begin
  CoInitialize(nil);
  try
    SpVoice := CreateOleObject('SAPI.SpVoice');
    // Speak asynchronously so the UI does not freeze
    SpVoice.Speak(TextToSpeak, SVSFlagsAsync);
  finally
    CoUninitialize;
  end;
end;

ステップ 3: 音声とハイライトの同期

真にアクセシブルなビューアは、テキストを盲目的に読み上げるだけでなく、読み上げられている画面上の単語をハイライトします。SAPIは、単語の境界に達したときに発生するイベント(コネクションポイント経由)を提供します。

SAPIの単語境界イベントによって返された文字インデックスを、FPDFText_GetCharBox()を使用してPDFiumの文字インデックスにマッピングし直すことで、現在読み上げられている単語の境界四角形を取得し、ビューアキャンバスにハイライトオーバーレイを描画できます。

procedure HighlightWord(TextPage: FPDF_TEXTPAGE; CharIndex: Integer; CharCount: Integer);
var
  i: Integer;
  L, T, R, B: Double;
begin
  // Iterate through the characters of the spoken word
  for i := CharIndex to CharIndex + CharCount - 1 do
  begin
    // Get the physical bounding box on the PDF page
    FPDFText_GetCharBox(TextPage, i, @L, @R, @B, @T);
    // Transform PDF coordinates to screen coordinates and draw highlighting rect...
  end;
end;

インクルーシブなアプリケーションの構築

PDFiumの空間的テキスト抽出とSAPIの音声エンジンを組み合わせることで、Delphi開発者は視覚障害を持つユーザーや聴覚的学習を好むユーザー向けの強力なツールを作成できます。これらの機能を適切に実装することで、アプリケーションは厳格な企業および政府のアクセシビリティ基準に準拠することができます。

注: 統合されたテキスト抽出と座標マッピングは、PDFium Componentで完全にサポートされています。