Artigo Técnico

Desenhar Tipos de Letra Incorporados em PDFs com o HotPDF em Delphi

O HotPDF desenha os tipos de letra (fontes) incorporados num PDF em Delphi sem necessidade de qualquer instalação no sistema operativo. O fluxo de renderização de glifos em HPDFGlyphRender.pas analisa os programas de fontes integrados no próprio ficheiro PDF — contornos TrueType glyf em FontFile2, strings de caracteres CFF Type 2 (charstrings) em FontFile3 e fluxos de conteúdo de glifos Type 3 —, reproduzindo-os como caminhos vetoriais preenchidos via GDI do Windows. Este artigo apresenta uma análise detalhada da fidelidade das fontes, complementando o artigo sobre conversão de páginas PDF em bitmaps (TBitmap) com o HotPDF

Por que razão as páginas PDF exibem caixas vazias em vez de texto?

A apresentação de caixas vazias, espaços em branco ou caracteres incorretos na conversão de um PDF ocorre habitualmente quando o renderizador solicita os tipos de letra ao sistema operativo em vez de utilizar os dados incorporados no ficheiro. O problema manifesta-se sempre da mesma forma: o documento apresenta-se correto no computador onde foi gerado, mas ao ser aberto num servidor ou ambiente de trabalho com restrições exibe caracteres corrompidos, ou a substituição por uma fonte genérica altera a quebra de linha. Estas fontes residem apenas no PDF e renderizadores que dependem de fontes do sistema não as conseguem mapear. O uso de subconjuntos de fontes (subset fonts) agrava a situação: um subconjunto pode conter apenas quarenta glifos sob mapeamentos específicos criados exclusivamente para esse ficheiro

A norma ISO 32000-1 §9.9 define três contentores para programas de fontes incorporados no descritor: FontFile para fontes Type 1 originais, FontFile2 para TrueType e FontFile3 para CFF (Type1C ou CIDFontType0C) ou OpenType. Um quarto formato, as fontes Type 3 descritas em ISO 32000-1 §9.6.5, não integra dados binários: cada glifo é representado por um pequeno fluxo de conteúdo PDF desenhado na coordenada de visualização. Estes formatos diferem na matemática dos contornos (curvas quadráticas B-splines versus charstrings cúbicas ou operadores gráficos genéricos), requerendo um interpretador específico para cada tipo e um mapeamento que associe os caracteres ao índice de glifo correspondente

HotPDF: Transportadores de fontes PDF incorporadas: o descritor de fontes contém programas FontFile, FontFile2 ou FontFile3 e fluxos de conteúdo Type 3, e cada transportador segue o seu próprio caminho de renderização
Cada transportador incorporado recebe o seu próprio interpretador, desde contornos glyf TrueType a fluxos de conteúdo Type 3

Como o HotPDF converte contornos TrueType glyf em caminhos GDI?

A classe THPDFEmbeddedTTF em HPDFGlyphRender.pas consulta a tabela loca para posicionar cada registo de glifo, percorrendo os contornos de glyf ponto a ponto para gerar o caminho GDI correspondente. Há duas regras TrueType cruciais: em primeiro lugar, pontos consecutivos fora da curva definem um ponto intermédio sobre a curva, e caminhos com todos os pontos fora da curva iniciam no ponto médio entre o primeiro e o último nós (ignorar esta lógica deforma os contornos arredondados); em segundo lugar, as curvas TrueType baseiam-se em curvas de Bézier quadráticas, enquanto o método PolyBezierTo da GDI exige cúbicas. Como tal, os segmentos quadráticos sofrem uma elevação de grau matemática exata em vez de serem aproximados por retas:

// Elevação de grau exata: quadrática (P0, Q, P2) -> cúbica (P0, C1, C2, P2)
// C1 = P0 + 2/3 (Q - P0),  C2 = P2 + 2/3 (Q - P2)
C1.X := P0.X + 2 * (Q.X - P0.X) / 3;
C1.Y := P0.Y + 2 * (Q.Y - P0.Y) / 3;
C2.X := P2.X + 2 * (Q.X - P2.X) / 3;
C2.Y := P2.Y + 2 * (Q.Y - P2.Y) / 3;
// PolyBezierTo com C1, C2, P2 — curva geometricamente idêntica

Esta conversão não apresenta perda de qualidade: a cúbica desenha o mesmo contorno, garantindo que o resultado coincide com o visualizado em qualquer zoom. O passo seguinte consiste na escala e posicionamento. Cada glifo é desenhado nas unidades de grelha da fonte (habitualmente 1000 ou 2048 unidades por em), cabendo ao renderizador combinar a matriz de escala, a matriz de texto e a matriz de transformação de visualização (CTM) antes de preencher os contornos. A ordem de combinação das matrizes é crítica: inverter a multiplicação colapsa todos os glifos na origem, resultando numa visualização em branco motivada por uma falha de ordenação algébrica

O interpretador Type 2 charstring para fontes CFF

A classe THPDFEmbeddedCFF disponibiliza um interpretador Type 2 charstring completo para os dados de FontFile3: processa as estruturas CFF INDEX, os dicionários Top DICT e Private DICT, executando as instruções para gerar os caminhos na GDI. Contentores OpenType (tipo OTTO) são abertos para extrair a tabela CFF; fluxos nativos CIDFontType0C e Type1C são lidos diretamente. As charstrings utilizam uma linguagem de pilha (stack language) compacta cuja leitura exige o cumprimento de três convenções: em primeiro lugar, o prefixo de largura opcional permite que o primeiro operador da pilha contenha um operando adicional; em segundo lugar, o operador hintmask assume um vstemhm quando existem operandos na pilha, dependendo os bytes de máscara a ignorar do número de hastes (stems) acumuladas (um erro nesta contagem corrompe os opcodes seguintes); em terceiro lugar, as sub-rotinas exigem aplicar um desvio (bias) ao seu índice (107, 1131 ou 32768, de acordo com o total de sub-rotinas) antes da pesquisa sob pena de aceder a funções erradas

Fontes CFF com chaves CID implicam uma indireção complementar: o código de caractere determina o CID, mas o índice da charstring baseia-se em GID, mapeando a tabela da fonte os índices GID para CID (exigindo que o renderizador monte a tabela inversa CID para GID antes de desenhar e selecione o Private DICT correspondente via FDSelect nas fontes que possuam múltiplos dicionários). Fontes Type1C associadas a nomes resolvem códigos de um byte através da codificação embutida no programa CFF ou na codificação ao nível do PDF. Lembre-se: o interpretador analisa operadores de ajuda (hinting) para manter o fluxo sincronizado, mas não executa o ajuste visual de píxeis (hinting)

O que são fontes Type 3 e como são desenhadas?

Um glifo Type 3 não constitui um contorno convencional: a norma ISO 32000-1 §9.6.5 define-o como um fluxo de conteúdo, pelo que o HotPDF o desenha salvaguardando o estado gráfico, combinando a matriz da fonte, tamanho e escala na CTM, e executando os operadores da mesma forma que desenha uma página comum utilizando o dicionário /Resources da fonte. Há duas regras críticas: as larguras (/Widths) na Type 3 baseiam-se no espaço do glifo (e não na escala 1/1000 das restantes fontes), exigindo aplicar a transformação /FontMatrix (fontes de códigos de barras com matriz 0.01 falhariam as larguras por uma ordem de grandeza); além disso, glifos definidos com o operador d1 impõem duas condições: o desenho é limitado pela caixa de corte (bounding box) e, nos termos de ISO 32000-1 §9.6.5.2, ignoram operadores de cor próprios desenhando com a cor de preenchimento ativa da página (omitindo operadores rg, g e k durante a chamada). Desrespeitar a regra de cor desenharia a preto códigos de barras que a página devesse exibir a azul; omitir o corte desenharia glifos fora dos respetivos limites

Pipeline de renderização de glyf TrueType em Delphi: a tabela loca localiza os records de glifos, os pontos médios implícitos em curva são reconstruídos, as quadráticas são elevadas exatamente a cúbicas, e o GDI preenche o caminho
Pontos médios implícitos e elevação exata de grau mantêm o contorno reproduzido idêntico às curvas glyf originais

Como converter códigos de caracteres em IDs de glifos

Os interpretadores de contornos resolvem metade do problema: os bytes no PDF representam códigos de caracteres e não índices de glifos. A norma ISO 32000-1 define duas metodologias de mapeamento. Para fontes simples, a secção 9.6.6 define uma precedência: o array /Differences sobrepõe-se à codificação base (WinAnsiEncoding, MacRomanEncoding ou StandardEncoding), que por sua vez se sobrepõe ao mapa embutido na fonte. O HotPDF converte estas definições numa tabela de 256 entradas de código para GID, associando nomes de glifos a índices por três caminhos: correspondência exata no CFF, índices literais baseados no padrão gNN/glyphNN e conversão via Adobe Glyph List para Unicode seguida de pesquisa na tabela cmap em TrueType. Para fontes compostas, a secção 9.7 delega o mapeamento no CIDToGIDMap: embora o valor padrão seja /Identity, o registo pode consistir num fluxo de pares big-endian indexados por CID (o gerador Unicode do HotPDF utiliza esta estrutura para criar subconjuntos compactos):

// /CIDToGIDMap em fluxo: pares Word big-endian indexados por CID
if 2 * CID + 1 <= High(MapBytes) then
  GID := (MapBytes[2 * CID] shl 8) or MapBytes[2 * CID + 1]
else
  GID := 0;  // fora dos limites mapeia para .notdef

Para pesquisas na tabela cmap em TrueType, o HotPDF percorre uma cadeia de recurso: valida subtabelas Windows Unicode (formato 4 e formato 12 para planos suplementares), a tabela de símbolos (3,0) com mapeamento na região privada F000 (o que permite a fontes como Wingdings responder a códigos ASCII comuns) e, por fim, formatos legados 6 e 0. O formato 2 é omitido uma vez que mapeia páginas multi-byte legadas (como Shift-JIS ou Big5) e não Unicode, contendo as fontes CJK modernas tabelas em formato 4 ou 12. Glifos sem correspondência nestas tabelas recorrem ao desenho genérico da GDI, restringindo a falha ao caractere em causa

Limitações técnicas e comportamento de recurso

As limitações do processo de desenho: o ajuste visual de píxeis (hinting) não é executado, preenchendo-se os contornos de acordo com as coordenadas originais (efeito irrelevante em escalas superiores a 150 DPI mas que pode divergir por um píxel em resoluções pequenas). Fontes Type 1 originais em FontFile (com cifragem eexec) não são interpretadas e eixos de fontes OpenType variáveis não são aplicados. Em caso de dados corrompidos ou tabelas glyf sem correspondência em cmap, o motor reverte para fontes do sistema para evitar falhas no desenho da página. Esta metodologia de fidelidade gráfica é transversal ao renderizador (como as graduações de cor analisadas no artigo sobre graduações de cor axiais e radiais), e o motor de gravação possui lógicas próprias descritas no artigo sobre ordenação de subconjuntos no método EndDoc

A utilização destas rotinas não exige código específico para gestão de fontes: os mecanismos descritos são executados automaticamente na chamada de desenho da página:

var
  Pdf: THotPDF;
  Bmp: TBitmap;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('invoice-embedded-fonts.pdf') > 0 then
    begin
      // Fontes incorporadas TrueType, CFF e Type 3 são lidas do
      // próprio ficheiro — sem necessidade de instalações no computador
      Bmp := Pdf.RenderLoadedPageToBitmap(0, 144);
      if Bmp <> nil then
      try
        Bmp.SaveToFile('page1.bmp');
      finally
        Bmp.Free;
      end;
    end;
  finally
    Pdf.Free;
  end;
end;

O benefício prático reflete-se na fiabilidade do sistema: os ficheiros PDF que incorporem tipos de letra são exibidos com as fontes corretas em servidores, contentores Windows ou terminais de clientes que não possuam as fontes instaladas. Estas rotinas integram o HotPDF Delphi Component para Delphi e C++Builder — uma biblioteca nativa VCL para criação, edição, extração de texto e desenho de páginas PDF sem dependências de DLLs externas

HotPDF: Mapeamento de códigos de caracteres PDF para IDs de glifos: as fontes simples resolvem-se através de Differences, da codificação base e do mapa do programa de fontes, as fontes compostas através de CIDToGIDMap, depois uma cadeia de fallback de cmap TrueType com um fallback GDI de glifo único
Ambas as famílias de tipos de letra terminam numa tabela de código-para-GID, e qualquer código não mapeável degrada um único glifo em vez da sequência de texto