Destrinchando o PDF com PDFLang
Na tela, um PDF parece uma coisa só e chapada, uma foto de uma página. Não é. É uma caixa de peças, e quase toda pergunta que se faz sobre um arquivo — isso vai imprimir direito? está faltando alguma coisa? — é na verdade uma pergunta sobre uma dessas peças.
O PDFLang te dá cinco maneiras de alcançar o que está dentro. Quatro já estão ali quando o seu script começa. A quinta você desenha.
O documento
Seção intitulada “O documento”Todo script começa com uma variável chamada doc. Não tem nada para abrir nem
carregar: doc é o arquivo que está sendo conferido.
check "O que eu estou olhando" { print("arquivo:", doc.filename) print("páginas:", doc.page_count) print("título:", doc.title)}doc.title e doc.author vêm dos metadados do arquivo — a informação que o PDF
carrega sobre si mesmo e que você nunca vê na página. Costuma estar vazia,
porque quem exportou o arquivo nunca preencheu. Isso já vale uma verificação:
check "O arquivo diz o que ele é" { assert doc.title != "", "o PDF não tem título nos metadados"}O doc também guarda três listas: doc.pages, doc.fonts e doc.images. São
listas comuns, então tudo que você faz com uma lista funciona nelas.
As páginas
Seção intitulada “As páginas”doc.pages entrega as páginas uma a uma. Cada uma sabe o próprio tamanho,
quanta tinta carrega e quais dos seus boxes estão definidos.
Tamanhos vêm em pontos, a unidade em que o PDF pensa: 72 pontos por polegada,
então uma página A4 tem 595 × 842. Mas você não precisa pensar em pontos —
escreva 210mm ou 8.5in e a comparação funciona:
check "Tudo em A4" { doc.pages.each { |page| assert page.width > 200mm, "a página #{page.number} está estreita demais: #{page.width}pt" }}Repare no page.number. As páginas contam a partir de 1, como na conversa, então
o número que aparece numa mensagem de erro é a página que a pessoa vai procurar.
E tem os boxes. Uma página pronta para impressão carrega vários retângulos que dizem o que fazer com ela: o TrimBox é onde a guilhotina corta, e o BleedBox é a área um pouco maior onde a cor precisa passar do corte, para que um milímetro de desvio não deixe uma borda branca. Um arquivo sem eles pode parecer perfeito na tela e ser inútil na gráfica:
check "Pronto para a guilhotina" { doc.pages.each { |page| assert page.has_trim_box, "a página #{page.number} não tem TrimBox" assert page.has_bleed_box, "a página #{page.number} não tem BleedBox" }}A página também informa o page.tac: quanta tinta cai no ponto mais carregado
dela, em porcentagem. Quatro tintas na força total dariam 400%, que nenhuma
máquina aceita; 300% é um teto comum.
As fontes
Seção intitulada “As fontes”doc.fonts lista as tipografias que o arquivo usa, e cada uma responde a uma
única pergunta importante: ela está embutida?
Uma fonte embutida viaja dentro do PDF. Uma fonte que não está embutida é apenas citada pelo nome, e a máquina que abre o arquivo substitui por alguma que tenha. O texto não some — ele muda de forma. As linhas quebram em outros lugares, um título que cabia em uma linha passa a ocupar duas, e ninguém percebe até estar impresso.
check "Toda fonte viaja com o arquivo" { doc.fonts.each { |font| assert font.is_embedded, "a fonte '#{font.name}' não está embutida" }}As imagens
Seção intitulada “As imagens”doc.images entrega todas as imagens do arquivo, e page.images as de uma
página específica. A propriedade que vale entender é o dpi.
DPI é pontos por polegada — quanto detalhe existe em cada polegada impressa. O PDFLang não informa o número guardado nos metadados da imagem. Ele informa a resolução efetiva: os pixels que a imagem realmente tem, divididos pelo tamanho em que ela é impressa. Estique uma foto de 1000 pixels por 20 centímetros e ela é uma imagem de baixa resolução, diga o que disser sobre si mesma. É esse número que decide se a impressão sai nítida.
check "Nítida o bastante para imprimir" { doc.images.each { |img| assert img.dpi >= 300, "imagem na página #{img.page_number}: #{img.dpi} DPI, precisa de 300" }}O img.color_space é o outro que as pessoas procuram. O offset trabalha em
CMYK; uma imagem que ainda está em RGB precisa ser convertida, e a conversão
desloca as cores. Melhor descobrir antes de gravar as chapas.
As áreas que você desenha
Seção intitulada “As áreas que você desenha”As quatro anteriores vêm com o arquivo. A quinta não: uma region é um retângulo que você define, para poder perguntar sobre uma parte da página em vez da página inteira.
rodape = region(0, 0, 595, 60, "rodapé")tarja = region(20mm, 250mm, 60mm, 15mm, "tarja de advertência")A única coisa que você precisa lembrar é onde fica a origem. Num PDF, 0, 0 é o
canto inferior esquerdo da página, não o superior. O Y cresce para cima.
As regions são o que permite escrever as regras que de fato importam num trabalho. Não “esse arquivo contém o texto legal”, mas “o texto legal está na tarja reservada para ele”:
check "Tarja de advertência" { tarja = region(0, 700, 595, 142, "tarja") conteudo = text::extract_from_region(1, tarja) assert conteudo.contains("VENDA SOB PRESCRIÇÃO"), "a tarja está sem o texto obrigatório"}Regions também se comparam. rodape.intersects(cabecalho) diz se duas áreas se
sobrepõem, que é como se pega um elemento que invadiu espaço reservado para
outra coisa. E rodape.expand(5mm) e rodape.inset(3mm) devolvem uma cópia
mais folgada ou mais apertada, para dar tolerância.
Onde olhar depois
Seção intitulada “Onde olhar depois”O capítulo de tipos do documento lista todas as propriedades e métodos dos cinco, com exemplos de cada um. É material de referência — leia quando precisar de algo específico, não do começo ao fim.
Se você quiser ver os cinco funcionando juntos em trabalhos reais, as receitas e os exemplos são o lugar.