A renderização progressiva do PDFium divide o trabalho de renderização de uma página em fatias. Em vez de bloquear até que uma página completa esteja pronta, o motor renderiza uma banda, cede o controlo, renderiza outra banda, cede novamente, e assim por diante. Cada fatia pode ser exibida assim que está pronta, pelo que o utilizador vê a página aparecer de cima para baixo em vez de aparecer toda de uma vez após um longo atraso. O mecanismo que controla essa cedência — a estrutura IFSDK_PAUSE — pode ser reutilizado como um sinal de cancelamento, o que é a parte não documentada que o PDFiumPas expõe através do registo TPdfProgressivePause
Como o IFSDK_PAUSE funciona
A estrutura IFSDK_PAUSE contém um ponteiro de função, NeedToPauseNow, que o PDFium chama após cada banda renderizada. Se o ponteiro de função devolver não-zero, o PDFium para de renderizar e devolve o controlo ao chamador com um código de estado que indica que a renderização está incompleta. O chamador pode então chamar FPDF_RenderPage_Continue para resumir, ou chamar FPDF_RenderPage_Close para descartar o estado de renderização incompleto
O objetivo pretendido é cedência: o chamador devolve verdadeiro para ceder o controlo quando precisa de atualizar a UI ou verificar eventos, e retoma imediatamente depois. Mas o mesmo mecanismo também serve como cancelamento: se o chamador devolver verdadeiro para NeedToPauseNow e depois chamar FPDF_RenderPage_Close sem nunca chamar FPDF_RenderPage_Continue, a renderização é efetivamente cancelada no ponto de cedência
O registo TPdfProgressivePause
O PDFiumPas embrulha IFSDK_PAUSE no registo TPdfProgressivePause. O registo mantém uma referência ao token de cancelamento passado pelo chamador e implementa o retorno de chamada NeedToPauseNow para verificar esse token. Quando o token é cancelado, o retorno de chamada devolve 1, fazendo com que o PDFium pare após a banda atual. O chamador vê isso, verifica o token, e chama FPDF_RenderPage_Close em vez de FPDF_RenderPage_Continue
// Simplified illustration of the progressive render loop
var
Pause: TPdfProgressivePause;
Status: Integer;
begin
Pause := TPdfProgressivePause.Create(CancellationToken);
Status := FPDF_RenderPageBitmap_Start(Bitmap, Page, 0, 0, Width, Height, 0, Flags, @Pause.PauseRec);
try
while Status = FPDF_RENDER_TOBECONTINUED do
begin
if CancellationToken.IsCancelled then
Break;
Status := FPDF_RenderPage_Continue(Page, @Pause.PauseRec);
end;
finally
FPDF_RenderPage_Close(Page); // always call Close in finally
end;
end;
O bloco finally é essencial. FPDF_RenderPage_Close liberta o estado de renderização interna que o PDFium alocou quando a renderização começou. Se não for chamado — por exemplo, porque uma exceção saltou fora do loop — o PDFium vaza esse estado. Isto é verdade tanto para a conclusão normal como para o cancelamento: o caminho de cancelamento termina o loop antecipadamente, mas o finally assegura que Close seja sempre chamado
PdfNoCancellationToken para renderização não cancelável
Nem toda a renderização progressiva precisa de suporte de cancelamento. Quando o chamador quer a cedência de banda (para mostrar o progresso) mas não vai nunca cancelar, passar nil como token seria arriscado — o retorno de chamada teria de verificar nulo antes de desreferenciar o token. O PDFiumPas fornece um singleton PdfNoCancellationToken para este caso. É um token que nunca reporta cancelamento, pelo que NeedToPauseNow cede após cada banda mas nunca solicita paragem
// Render with banding (for progressive display) but no cancellation
Pause := TPdfProgressivePause.Create(PdfNoCancellationToken);
Status := FPDF_RenderPageBitmap_Start(..., @Pause.PauseRec);
// loop calls FPDF_RenderPage_Continue; never cancelled
Isso mantém o caminho de código do loop de renderização uniforme — sem verificações de nulo, sem ramificações especiais — e deixa o chamador substituir PdfNoCancellationToken por um token real quando o cancelamento for posteriormente necessário
Exibir bandas à medida que chegam
A vantagem de desempenho percebido da renderização progressiva vem de exibir cada banda assim que está pronta. Depois de FPDF_RenderPageBitmap_Start ou FPDF_RenderPage_Continue retornar com FPDF_RENDER_TOBECONTINUED, a área do bitmap correspondente às bandas concluídas até agora é válida e pode ser exibida. A área das bandas que ainda não foram renderizadas contém seja o conteúdo de fundo do bitmap (tipicamente branco) ou o resultado da renderização anterior se o bitmap estiver a ser reutilizado entre páginas
O mecanismo de exibição depende da UI. Para aplicações VCL, isso tipicamente implica invalidar o controlo de visualização e chamar Repaint ou Update dentro do loop de renderização. A frequência das atualizações de exibição deve ser equilibrada contra o custo de redesenho — atualizar após cada banda é desnecessário se as bandas forem muito pequenas, mas não atualizar suficientemente frequentemente nega o benefício percebido
Quando usar renderização progressiva versus futuros de segundo plano
A renderização progressiva e os futuros canceláveis de segundo plano abordam o mesmo problema de ângulos diferentes. A renderização progressiva corre na thread atual, cede periodicamente para que a UI possa ser atualizada, e pode mostrar resultados parciais. Os futuros de segundo plano movem a renderização inteiramente para uma thread separada, mantendo a thread da UI livre mas sem mostrar resultados parciais
A renderização progressiva é melhor quando: a UI precisa de mostrar o progresso de renderização; a página é tão grande que esperar por ela completa seria desorientador; ou o código já corre numa thread de trabalho e os resultados parciais são valiosos. Os futuros de segundo plano são melhores quando: a UI deve permanecer totalmente responsiva durante a renderização; os resultados parciais não acrescentam valor; ou o pipeline de renderização é mais simples de gerir sem cedência dentro do loop
Os dois padrões podem ser combinados: um futuro de segundo plano que internamente usa renderização progressiva com cedência para transmitir resultados parciais de volta à thread principal. O artigo sobre futuros canceláveis abrange a camada de futuro em detalhe. A medição de texto, que pode informar os limites de banda de renderização para conteúdo de texto pesado, está em medição de texto PDF para composição. Ambas as funcionalidades fazem parte do Componente PDFiumPas para Delphi e Lazarus