Technical Article

Creare Visualizzatori PDF Accessibili con Sintesi Vocale in Delphi

L'accessibilità non è più una funzionalità opzionale nello sviluppo di software moderno. In base a normative come l'ADA (Americans with Disabilities Act) e la Direttiva UE sull'Accessibilità del Web, i software che gestiscono documenti devono fornire tecnologie assistive. Per i visualizzatori PDF, questo significa implementare una robusta sintesi vocale (Text-to-Speech, TTS) e l'integrazione degli screen reader.

In questo articolo, vedremo come estrarre flussi di testo semantico da un PDF utilizzando PDFium in Delphi, per poi passare tale testo all'API vocale di Windows (SAPI) per creare un visualizzatore PDF completamente accessibile.

La Sfida dell'Estrazione del Testo dai PDF

Un PDF è fondamentalmente un canvas di istruzioni di disegno. Non sa intrinsecamente cosa sia un "paragrafo" o una "colonna"; si limita a posizionare i glifi a specifiche coordinate X/Y. Per leggere un documento ad alta voce in un ordine logico, il parser deve ricostruire l'ordine di lettura.

PDFium gestisce questo tramite la famiglia di API FPDFText_*, che analizza le relazioni spaziali dei glifi per produrre flussi di testo coerenti.

Passaggio 1: Estrazione del Testo con PDFium

Prima di poter pronunciare il testo, dobbiamo estrarlo. Il seguente codice Delphi dimostra come inizializzare una pagina di testo ed estrarne i contenuti in una stringa standard.

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;

Passaggio 2: Integrazione dell'API Vocale di Windows (SAPI)

Una volta ottenuto il testo semantico, possiamo passarlo all'API Vocale di Windows. SAPI fornisce l'interfaccia COM SpVoice, che consente la sintesi vocale asincrona, la selezione della voce e il controllo della velocità del parlato.

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;

Passaggio 3: Sincronizzare la Voce con l'Evidenziazione

Un visualizzatore veramente accessibile non si limita a leggere il testo in modo cieco; evidenzia le parole sullo schermo man mano che vengono pronunciate. SAPI fornisce eventi (tramite connection point) che si attivano quando si raggiunge il limite di una parola.

Mappando l'indice del carattere restituito dall'evento SAPI al corrispondente indice del carattere in PDFium usando FPDFText_GetCharBox(), è possibile recuperare il rettangolo di delimitazione della parola attualmente pronunciata e disegnare un'evidenziazione sul canvas del visualizzatore.

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;

Creare un'Applicazione Inclusiva

Combinando l'estrazione spaziale del testo di PDFium con il motore di sintesi vocale di SAPI, gli sviluppatori Delphi possono creare strumenti potenti per utenti non vedenti o ipovedenti, o per coloro che preferiscono l'apprendimento uditivo. L'implementazione corretta di queste funzionalità garantisce che l'applicazione sia conforme ai rigorosi standard di accessibilità governativi e aziendali.

Nota: l'estrazione del testo e la mappatura delle coordinate sono pienamente supportate dal Componente VCL PDFium Component.