Technical Article

Criar Visualizadores de PDF Acessíveis com Texto para Voz em Delphi

A acessibilidade já não é uma funcionalidade opcional no desenvolvimento de software moderno. Ao abrigo de regulamentações como a ADA (Lei dos Americanos com Deficiência) e a Diretiva de Acessibilidade Web da UE, o software que lida com documentos deve fornecer tecnologias de apoio. Para visualizadores de PDF, isto significa implementar uma robusta integração de Texto para Voz (Text-to-Speech - TTS) e leitores de ecrã.

Neste artigo, veremos como extrair fluxos de texto semântico de um PDF usando PDFium em Delphi e, em seguida, alimentar esse texto na API de Voz do Windows (SAPI) para construir um visualizador de PDF totalmente acessível.

O Desafio da Extração de Texto de PDF

Um PDF é fundamentalmente uma tela (canvas) de instruções de pintura. Não sabe inerentemente o que é um "parágrafo" ou uma "coluna"; apenas coloca glifos em coordenadas X/Y específicas. Para ler um documento em voz alta numa ordem lógica, o seu analisador deve reconstruir a ordem de leitura.

O PDFium lida com isto através da família de APIs FPDFText_*, que analisa as relações espaciais dos glifos para produzir fluxos de texto coerentes.

Passo 1: Extrair Texto com PDFium

Antes de podermos vocalizar o texto, devemos extraí-lo. O código Delphi seguinte demonstra como inicializar uma página de texto e extrair o seu conteúdo para uma cadeia de caracteres (string) padrão.

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;

Passo 2: Integrar a API de Voz do Windows (SAPI)

Assim que tivermos o texto semântico, podemos passá-lo para a API de Voz do Windows. A SAPI fornece a interface COM SpVoice, que permite a síntese de voz assíncrona, seleção de voz e controlo da velocidade da fala.

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;

Passo 3: Sincronizar a Voz com o Realce

Um visualizador verdadeiramente acessível não lê apenas o texto de forma cega; realça as palavras no ecrã à medida que são faladas. A SAPI fornece eventos (através de pontos de ligação) que disparam quando se atinge o limite de uma palavra.

Ao mapear o índice do caráter devolvido pelo evento de limite de palavra da SAPI de volta ao índice do caráter no PDFium utilizando FPDFText_GetCharBox(), pode recuperar o retângulo delimitador (bounding rectangle) da palavra falada atualmente e desenhar uma sobreposição de realce na tela do seu visualizador.

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;

Construir uma Aplicação Inclusiva

Ao combinar a extração espacial de texto do PDFium com o motor de voz SAPI, os programadores Delphi podem criar ferramentas poderosas para utilizadores com deficiência visual ou aqueles que preferem a aprendizagem auditiva. A implementação adequada destas funcionalidades garante que a sua aplicação cumpre rigorosos padrões de acessibilidade empresariais e governamentais.

Nota: A extração integrada de texto e o mapeamento de coordenadas são totalmente suportados pelo Componente VCL PDFium Component.