Artigo Técnico

Anexos de PDF em Delphi com o PDFium Component: Ler, Adicionar e Eliminar

Os anexos de ficheiros PDF são armazenados na árvore de ficheiros incorporados do documento, uma estrutura que a maioria dos visualizadores apresenta como um painel de clipe de papel ou uma barra lateral de anexos. A partir do código Delphi, o PDFium Component expõe essa árvore por meio de um pequeno conjunto de propriedades indexadas no TPdf: você itera pelo índice inteiro, lê nomes e cargas de bytes, cria novos slots e exclui os existentes. A interface da API é estreita; existem apenas algumas restrições de ordenação e uma regra de higienização que vale a pena conhecer antes de escrever o código de produção correspondente

Lendo anexos a partir de um documento aberto

O AttachmentCount fornece o número de ficheiros incorporados que o documento declara. Ele lê diretamente da chamada subjacente do PDFium, portanto, reflete apenas o que o PDF realmente contém. A partir daí, o AttachmentName[Index] retorna o nome de exibição como um WString, e o Attachment[Index] entrega os bytes brutos como uma matriz TBytes. Ambos são baseados em zero. O documento deve estar aberto (Pdf.Active = True) antes de você consultar qualquer uma das propriedades; chamá-las em um documento fechado retorna zero ou um resultado vazio sem gerar exceções

Uma coisa a ter em mente: o Attachment[Index] aloca e retorna a carga completa do ficheiro em cada leitura. Para um documento que carrega um ativo incorporado grande, iterar por todos os anexos para construir uma lista de exibição significa pagar esse custo de alocação em cada chamada. Se você precisar apenas dos nomes para fins de exibição, leia primeiro o AttachmentName e adie a busca de bytes até que o utilizador realmente solicite o ficheiro

__CODE_BLOCK_

Extraindo um anexo para o disco

Não há um método auxiliar SaveAttachment. Você lê os bytes e os grava onde for necessário, o que coloca a construção e a higienização do caminho inteiramente sob a responsabilidade do seu código. Isso é importante quando os nomes dos anexos vêm de documentos não confiáveis. Os nomes de anexos de PDF são strings armazenadas dentro do ficheiro; eles podem conter separadores de caminho, carateres Unicode semelhantes e outros carateres que produzirão resultados inesperados se você os passar diretamente para o TFileStream.Create. Sempre passe o nome por ExtractFileName antes de criar qualquer caminho de saída, e considere rejeitar nomes que comecem com um ponto ou conterem carateres fora do esperado pelo seu sistema

A matriz de bytes retornada por Attachment[Index] pertence ao chamador. Grave-a com um TFileStream normal e ela será sua para fazer o que desejar, incluindo inspecionar os primeiros bytes para verificar o formato real do ficheiro, em vez de confiar no nome declarado

__CODE_BLOCK_

Adicionando anexos e a gravação em duas etapas

Criar um anexo requer duas chamadas, não uma. O CreateAttachment(Name) registra um novo slot na árvore de ficheiros incorporados e retorna True em caso de sucesso. Esse slot começa vazio. Você então atribui a carga gravando no Attachment[AttachmentCount - 1], visando a entrada criada mais recentemente. Se o CreateAttachment retornar False, o slot não foi criado e a atribuição corromperia o anexo em qualquer índice que estivesse por último

Depois de modificar a lista de anexos, as alterações residem apenas na memória. Chame o SaveAs para gravar um novo ficheiro com a árvore de ficheiros incorporados atualizada. O PDFium Component não suporta guardar de volta no mesmo ficheiro que está aberto no momento, porque o mecanismo mantém um identificador de leitura para a origem. O padrão comum para uma atualização local é guardar em um caminho temporário, fechar o documento, excluir ou renomear o original, depois renomear o ficheiro temporário para a posição correta e reabrir

__CODE_BLOCK_

Informações sobre o tipo de anexo

Além do nome e da carga de bytes, o AttachmentType[Index] retorna a string de tipo MIME armazenada no dicionário de ficheiros incorporados do PDF, se alguma foi registrada quando o ficheiro foi originalmente anexado. Muitos geradores deixam esse campo vazio ou o definem com um valor genérico como application/octet-stream, por isso você não pode confiar nele para detecção de formato em um pipeline de produção. Para uma identificação confiável, leia os primeiros bytes da carga e verifique assinaturas de ficheiro conhecidas: %PDF para um PDF aninhado, o cabeçalho de ficheiro local ZIP PK\x03\x04 para documentos Office Open XML, \xD0\xCF\x11\xE0 para binários legados de ficheiros compostos. As informações de tipo do dicionário são boas para exibir em um rótulo de interface do utilizador, mas não devem orientar decisões de processamento quando você tem os bytes reais disponíveis

Excluindo anexos

O DeleteAttachment(Index) remove a entrada nessa posição e retorna True em caso de sucesso. Após a exclusão, as entradas restantes são deslocadas para baixo, portanto, se você estiver excluindo vários anexos em um loop, deve iterar do último índice para baixo, e não para a frente, para evitar pular entradas após cada deslocamento. A alteração permanece na memória até que você chame o SaveAs

Um cenário comum em pipelines de processamento de documentos é remover todos os anexos de um PDF recebido antes de passá-lo adiante, por motivos de segurança ou tamanho. Conte uma vez antes do loop e itere em ordem inversa:

__CODE_BLOCK_

Onde os anexos de PDF aparecem na prática

A API de anexos funciona em qualquer PDF que o PDFium consiga abrir, mas os documentos onde você realmente encontra ficheiros incorporados se concentram em alguns casos específicos. O PDF/A-3 (ISO 19005-3) permite explicitamente ficheiros incorporados conformes como um mecanismo para agrupar dados de origem junto com a renderização de arquivamento; as faturas eletrônicas ZUGFeRD e Factur-X contam exatamente com esse mecanismo para incorporar uma carga estruturada de XML dentro do layout de PDF legível por humanos. PDFs derivados de correio eletrónico às vezes carregam seus anexos de mensagens originais encaminhados na árvore de ficheiros incorporados. A documentação técnica originada em sistemas de autoria estruturada ocasionalmente agrupa ativos de suporte da mesma maneira

Quando a sua aplicação processa PDFs recebidos de fora da sua organização, vale a pena verificar o AttachmentCount como parte da entrada do documento por dois motivos independentes. Primeiro, os ficheiros incorporados podem conter dados que você deseja extrair e processar, como o XML dentro de um PDF de fatura. Segundo, os ficheiros incorporados podem carregar conteúdo executável arbitrário, de modo que saber o que está presente é importante, mesmo quando você nunca pretende extraí-lo. Nenhum dos motivos exige que você faça algo complexo: leia a contagem, verifique os nomes e decida o que fazer com os bytes

As propriedades de anexos mostradas aqui fazem parte do PDFium Component para Delphi e C++Builder