Technical Article

Delphi'de Metinden Sese Özellikli Erişilebilir PDF Görüntüleyicileri Oluşturma

Modern yazılım geliştirmede erişilebilirlik (accessibility) artık isteğe bağlı bir özellik değildir. ADA (Engelli Amerikalılar Yasası) ve AB Web Erişilebilirlik Direktifi gibi düzenlemeler uyarınca, belgeleri işleyen yazılımlar yardımcı teknolojiler sağlamalıdır. PDF görüntüleyiciler için bu, sağlam bir Metinden Sese (Text-to-Speech - TTS) ve ekran okuyucu entegrasyonu uygulamak anlamına gelir.

Bu makalede, Delphi'de PDFium kullanarak bir PDF'den anlamsal metin akışlarını nasıl çıkaracağımıza ve ardından tamamen erişilebilir bir PDF görüntüleyici oluşturmak için bu metni Windows Konuşma API'sine (Windows Speech API - SAPI) nasıl besleyeceğimize bakacağız.

PDF Metin Çıkarma Zorluğu

Bir PDF temel olarak boyama talimatlarının (painting instructions) yer aldığı bir tuvaldir (canvas). İçsel olarak bir "paragrafın" veya "sütunun" ne olduğunu bilmez; yalnızca glifleri (glyphs) belirli X/Y koordinatlarına yerleştirir. Bir belgeyi mantıksal bir sırayla sesli okumak için ayrıştırıcınızın (parser) okuma sırasını yeniden oluşturması gerekir.

PDFium bunu, tutarlı metin akışları çıkarmak için gliflerin mekansal ilişkilerini analiz eden FPDFText_* API ailesi aracılığıyla ele alır.

Adım 1: PDFium ile Metin Çıkarma

Metni seslendirmeden önce, onu çıkarmalıyız. Aşağıdaki Delphi kodu, bir metin sayfasının (text page) nasıl başlatılacağını ve içeriğinin standart bir dizeye (string) nasıl çıkarılacağını gösterir.

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;

Adım 2: Windows Konuşma API'sini (SAPI) Entegre Etme

Anlamsal metne sahip olduğumuzda, onu Windows Konuşma API'sine geçirebiliriz. SAPI, asenkron konuşma sentezi, ses seçimi ve konuşma hızı kontrolüne olanak tanıyan SpVoice COM arayüzünü (interface) sağlar.

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;

Adım 3: Konuşmayı Vurgulama (Highlighting) ile Senkronize Etme

Gerçekten erişilebilir bir görüntüleyici metni sadece körü körüne okumaz; kelimeler konuşuldukça onları ekranda vurgular. SAPI, bir kelime sınırına (word boundary) ulaşıldığında tetiklenen olayları (bağlantı noktaları/connection points aracılığıyla) sağlar.

SAPI kelime sınırı olayı tarafından döndürülen karakter dizinini FPDFText_GetCharBox() kullanarak PDFium'daki karakter diziniyle eşleştirerek, o anda konuşulan kelimenin sınırlayıcı dikdörtgenini (bounding rectangle) alabilir ve görüntüleyici tuvalinizin (canvas) üzerine bir vurgulama katmanı (highlight overlay) çizebilirsiniz.

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;

Kapsayıcı (Inclusive) Bir Uygulama Oluşturmak

PDFium'un mekansal metin çıkarma özelliğiyle SAPI'nin konuşma motorunu birleştiren Delphi geliştiricileri, görme engelli kullanıcılar veya işitsel öğrenmeyi tercih edenler için güçlü araçlar oluşturabilirler. Bu özelliklerin düzgün bir şekilde uygulanması, uygulamanızın katı kurumsal ve devlet erişilebilirlik standartlarına uymasını sağlar.

Not: Entegre metin çıkarma ve koordinat eşleme PDFium Component tarafından tam olarak desteklenmektedir.