Artigo Técnico

PDF para Markdown e DOCX no Delphi com PDF Library for Delphi

O PDF Library for Delphi converte conteúdo de PDF em dois formatos editáveis sem automação do Office. ExportPageMarkdown e ExportDocumentMarkdown retornam Markdown semântico com títulos inferidos, listas ordenadas e não ordenadas e tabelas em formato pipe, enquanto SaveDOCXToFile e SaveDOCXToStream gravam um pacote WordprocessingML contendo parágrafos, títulos, numeração de lista nativa, tabelas detectadas, estilo de fonte, quebras de página e imagens PNG posicionadas

Ambos rodam inteiramente em Pascal, em um servidor, sem o Word instalado e sem COM. Essa restrição é o motivo pelo qual o recurso existe em uma biblioteca de PDF, e não em uma ferramenta desktop

Por que "PDF para Word" é genuinamente difícil?

Porque uma página de PDF não contém parágrafos. Ela contém operadores de exibição de texto que posicionam runs de glifos em coordenadas, na ordem em que o produtor os emitiu, sem nenhuma obrigação de indicar que duas runs pertencem à mesma frase, muito menos ao mesmo item de lista. O formato foi projetado para descrever uma página impressa com exatidão, e ele consegue isso descartando a estrutura que produziu a página

Então todo conversor precisa reconstruir o que o gerador descartou. O agrupamento de linhas vem do espaçamento vertical e do alinhamento de baseline. Os limites de parágrafo vêm de mudanças de espaçamento e de recuo. Um título é uma linha cuja fonte é maior ou mais pesada do que o corpo do texto e que se destaca do que vem depois. Uma lista é uma sequência de parágrafos começando com um caractere de marcador ou um padrão numérico. Uma tabela é uma grade de blocos de texto cujas bordas se alinham ao longo de linhas e colunas. Cada uma dessas é uma inferência, e inferência significa um bom resultado em documentos que seguem convenções tipográficas comuns e um resultado medíocre em documentos que não seguem

Diagrama do PDF Library for Delphi do pipeline de inferência que transforma execuções brutas de glifos de PDF em linhas agrupadas, parágrafos, títulos, listas e tabelas exportados como Markdown ou DOCX
Todo título, lista e tabela na exportação é inferido de espaçamentos, fontes e bordas alinhadas, porque o próprio formato PDF não registra nenhum deles

PDFs tagueados são a exceção, e uma exceção grande. Quando o documento carrega uma árvore de estrutura, os papéis de parágrafo, título, lista e tabela são registrados em vez de adivinhados, e é por isso que o trabalho de acessibilidade descrito em estrutura de acessibilidade de PDF tagueado também compensa na qualidade da conversão. Se você controla o produtor, tagear sua saída é a coisa de maior alavancagem que você pode fazer por qualquer um que mais tarde precise convertê-la

Exportação em Markdown, uma página por vez

O caminho de Markdown é o indicado quando o destino é um pipeline de texto: um site de documentação, um índice de busca, um corpus de retrieval para um assistente. As opções são uma bit mask: PDF_MARKDOWN_INCLUDE_PAGE_MARKERS, PDF_MARKDOWN_DETECT_HEADINGS, PDF_MARKDOWN_PRESERVE_STYLES, com PDF_MARKDOWN_DEFAULT combinando as três

var
  Pdf: TPDFlib;
  Md: WideString;
begin
  Pdf := TPDFlib.Create;
  try
    Pdf.LoadFromFile('handbook.pdf', '');

    // Uma página, como string
    Md := Pdf.ExportPageMarkdown(1, PDF_MARKDOWN_DEFAULT);

    // Um intervalo de páginas, gravado em disco em streaming como UTF-8 sem BOM
    Pdf.SaveMarkdownToFile('1-40',
      PDF_MARKDOWN_DETECT_HEADINGS or PDF_MARKDOWN_PRESERVE_STYLES,
      'handbook.md');
  finally
    Pdf.Free;
  end;
end;

Os marcadores de página se justificam em trabalho de retrieval. Um trecho de texto que carrega a página de onde veio pode ser citado com precisão, e um leitor que segue a citação chega exatamente onde a afirmação está. Desative-os quando o Markdown for destinado à leitura humana, onde os limites de página do layout de origem são apenas ruído

Os pontos de entrada de streaming importam para documentos grandes. SaveMarkdownToStream e SaveMarkdownToFile gravam UTF-8 uma página por vez e não armazenam em buffer a saída completa, então um manual de 900 páginas não vira antes uma string de 900 páginas na memória. A ausência de uma byte-order mark também é deliberada: um BOM em um arquivo Markdown confunde um número surpreendente de geradores de sites estáticos e ferramentas de diff

DOCX sem Office na máquina

O gravador de DOCX produz o pacote por conta própria: entradas ZIP gravadas como Deflate bruto com verificações CRC, as partes WordprocessingML, e os relacionamentos que as ligam. Nada chama o Word, o que significa que a conversão roda em um servidor headless, dentro de uma conta de serviço, em um container, em todos os lugares onde a automação do Office é sem licença, instável ou proibida

var
  Pdf: TPDFlib;
  Target: TFileStream;
begin
  Pdf := TPDFlib.Create;
  Target := TFileStream.Create('handbook.docx', fmCreate);
  try
    Pdf.LoadFromFile('handbook.pdf', '');
    Pdf.SaveDOCXToStream('1-40',
      PDF_DOCX_INCLUDE_IMAGES or PDF_DOCX_DETECT_HEADINGS or
      PDF_DOCX_PRESERVE_STYLES or PDF_DOCX_PRESERVE_PAGE_BREAKS,
      Target);
  finally
    Target.Free;
    Pdf.Free;
  end;
end;

Os dados de imagem são gravados à medida que cada página é processada, em vez de coletados e anexados no final, então o pico de memória acompanha uma página, e não o documento inteiro. A ordem explícita das páginas é preservada, e a página de PDF selecionada é restaurada depois, o que importa quando a exportação é uma etapa dentro de um job mais longo que tinha uma página selecionada por outros motivos

O que o empacotamento determinístico traz de benefício?

Reprodutibilidade byte a byte. Duas conversões da mesma entrada com as mesmas opções produzem o mesmo pacote, o que significa que você pode calcular o hash da saída para detectar mudanças, comparar dois builds de um documento gerado, e usar cache de forma agressiva sem se preocupar que uma entrada idêntica tenha produzido um artefato diferente

Diagrama do PDF Library for Delphi da exportação de Markdown em streaming em que a máscara de bits de opções seleciona marcadores de página, detecção de títulos e preservação de estilos antes das gravações UTF-8 página a página
As opções se combinam como bitmask enquanto os gravadores em streaming emitem UTF-8 sem BOM, mantendo a memória de pico estável e deixando cada trecho recuperado com um número de página citável

A automação do Office não pode prometer isso. Ela incorpora timestamps, identificadores de revisão e metadados dependentes da máquina, então o mesmo documento convertido duas vezes difere de formas que inviabilizam o hashing. O mesmo raciocínio motiva os identificadores de arquivo determinísticos discutidos em IDs de PDF determinísticos para builds reproduzíveis: quando a saída é reproduzível, a verificação vira uma comparação em vez de uma inspeção

Onde a saída é boa, e onde não é

Seja honesto com seus usuários sobre isso, porque a qualidade da conversão varia mais com a entrada do que com o conversor. PDFs tagueados e documentos comerciais gerados de forma limpa, notas fiscais, relatórios, contratos, convertem bem: títulos viram títulos, tabelas sobrevivem, listas são renumeradas corretamente no Word. Layouts acadêmicos de duas colunas convertem de forma aceitável se a geometria das colunas for regular. Tabelas que atravessam quebras de página são remontadas por inferência e às vezes ficam divididas. Material de marketing fortemente desenhado, onde o texto é posicionado por efeito visual em vez de ordem de leitura, converte mal, e nenhuma quantidade de inferência corrige isso

Diagrama do PDF Library for Delphi do empacotamento determinístico de DOCX construído entrada por entrada em Pascal puro ao lado da automação Office cujos metadados incorporados mudam a cada conversão
Duas conversões idênticas geram hashes idênticos para diff e cache, uma propriedade que a automação do Office abre mão no instante em que incorpora carimbos de tempo novos

Documentos digitalizados são um caso totalmente separado. Uma página que é uma única imagem grande não contém nenhum objeto de texto, então não há nada para exportar até que exista uma camada de texto; o caminho de OCR que produz essa camada é um pré-requisito, não uma opção. Antes de rodar um lote grande, amostre uma dúzia de arquivos representativos e observe a saída, e considere enumerar os elementos da página primeiro, como descrito em busca de texto e enumeração de elementos de página, para ver o que as páginas realmente contêm

Para pipelines de assistentes e de retrieval, o caminho de Markdown costuma ser o alvo melhor: títulos viram limites de chunk, tabelas continuam legíveis como tabelas pipe, e os marcadores de página dão a cada chunk uma localização citável. Para edição humana, DOCX é a resposta, porque o que o usuário quer não é o texto, e sim a capacidade de alterá-lo

O PDF Library for Delphi é uma biblioteca de PDF para Delphi, C++Builder e Lazarus com interfaces DLL e ActiveX correspondentes, de modo que as mesmas chamadas de exportação estão disponíveis a partir de C#, C++ ou hosts de script. A documentação completa e uma versão de teste estão na página da biblioteca PDF Library for Delphi Delphi para PDF