Technical Article

Creación de visores de PDF accesibles con texto a voz en Delphi

La accesibilidad ya no es una función opcional en el desarrollo de software moderno. Según normativas como la ADA (Ley de Estadounidenses con Discapacidades) y la Directiva de Accesibilidad Web de la UE, el software que maneja documentos debe proporcionar tecnologías de asistencia. Para los visores de PDF, esto significa implementar integraciones sólidas de texto a voz (TTS) y lectores de pantalla.

En este artículo, analizaremos cómo extraer flujos de texto semántico de un PDF utilizando PDFium en Delphi, y luego enviar ese texto a la API de voz de Windows (SAPI) para crear un visor de PDF totalmente accesible.

El desafío de la extracción de texto en PDF

Fundamentalmente, un PDF es un lienzo de instrucciones de dibujo. Inherentemente no sabe qué es un "párrafo" o una "columna"; simplemente coloca glifos en coordenadas X/Y específicas. Para leer un documento en voz alta en un orden lógico, su analizador debe reconstruir el orden de lectura.

PDFium maneja esto a través de la familia de API FPDFText_*, que analiza las relaciones espaciales de los glifos para generar flujos de texto coherentes.

Paso 1: Extracción de texto con PDFium

Antes de que podamos pronunciar el texto, debemos extraerlo. El siguiente código de Delphi demuestra cómo inicializar una página de texto y extraer su contenido en una cadena (string) estándar.

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;

Paso 2: Integración de la API de voz de Windows (SAPI)

Una vez que tenemos el texto semántico, podemos pasarlo a la API de voz de Windows. SAPI proporciona la interfaz COM SpVoice, que permite la síntesis de voz asincrónica, la selección de voz y el control de la velocidad de lectura.

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;

Paso 3: Sincronización de voz con resaltado

Un visor verdaderamente accesible no solo lee el texto a ciegas; resalta las palabras en la pantalla a medida que se pronuncian. SAPI proporciona eventos (a través de puntos de conexión) que se disparan cuando se alcanza el límite de una palabra.

Al asignar el índice de caracteres devuelto por el evento de límite de palabra de SAPI nuevamente al índice de caracteres en PDFium utilizando FPDFText_GetCharBox(), puede recuperar el rectángulo delimitador de la palabra pronunciada actualmente y dibujar una superposición de resaltado en el lienzo de su visor.

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;

Creación de una aplicación inclusiva

Al combinar la extracción de texto espacial de PDFium con el motor de voz de SAPI, los desarrolladores de Delphi pueden crear herramientas potentes para usuarios con discapacidad visual o para aquellos que prefieren el aprendizaje auditivo. La implementación adecuada de estas características garantiza que su aplicación cumpla con los estrictos estándares de accesibilidad empresariales y gubernamentales.

Nota: La extracción de texto integrada y el mapeo de coordenadas son totalmente compatibles con el PDFium Component.