A redação real de PDF no Delphi significa excluir os bytes subjacentes, não pintar por cima deles. A losLab PDF Library traça essa linha com duas APIs: o RedactRegion edita o fluxo de conteúdo da página para que o texto, vetores e imagens removidos desapareçam fisicamente, e o SanitizeDocument limpa os metadados, scripts e ações onde informações redigidas também se escondem. Se errar em qualquer uma das partes, você enviarà um arquivo que parece censurado na tela, mas revelará o segredo para qualquer pessoa que copiar o texto ou abrir as Propriedades do Documento
O modo de falha é de conhecimento público. Jornais, tribunais e agências governamentais já publicaram PDFs com um retângulo preto sobre um nome ou um número, apenas para um leitor selecionar o texto por baixo, copiá-lo e colar a string supostamente oculta em texto simples. O retângulo era um desenho. O texto ainda estava lá, situado no fluxo de conteúdo uma camada abaixo da pintura, totalmente intacto e selecionável. A losLab PDF Library, o motor de PDF para Delphi e C++Builder exposto através da classe TPDFlib, trata essa distinção como o objetivo central da redação, e não como um detalhe secundário
Por que desenhar uma caixa preta sobre o texto de um PDF não é redação real?
A losLab PDF Library expõe o MaskRect para os casos em que a cobertura é genuinamente o que você deseja: uma marca d'água, um apagão visual, um carimbo de prova. O MaskRect(Page, Left, Top, Width, Height, Red, Green, Blue) anexa um retângulo ao fluxo de conteúdo da página e o pinta com o operador de construção de caminho re seguido pelo operador de preenchimento f (ISO 32000-1 §8.5). Cada byte que já estava na página — os glifos do nome que você cobriu, os traços vetoriais, a imagem incorporada — permanece exatamente onde estava, uma instrução anterior no mesmo fluxo. Um visualizador compatível desenha a caixa preta por último, por isso ela vence visualmente. A cobertura não altera nada por baixo
Qualquer pessoa pode recuperar o conteúdo original de três maneiras: selecionar e copiar o texto diretamente através da caixa opaca, rodar um extrator de texto na página ou excluir o retângulo de cobertura do fluxo de conteúdo. O MaskRect é honesto sobre isso em seu próprio nome — ele mascara, não remove. O resultado é como um adesivo sobre uma palavra que você ainda pode ler simplesmente removendo o adesivo
Como o RedactRegion exclui conteúdo no nível da instrução
O RedactRegion(Page, Left, Top, Width, Height, Options) funciona uma camada abaixo da pintura. A losLab PDF Library analisa cada camada de conteúdo da página em um TPDFContentProgram, percorre a lista de instruções com um analisador de rastreamento CTM (FindInstructionsInRect no modelo de conteúdo) e marca cada operador de exibição de texto, pintura de caminho e Do de imagem cuja área pintada intersecte o seu retângulo. Essas instruções são removidas com o TPDFContentProgram.Delete, e a camada editada é gravada de volta com o WritePageContentLayer. Os bytes não deslizam para trás de uma cobertura — eles saem do fluxo. Um extrator de texto executado posteriormente não encontra nada, porque não há nada para encontrar
Dois detalhes de implementação valem a pena levar para o seu próprio raciocínio. O RedactRegion recebe um retângulo superior esquerdo na origem e unidades atuais e o converte internamente para o espaço do usuário inferior esquerdo que o fluxo de conteúdo utiliza, para que você especifique a região da forma como a vê na página. Quando um retângulo cobre várias instruções, o RedactRegion as exclui em ordem decrescente de índice, porque remover a instrução 4 antes da instrução 7 deslocaria cada índice posterior e corromperia o intervalo. Passe Options = 0 e a losLab PDF Library também estampará uma caixa preta sólida sobre a região limpa como um marcador visível — mas esse marcador é cosmético, aplicado depois que o conteúdo real já desapareceu
Na prática, você raramente codifica rigidamente o retângulo. Você pesquisa na página a string confidencial e lê de volta sua caixa delimitadora — a mesma pesquisa de página e enumeração de elementos coberta em localizando texto e elementos de página no Delphi — e então entrega esse retângulo ao RedactRegion:
var
PDF: TPDFlib;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('contract.pdf', '') = 1 then
begin
// Delete everything painted inside a 220 x 14 pt box on page 1.
// Coordinates are top-left, in the current origin and units.
PDF.RedactRegion(1, 90, 705, 220, 14, 0); // Options=0 stamps a black marker
PDF.SaveToFile('contract-redacted.pdf');
end;
finally
PDF.Free;
end;
end;
Onde as informações confidenciais ainda se escondem em um PDF?
A redação que para na camada visível deixa um rastro, porque um PDF armazena informações em locais onde o leitor nunca olha. A losLab PDF Library agrupa os vazamentos em canais que valem a pena ser nomeados. O Document Information Dictionary e o fluxo de metadados XMP rotineiramente carregam uma string de Author (Autor), Producer (Produtor) ou Keywords (Palavras-chave) que identifica a própria pessoa ou caso que você removeu do corpo. O JavaScript em nível de documento e o OpenAction do catálogo podem ser executados ao abrir e acessar dados que você pensou que tivessem desaparecido. Cada anotação carrega uma ação opcional /A, e a própria anotação — uma nota adesiva, um campo de formulário antigo, um link — pode ficar diretamente sobre o local redigido com sua própria cópia do texto. Nada disso é afetado pela criptografia; um arquivo criptografado com um pacote XMP em texto simples ainda vaza seu título para qualquer indexador, uma lacuna que por si só justifica auditar a criptografia e as permissões. A higienização defensável deve passar por cada um desses esconderijos, não apenas pela página que você estava olhando
Limpando o documento com o SanitizeDocument
A losLab PDF Library responde a essa propagação de vazamentos com o SanitizeDocument(Flags), uma chamada de orquestração que executa um conjunto de remoções independentes selecionadas por uma máscara de bits e retorna o número de remoções aplicadas. As flags são combináveis. O SANITIZE_JAVASCRIPT remove todos os pacotes JavaScript em nível de documento e qualquer script OpenAction. O SANITIZE_ACTIONS remove o dicionário de ação /A de cada anotação, através do novo TPDFAnnots.RemoveAnnotAction, e limpa o OpenAction do catálogo. O SANITIZE_METADATA apaga Autor, Assunto, Palavras-chave, Criador e Produtor tanto no Document Information Dictionary (ISO 32000-1 §14.3.3) quanto no fluxo de metadados XMP (§14.3.2). O SANITIZE_ANNOTATIONS exclui todas as anotações do documento, e o SANITIZE_OPEN_ACTION remove a entrada /OpenAction do catálogo. O SANITIZE_ALL é a união bit a bit de todos os cinco
As convenições de índice dentro dessas remoções são exatamente o tipo de coisa que quebra silenciosamente um loop manual, o que explica bastante o motivo de o SanitizeDocument existir como uma única chamada. O GlobalJavaScriptPackageName é baseado em 0, e o RemoveGlobalJavaScript reduz a lista conforme avança, de modo que a remoção de JavaScript sempre lê o índice de pacote 0 e remove até que a contagem de pacotes chegue a zero. O DeleteAnnotation, por outro lado, é baseado em 1. O AnnotationCount relata em relação à página selecionada no momento, de modo que a remoção de anotações seleciona cada página por vez antes de contar. A losLab PDF Library acerta esses limites internamente, permitindo que você passe um conjunto de flags e leia uma contagem de volta em vez de reproduzir cinco regras de iteração diferentes
O fluxo completo de redação e higienização
Junte os dois e o procedimento é curto. A losLab PDF Library remove o conteúdo visível com o RedactRegion, página por página, e então limpa todos os canais ocultos com SanitizeDocument(SANITIZE_ALL) antes de o arquivo ser gravado. Como a redação quase sempre é executada em documentos que vieram de fora do seu controle, vale a pena carregá-los através de um caminho protegido — a mesma postura defensiva descrita em análise segura de PDFs não confiáveis — para que uma entrada malformada falhe de forma limpa, em vez de parar no meio do processo de redação
var
PDF: TPDFlib;
Applied: Integer;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('incoming.pdf', '') = 1 then
begin
PDF.RedactRegion(1, 90, 705, 220, 14, 0); // delete the sensitive line
Applied := PDF.SanitizeDocument(SANITIZE_ALL); // clear metadata, JS, actions, annots
PDF.SaveToFile('published.pdf');
end;
finally
PDF.Free;
end;
end;
Onde a redação do fluxo de conteúdo para
O RedactRegion opera no fluxo de conteúdo, que é exatamente onde o texto nativamente digital reside e onde o texto digitalizado não reside. Quando uma página é uma imagem digitalizada, as palavras são pixels dentro de um XObject de imagem, não operadores de texto, e a losLab PDF Library não pode extrair um nome do meio de um raster da mesma forma que exclui um glifo. Seu manuseio de imagem é deliberadamente conservador: qualquer imagem cuja inserção intersecte o retângulo é removida por inteiro, de modo que uma caixa de redação em uma página digitalizada leva o escaneamento inteiro com ela em vez de apenas um trecho. Redigir uma região dentro de uma imagem significa rasterizar, pintar e reincorporar no nível do pixel, o que é uma operação diferente da exclusão do fluxo de conteúdo. A mesma cautela se aplica a uma camada de texto OCR sobre uma página digitalizada: o RedactRegion remove os operadores de texto que consegue ver, mas a imagem abaixo permanece intocada, a menos que seu XObject de imagem também intersecte a caixa
Dois pontos mais detalhados mantêm os resultados corretos. O teste de interseção estima a largura de cada trecho de texto a partir de sua contagem de caracteres e tamanho de fonte, em vez de medir as métricas exatas do glifo, portanto uma palavra muito curta definida em uma fonte proporcional pode passar por um retângulo desenhado de forma muito apertada — dê à caixa uma pequena margem. A exclusão também é granular por instrução: o RedactRegion remove operadores de exibição de texto inteiros, de modo que um retângulo que corta apenas parte de um trecho pode levar caracteres adjacentes que compartilhavam o mesmo operador. Nenhum dos limites é motivo para desconfiar do mecanismo, apenas para limitar o retângulo com cuidado e verificar o resultado
A disciplina que encerra todo fluxo de trabalho de redação é a verificação, não a confiança. Redija a região, higienize o documento, salve em um novo arquivo, abra-o novamente e tente extrair tanto o texto que você removeu quanto os metadados que limpou; quando ambos retornarem vazios, a redação funcionou. As APIs RedactRegion e SanitizeDocument mostradas aqui são fornecidas com a losLab PDF Library para Delphi e C++Builder, juntamente com a referência completa do modelo de conteúdo e da higienização