無障礙功能 (Accessibility) 在現代軟體開發中已不再是可有可無的功能。在 ADA (美國身心障礙者法案) 和歐盟 Web 無障礙指令等法規下,處理文件的軟體必須提供輔助技術。對於 PDF 檢視器來說,這意味著要實作穩健的文字轉語音 (Text-to-Speech, TTS) 與螢幕閱讀器整合。
在本文中,我們將探討如何使用 Delphi 中的 PDFium 從 PDF 擷取語意文字串流,然後將該文字輸入至 Windows 語音 API (SAPI),以建置完全無障礙的 PDF 檢視器。
PDF 文字擷取的挑戰
PDF 基本上是一塊繪圖指令畫布。它本身並不知道「段落」或「欄位」是什麼;它只是將字形 (glyphs) 放置在特定的 X/Y 座標上。為了以合乎邏輯的順序大聲朗讀文件,您的解析器必須重建閱讀順序。
PDFium 透過 FPDFText_* API 系列處理這個問題,它會分析字形的空間關係,輸出連貫的文字串流。
第 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 語音 API (SAPI)
一旦我們有了語意文字,我們就可以將它傳遞給 Windows 語音 API。SAPI 提供了 SpVoice COM 介面,允許進行非同步語音合成、語音選擇與語速控制。
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 提供事件 (透過連線點 connection points),這些事件會在到達單字邊界時觸發。
藉由將 SAPI 單字邊界事件傳回的字元索引對映回使用 FPDFText_GetCharBox() 的 PDFium 字元索引,您可以擷取目前朗讀單字的邊界矩形,並在檢視器畫布上繪製反白覆蓋層。
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 元件 完全支援整合文字擷取與座標對映。