HTML Unifier

HTML Unifier

Hoje eu passarei para vocês um conhecimento proibido. Um conhecimento milenar e obscuro dos programadores web,  Uma técnica capaz de reverter o que na maioria dos casos são consideradas boas práticas no desenvolvimento web. Estou falando de Data URI.

Se você já criou alguma página web em HTML você sabe como imagens são adicionadas. Não só imagens, mas muitos arquivos são necessários para ajudar o HTML a compor a página que vemos. Folhas de estilo, javascript, videos... O Data URI Scheme foi criado para permitir embutir diretamente no código HTML um conteúdo que deveria estar separado em seu próprio arquivo.

Como assim? No normal, voocê tem uma imagem praia.jpg. Com Data URI nessa imagem, o arquivo não é mais necessário. A imagem é convertida para ASCII e incluida dentro do HTML, na tag que faz a imagem.

Andei vasculhando os editores de HTML livres e não achei nenhum que permitisse incluir uma imagem nesse modo. Nem Nvu/Kompozer/BlueGriphon, nem LibreOffice, nem os editores de código... Como disse no começo, a técnica é "proibida". Se você adiciona imagens diretamente no arquivo da página, toda aquela modularidade vai pro saco. O cache não poderá ser feito de maneira adequada, por exemplo.

Bom, então por que você quis fazer isso? Simples: precisei preparar email em HTML com imagens, de uma maneira legal, através de um serviço que não aceita anexos. E imagens hospedadas geralmente não são lidas por alguns clientes de email, que temem que haja fraudes ou sei lá o quê. Solução? Data URI.

Pesquisei e não encontrei uma solução para isso. Terminei tendo que criar uma. Assim, apresento o HTMLunifier, que escrevi em PHP CLI, usando DOM e DataURI. Claro que você sabia que o PHP pode ser usado pra fazer script que rode no shell, não é?

O que o HTMLunifier faz basicamente é abrir um arquivo HTML e substituir todas as imagens por Data URI. Por segurança, ele não substitui o arquivo, mas cria um novo com terminação -u.html.

Convertendo a imagem para Data URI

Este código eu peguei na página do criador da biblioteca DataURI para PHP, que utilizei. Claro, com algumas melhorias.

function image_to_html($imagefile) {
	if (preg_match("/.jp(e?)g$/i", $imagefile)) {
		$filetype = "image/jpg";
	} elseif (preg_match("/.png$/i", $imagefile)) {
		$filetype = "image/png";
	} else {
		return $imagefile;
	}
	$fileContents = file_get_contents($imagefile);

	if($fileContents != false) {
		$dataUri = new DataUri(
			$filetype,
			$fileContents,
			DataUri::ENCODING_BASE64
	);

	return $dataUri->toString();
}

O processo unificador

Já temos o método que converte imagem em Data URI. Agora precisamos varrer o HTML procurando pelas tags img. Isso é feito com o seguinte código:

function unify($f) {
	$fpath = dirname(realpath($f));
	$dom = new DomDocument();
	$dom->loadHTML(file_get_contents($f));
	$dom->preserveWhiteSpace = false; 
	$images = $dom->getElementsByTagName('img'); 
	foreach ($images as $img) {    
		$imgfile = $img->getAttribute('src');
		if (preg_match("/^http(s?):/i", $imgfile)) {
			$img->setAttribute('src', image_to_html($imgfile));
		} elseif (preg_match("/^file:/", $imgfile)) {
			$rimgfile = preg_replace('/^file:(\/+)/', '/', $imgfile);
			$img->setAttribute('src', image_to_html($rimgfile));
		} else {
			$rimgfile = "$fpath/$imgfile";
			$img->setAttribute('src', image_to_html($rimgfile));
		}
    } 
    $f2 = $fpath . "/" . basename($f, ".html") . "-u.html";
    $dom->save($f2);
}

Se o arquivo está com protocolor http, https ou file, o tratamento é diferente do endereçamento relativo. No fim, o novo arquivo é gerado.

O programa em si

Claro, temos as funções, agora é hora de escrever o programa que as use. Isso é simples e curto:

for ($i = 1; $i < sizeof($argv); $i++) {
	$f = $argv[$i];
	if (file_exists($f)) {
		print "I will transform $f.\n";
		unify($f);
	} else {
		print "File $f don't exists.\n";
	}
}

Corrigindo o Doctype

Isso por si já deve funcionar, mas "vai que"... Então também fiz um método-gambiarra que corrige o Doctype do arquivo novo:

function fix_doctype($f) {
	$af = file($f);
	print "fix\n";
	$sf = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" .
		"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
	foreach ($af as $s) {
		if (preg_match("/^\<\?xml/i", $s)) {
			print "achei xml\n";
		} elseif (preg_match("/^\<\!DOCTYPE/i", $s)) {
			print "achei doctype\n";
		} else {
			$sf .= "\n$s";
		}
	}
	file_put_contents($f, $sf);
}

Concluindo

Enfim, é isso. Espero que isso seja útil para você. Estou anexando neste artigo o código. Como não posso enviar PHP, estou enviando o código em TXT. Basta salvar renomeando o arquivo para tirar o ".txt" ou selecionar, copiar e colar num editor de textos. O arquivo precisa ser marcado como executável.

E lembre-se: você precisará do php5-cli (ou talvez não), do php-xml-algumacoisa (na dúvida dei apt-get install php-xml-*) e do DataURI.php (coloquei na mesma pasta do HTMLunifier). Muita sabedoria para usá-lo e bom proveito!

AnexoTamanho
Plain text icon htmlunifier.php_.txt2.02 KB
Special: 
Avalie: 
Average: 3.4 (14 votes)

Comentar


Warning: PHP Startup: Unable to load dynamic library '/opt/php56/lib/php/extensions/no-debug-non-zts-20131226/pdo.so' - /opt/php56/lib/php/extensions/no-debug-non-zts-20131226/pdo.so: cannot open shared object file: No such file or directory in Unknown on line 0

Warning: PHP Startup: Unable to load dynamic library '/opt/php56/lib/php/extensions/no-debug-non-zts-20131226/pdo_mysql.so' - /opt/php56/lib/php/extensions/no-debug-non-zts-20131226/pdo_mysql.so: cannot open shared object file: No such file or directory in Unknown on line 0

Warning: PHP Startup: Unable to load dynamic library '/opt/php56/lib/php/extensions/no-debug-non-zts-20131226/php_pdo_odbc.dll' - /opt/php56/lib/php/extensions/no-debug-non-zts-20131226/php_pdo_odbc.dll: cannot open shared object file: No such file or directory in Unknown on line 0