O HotPDF consegue manter subsets de fontes TrueType e OpenType em disco e reutilizá-los entre documentos e entre execuções do processo, de modo que um lote que renderiza dez mil demonstrativos com as mesmas três fontes faz subset delas uma vez em vez de dez mil vezes. O cache é configurado com duas propriedades, inspecionado com um registro, e seguro para deixar ligado: uma falha de cache cai para o subsetting normal em memória e nunca impede um documento de ser produzido
Subsetting é caro por um motivo. Construir um subset significa percorrer o fechamento de glifos, reescrever loca e glyf, reconstruir cmap e hmtx, e emitir um mapeamento CID que o PDF possa endereçar. Para um documento esse custo desaparece no ruído. Para um servidor de relatórios que produz documentos em um loop, costuma ser o maior bloco único de tempo de CPU da execução
O que torna um cache hit possível
Quatro coisas precisam corresponder: o conteúdo da fonte, o conjunto de glifos usados, o modo de subset, e o esquema do cache. erre um só e o HotPDF faz subset do zero, porque um subset só é reutilizável quando teria sido byte idêntico de qualquer maneira
O conjunto de glifos é a condição que surpreende. Duas faturas que diferem em um único nome de cliente usam conjuntos de glifos diferentes, e portanto produzem subsets diferentes e entradas de cache diferentes. O cache se paga quando documentos compartilham um repertório de glifos — demonstrativos de um template fixo, formulários cujos dados variáveis são numéricos, catálogos desenhados a partir de um banco de dados de produtos — e não paga nada quando cada documento desenha uma fatia diferente de uma fonte CJK grande. Meça antes de supor em qual caso você está
var
Pdf: THotPDF;
Info: THPDFFontSubsetCacheInfo;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.EnableFontSubsetting := True;
Pdf.FontSubsetCacheFolder := 'C:\ProgramData\Reports\fontcache';
Pdf.FontSubsetCacheMaxBytes := 64 * 1024 * 1024; // 64 MiB, default is 256
// ... generate the batch ...
Info := Pdf.GetFontSubsetCacheInfo;
LogFmt('subset cache: %d hits, %d misses, %d bytes in %d files',
[Info.HitCount, Info.MissCount, Info.CurrentBytes, Info.FileCount]);
finally
Pdf.Free;
end;
end;
Como você sabe se o cache está fazendo alguma coisa?
GetFontSubsetCacheInfo devolve nove contadores, e a razão entre os dois primeiros responde à pergunta diretamente. HitCount e MissCount dão a taxa de acerto. WriteCount e EvictionCount mostram se as entradas sobrevivem tempo suficiente para serem reutilizadas ou estão sendo empurradas para fora por um orçamento pequeno demais. CurrentBytes e FileCount informam o que está no disco agora
Os três restantes são os que valem alertar. CorruptCount conta entradas que falharam na validação e foram removidas — algumas depois de um desligamento incômodo são normais, um fluxo constante significa armazenamento não confiável. RejectedCount conta entradas recusadas antes do uso. WriteFailureCount conta entradas que não puderam ser escritas de jeito nenhum, o que costuma significar um problema de permissões na pasta em vez de algo sobre fontes. Nenhum dos três interrompe a geração de documentos, que é exatamente por que você tem que olhar para eles: um cache que silenciosamente nunca escreve parece igual por fora a um cache que funciona, exceto pela conta de CPU
Despejo, orçamentos e o momento em que você reduz um
FontSubsetCacheMaxBytes tem padrão de 268435456 bytes, ou seja, 256 MiB, e pode ser reduzido em runtime. Reduzir dispara despejo imediato por uso menos recente em vez de esperar pela próxima escrita, então um serviço que reage à pressão de disco consegue liberar espaço no momento em que decide, não em algum ponto posterior que ele não controla
Definir FontSubsetCacheFolder como uma string vazia desabilita o nível em disco sem limpar nada já armazenado, e sem mudar um único byte da saída de fonte. Essa é a propriedade a alcançar quando você quer isolar o cache durante o diagnóstico: desligue, rode o mesmo lote, e compare os PDFs produzidos. Eles devem ser idênticos, porque o cache armazena um resultado, não uma política
O que o cache faz quando uma entrada está danificada
Ele a remove e faz subset normalmente. Entradas malformadas ou truncadas são rejeitadas antes que o subset possa chegar a um stream PDF, que é a parte do projeto que mais importa: uma entrada de cache corrompida que chegasse a um documento produziria um PDF com um programa de fonte quebrado, e essa falha apareceria longe de sua causa — em um visualizador, na máquina de um cliente, semanas depois
Escritas são atômicas, então um leitor nunca observa uma entrada parcialmente escrita, e uma queda no meio da escrita deixa o cache consistente em vez de envenenado. Entradas de subset compactas retêm os dados de remapeamento CID que os dicionários de fonte PDF/A exigem, então um subset em cache ainda é um subset compatível — saída arquivável não precisa contornar o cache para se manter válida
// Reset the disk tier after a font upgrade or a schema change
Pdf.ClearFontSubsetCache;
// Or move it somewhere writable and let the budget apply immediately
Pdf.SetFontSubsetCacheFolder('D:\cache\fonts');
Onde colocar a pasta em uma implantação real
Três propriedades decidem isso: a pasta precisa ser gravável pela conta sob a qual o serviço roda, deve ficar em armazenamento local em vez de em um compartilhamento de rede, e não deve estar dentro de um diretório que uma etapa de implantação limpe. Um cache em compartilhamento transforma cada miss em uma ida e volta e cada hit em duas; um cache sob uma pasta da aplicação que o instalador recria é um cache que começa frio após cada atualização
Para serviços multi-instância, dê a cada instância sua própria pasta a menos que você tenha confirmado que o armazenamento trata substituição atômica concorrente do jeito que você espera. O custo de uma entrada duplicada é uma passagem extra de subsetting; o custo de depurar uma corrida em cache compartilhado é uma tarde
Quando buscar outra coisa
O cache reduz trabalho repetido. Ele não reduz o trabalho do primeiro documento, e não ajuda uma carga cujos conjuntos de glifos nunca se repetem. Se a sua saída é dominada por uma fonte CJK enorme usada em texto imprevisível, a alavanca mais eficaz é o fechamento do subset em si — quais glifos são puxados, e por quê — coberta nas notas sobre fechamento de subset de fonte e shaping de glifos. Se o seu lote é lento por razões que acabam não sendo fontes de forma nenhuma, o guia de saída de relatórios com fontes e imagens mostra para onde vai geralmente o outro tempo, e o estudo de caso sobre o bug de ordenação de subset em EndDoc é um lembrete de que correção de subsetting e velocidade de subsetting são problemas separados
O HotPDF é um componente PDF VCL nativo para Delphi e C++Builder, e o cache de subsets faz parte da biblioteca em vez de um serviço de complemento, de modo que um servidor de relatórios o obtém ao definir um caminho de pasta — veja a página do componente HotPDF para a lista completa de recursos de fonte e desempenho