Technical Article

Создание доступных средств просмотра PDF с функцией преобразования текста в речь в Delphi

Доступность больше не является необязательной функцией при разработке современного программного обеспечения. В соответствии с такими нормами, как ADA (Закон о защите прав граждан США с ограниченными возможностями) и Директива ЕС о доступности веб-сайтов, программное обеспечение, работающее с документами, должно предоставлять вспомогательные технологии. Для программ просмотра PDF это означает внедрение надежной интеграции с преобразованием текста в речь (TTS) и программами чтения с экрана.

В этой статье мы рассмотрим, как извлекать семантические текстовые потоки из PDF с помощью PDFium в Delphi, а затем передавать этот текст в Windows Speech API (SAPI) для создания полностью доступного средства просмотра PDF.

Сложность извлечения текста из PDF

PDF по своей сути представляет собой холст с инструкциями по рисованию. Он по своей природе не знает, что такое «абзац» или «столбец»; он просто размещает глифы по определенным координатам X/Y. Чтобы прочитать документ вслух в логическом порядке, ваш парсер должен реконструировать порядок чтения.

PDFium справляется с этим с помощью семейства API FPDFText_*, которое анализирует пространственные отношения глифов для вывода связных текстовых потоков.

Шаг 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 предоставляет COM-интерфейс SpVoice, который позволяет выполнять асинхронный синтез речи, выбирать голос и управлять скоростью речи.

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, обратно на индекс символа в PDFium с помощью FPDFText_GetCharBox(), вы можете получить ограничивающий прямоугольник текущего произносимого слова и нарисовать оверлей с выделением на холсте средства просмотра.

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 VCL.