O PDFlibPas converte conteúdo PDF em dois formatos editáveis sem automação do Office. ExportPageMarkdown e ExportDocumentMarkdown devolvem Markdown semântico com títulos inferidos, listas ordenadas e não ordenadas e tabelas em formato pipe, enquanto SaveDOCXToFile e SaveDOCXToStream escrevem um pacote WordprocessingML contendo parágrafos, títulos, numeração de listas nativa, tabelas detetadas, estilo tipográfico, quebras de página e imagens PNG posicionadas
Ambos funcionam inteiramente em Pascal, num servidor, sem Word instalado e sem COM. Essa restrição é a razão pela qual esta funcionalidade existe numa biblioteca PDF e não numa ferramenta de ambiente de trabalho
Porque é "PDF para Word" genuinamente difícil?
Porque uma página PDF não contém parágrafos. Contém operadores de exibição de texto que colocam sequências de glifos em coordenadas, pela ordem em que o produtor as emitiu, sem qualquer obrigação de indicar que duas sequências pertencem à mesma frase, quanto mais ao mesmo item de lista. O formato foi concebido para descrever uma página impressa com exatidão, e consegue-o precisamente por descartar a estrutura que produziu a página
Por isso, cada conversor tem de reconstruir aquilo que o gerador descartou. O agrupamento de linhas provém do espaçamento vertical e do alinhamento da linha de base. Os limites de parágrafo provêm de mudanças de espaçamento e de indentação. Um título é uma linha cujo tipo de letra é maior ou mais forte do que o corpo do texto e que se destaca do que se segue. Uma lista é uma sequência de parágrafos que começam por um carácter de marcador ou um padrão numérico. Uma tabela é uma grelha de blocos de texto cujos limites se alinham entre linhas e colunas. Cada um destes casos é uma inferência, e inferência significa um bom resultado em documentos que seguem convenções tipográficas comuns e um resultado medíocre naqueles que não seguem
Os PDFs etiquetados são a exceção, e uma exceção considerável. Quando o documento transporta uma árvore de estrutura, os papéis de parágrafo, título, lista e tabela ficam registados em vez de adivinhados, e é por isso que o trabalho de acessibilidade descrito em estrutura de acessibilidade de PDF etiquetado também compensa em qualidade de conversão. Se controla o produtor, etiquetar a sua saída é a ação de maior impacto que pode tomar em benefício de quem mais tarde tiver de a converter
Exportação para Markdown, página a página
O caminho de Markdown é o indicado quando o destino é um fluxo de texto: um site de documentação, um índice de pesquisa, um corpus de recuperação para um assistente. As opções são uma máscara de bits: PDF_MARKDOWN_INCLUDE_PAGE_MARKERS, PDF_MARKDOWN_DETECT_HEADINGS, PDF_MARKDOWN_PRESERVE_STYLES, com PDF_MARKDOWN_DEFAULT a combinar as três
var
Pdf: TPDFlib;
Md: WideString;
begin
Pdf := TPDFlib.Create;
try
Pdf.LoadFromFile('handbook.pdf', '');
// Uma página, como cadeia de texto
Md := Pdf.ExportPageMarkdown(1, PDF_MARKDOWN_DEFAULT);
// Um intervalo de páginas, transmitido em fluxo para disco 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 justificam-se plenamente em trabalho de recuperação. Um fragmento de texto que transporta 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 se encontra. Desative-os quando o Markdown se destina à leitura humana, contexto em que os limites de página do esquema original são apenas ruído
Os pontos de entrada de escrita em fluxo importam para documentos extensos. SaveMarkdownToStream e SaveMarkdownToFile escrevem UTF-8 uma página de cada vez e não colocam em buffer a saída completa, pelo que um manual de 900 páginas não se transforma primeiro numa cadeia de texto de 900 páginas em memória. A ausência de uma marca de ordem de bytes também é deliberada: um BOM num ficheiro Markdown confunde um número surpreendente de geradores de sites estáticos e de ferramentas de comparação
DOCX sem Office na máquina
O escritor de DOCX produz o próprio pacote: entradas ZIP escritas como Deflate em bruto com verificações CRC, as partes WordprocessingML e as relações que as unem. Nada invoca o Word, o que significa que a conversão corre num servidor sem interface gráfica, dentro de uma conta de serviço, num contentor, em todos os locais onde a automação do Office é não licenciada, 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 escritos à medida que cada página é processada, em vez de recolhidos e anexados no final, pelo que 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 PDF selecionada é restaurada depois, o que importa quando a exportação é apenas um passo dentro de um trabalho mais longo que tinha uma página selecionada por outros motivos
O que traz o empacotamento determinístico?
Reprodutibilidade byte a byte. Duas conversões da mesma entrada com as mesmas opções produzem o mesmo pacote, o que significa que pode calcular o hash da saída para detetar alterações, comparar duas compilações de um documento gerado e utilizar cache de forma agressiva sem se preocupar que uma entrada idêntica tenha produzido um artefacto diferente
A automação do Office não consegue prometer isso. Incorpora marcas temporais, identificadores de revisão e metadados dependentes da máquina, pelo que o mesmo documento convertido duas vezes difere de formas que impedem o cálculo de hash. A mesma lógica orienta os identificadores de ficheiro determinísticos discutidos em identificadores de PDF determinísticos para compilações reprodutíveis: quando a saída é reprodutível, a verificação passa a ser uma comparação em vez de uma inspeção
Onde o resultado é bom, e onde não é
Seja honesto com os seus utilizadores quanto a isto, porque a qualidade da conversão varia mais com a entrada do que com o conversor. PDFs etiquetados e documentos comerciais gerados de forma limpa, faturas, relatórios, contratos, convertem-se bem: os títulos ficam como títulos, as tabelas sobrevivem, as listas renumeram corretamente no Word. Os esquemas académicos de duas colunas convertem-se de forma aceitável se a geometria das colunas for regular. As tabelas que atravessam quebras de página são reconstruídas por inferência e por vezes divididas. Material de marketing muito trabalhado esteticamente, onde o texto é colocado por efeito visual e não pela ordem de leitura, converte-se mal, e nenhuma quantidade de inferência corrige isso
Os documentos digitalizados são um caso totalmente à parte. Uma página que é uma única imagem não contém objetos de texto, pelo que não há nada a exportar até existir uma camada de texto; o caminho de OCR que a produz é um pré-requisito, não uma opção. Antes de executar um lote grande, analise uma dúzia de ficheiros representativos e observe o resultado, e considere enumerar primeiro os elementos da página, conforme descrito em pesquisa 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 recuperação, o caminho de Markdown é geralmente o melhor alvo: os títulos tornam-se limites de fragmento, as tabelas mantêm-se legíveis como tabelas pipe e os marcadores de página dão a cada fragmento uma localização citável. Para edição humana, o DOCX é a resposta, porque o que o utilizador quer não é o texto, mas sim a capacidade de o alterar
O PDFlibPas é uma biblioteca PDF para Delphi, C++Builder e Lazarus com interfaces DLL e ActiveX correspondentes, pelo que as mesmas chamadas de exportação estão disponíveis a partir de C#, C++ ou motores de scripting. A documentação completa e uma versão de avaliação encontram-se na página da biblioteca PDF PDFlibPas para Delphi