Skip to content

Practica Documentada

pmmollinedo edited this page Mar 2, 2017 · 10 revisions

Bienvenidos a la wiki de TRAPdf!

A lo largo de esta página queremos enseñaros como fue nuestro trabajo desarrollando TRAPdf y cual es su estado actual.

FORMULARIO

Primero creamos un formulario para subir el archivo pdf al servidor

<form enctype="multipart/form-data" action="engine.php" method="post">
<input type="text" class="form-control input-ok" style="text-align:center" value="Seleccione un pdf" readonly>

Y así se referencia en el php que hace todo

// usamos move_uploaded_file para cambiarle el nombre temporal del archivo subido a uno con extensión pdf
move_uploaded_file($_FILES['src-pdf']['tmp_name'], 'document.pdf');

PDF -> HTML

Una vez conseguimos que el formulario enviase el documento pdf a una variable buscamos como convertir el pdf en html, esto lo con seguimos con http://www.pdfparser.org/ usando sus librerías y clases conseguimos convertir pdf en una variable html pero no guardaba el formato de los backgrounds, imágenes, figuras etc... después de mucho buscar al final encontramos https://github.com/mgufrone/pdf-to-html que era una herramienta muy potente que guardaba el formato de todo el pdf y lo guardaba en una variable HTML.

// CONFIGURACION DEL CONVERTIDOR A HTML
use Gufy\PdfToHtml\Config;
Config::set('pdftohtml.bin', 'C:/poppler-0.51/bin/pdftohtml.exe');
Config::set('pdfinfo.bin', 'C:/poppler-0.51/bin/pdfinfo.exe');
// buscar el archivo pdf
$pdf = new Gufy\PdfToHtml\Pdf('document.pdf');
// sacas el total de paginas
$total_pages = $pdf->getPages();
// conviertes el pdf en HTML de la pagina que quieras
$html = $pdf->html($page);

TRADUCCION

Para la traducción queríamos usar la API de Google directamente pero como es de pago tuvimos que buscar alguna clase de php que hiciera esta función, encontramos https://github.com/Stichoza/google-translate-php con el cual usamos la API de google indirectamente y de manera gratuita.

use Stichoza\GoogleTranslate\TranslateClient;
// NULL significa idioma de origen automatico y el $_POST["lang"] es el idioma destino que le pasa el formulario del index
$tr = new TranslateClient(null, $_POST["lang"]);
// le dices que variable va a traducir
$p = $tr->translate($p);

HTML -> PDF

Esta fue una de las partes mas complicadas pues todas las clases que probábamos no componían bien el css de el html traducido, probamos muchas: FPDF, TCPDF, Dompdf, etc... al final después muchas horas encontramos https://github.com/mikehaertl/phpwkhtmltopdf que encajaba a la perfección en nuestra pagina respetando el css.

// La configuración inicial es un poco liosa pero no hay que tocar nada es para que sepa donde esta sus librerías ignorar
// advertencias dar permiso de shell y demas
use mikehaertl\wkhtmlto\Pdf;
$pdf2 = new Pdf(array(
    'binary' => 'C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe',
    'ignoreWarnings' => true,
    'commandOptions' => array(
        'useExec' => true,      // Can help if generation fails without a useful error message
        'procEnv' => array(
            // Check the output of 'locale' on your system to find supported languages
            'LANG' => 'en_US.utf-8',
        ),
    ),
));
// mete la salida traducida en un archivo HTML, uno por cada página
file_put_contents('translated' . $page . '.html', $html2);
// añade una pagina pdf a la variable que compone el pdf
$pdf2->addPage('localhost/TRAPdf/translated' . $page . '.html');
// guarda el resultado en un archivo pdf
$pdf2->saveAs('TRAPdf-translated.pdf');

Problemas con el traductor

al traducir un html con todas las etiquetas, Google lo interpreta todo como texto y estropeaba las etiquetas porque intentaba traducirlas para eso usamos una solución un poco liosa y que explicaremos paso a paso pero que en teoría es convertir el html en DOM y buscar las etiquetas p, traducirlas y reemplazarlas. Primero convertimos las etiquetas br a AAAA porque son las únicas que están dentro de las etiquetas p y Google las traduciría

$html = str_replace('<br>', ' AAAA ', $html);

creamos un documento DOM y cargamos en el HTML

$dom = new DOMDocument;
$dom->loadHTML('<?xml version="1.0" encoding="UTF-8"?>' . $html); 

buscamos las etiquetas p en el html y las metemos en un array

$b = $dom->getElementsByTagName('p');

Y por cada etiqueta p hace una traducción de lo que contiene y la reemplaza en el DOM

foreach ($b as $p)
{
    $tr = new TranslateClient(null, $_POST["lang"]);
    $p->nodeValue = $tr->translate($p->nodeValue);
}

guardamos el DOM en una variable como HTML y volvemos a convertir las AAAA en br

$html2 = $dom->saveHTML();
$html2 = str_replace('AAAA', ' <br> ', $html2);

Como traducir varias paginas

para hacer la traducción de varias paginas hay que hacer un bucle que vaya sacando el texto de cada pagina lo traduce como hemos visto antes crea un archivo HTML y añade la pagina al pdf mientras que la pagina que va sea menor o igual que el numero de paginas del documento pdf.

$page='1';
do
{
// aquí iría todo el proceso mencionado antes que es bastante largo y se puede ver en el engine.php 
$page++;
}
while ($page <= $total_pages);

Proceso de descarga del archivo

Durante la traducción y guardado del PDF se producen una serie de sucesos en modo gráfico que esconden sucesos fuera de la vista del usuario final.

Después de seleccionar el pdf y el idioma se nos presenta dentro del mismo index.html un barra de progreso

<div class="progreso" style="display:none">
    <h1 class="cover-heading">YOUR FILE IS TRANSLATING.</h1>
    <p class="lead">If you close that window the translate will be cancelled.</p>
    <div class="progress">
        <div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%">
                <span>It could be take a few minutes.</span> </div>
        </div>
        <a class="btn btn-lg btn-dark" href="index.html"> <i class="fa fa-upload" aria-hidden="true"></i> &nbsp;Cancel </a>
</div>

la cual está oculta y se muestra al pulsar el botón para enviar el formulario, gracias al script que está situado al final del index.

$(".submit-form").click(function (a) {
    //coge los valores del name
    var btn = $(".input-ok").val(),
              select_lang = $(".select-ok").val();
    //Comprobación de formulario completo
    if (btn == "Seleccione un pdf" && select_lang == "none") {
        // Muestra las alertas
        a.preventDefault();
        $(".alert-danger-input").css('display', '')
        $(".alert-danger-select").css('display', '')
    } else if (btn == "Seleccione un pdf") {
        a.preventDefault();
        $(".alert-danger-input").css('display', '')
    } else if (select_lang == "none") {
        a.preventDefault();
        $(".alert-danger-select").css('display', '')
    } else {
        // Oculta el formulario y muestra la barra de progreso si todo está correcto 
        $(".formulario").css('display', 'none')
        $(".progreso").css('display', '')
    }
});

Esto que se ejecuta en el entorno gráfico, en sí solo es a modo decorativo, ya que la barra de progreso se mostraría indefinidamente. Lo que se hace para que deje de aparecer el progreso es una redirección a otra página (descargado.html) desde el engine.php

header("Location: descargado.html");

Dentro de esta página encontramos un botón para descargar el archivo.

<a href="descarga.php?pdf-file=TRAPdf-translated.pdf">Download</a>

Como se observa, en realidad es un enlace que redirecciona a un fichero php con una petición GET del fichero que se quiere descargar. El script de descarga consiste en enviar una petición HTTP mediante una serie de cabeceras HTTP usando la función header() de PHP que solo funciona si no se ha enviado nada al flujo de salida.

Las cabeceras de la respuesta HTTP que vamos a enviar son:

– Content-Type: Tipo_de_contenido

– Content-Disposition: attachment; filename=nombre_del_fichero

– Content-Transfer-Enconding: binary

– Content-Length: tamaño_del_fichero

<?php
// Si la variable archivo que pasamos por URL no esta 
// establecida acabamos la ejecución del script.
if (!isset($_GET['pdf-file']) || empty($_GET['pdf-file'])) {
   exit();
}

// Se utiliza basename por seguridad
// Devuelve el nombre del archivo eliminando cualquier ruta. 
$archivo = basename($_GET['pdf-file']);


// Se verifica que el archivo existe
// Se envian las cuatro cabeceras y se usa readfile()
// que devuelve el contenido del archivo por el flujo de salida
if (is_file($archivo)) {
   header('Content-Type: application/pdf');
   header('Content-Disposition: attachment; filename='.$archivo);
   header('Content-Transfer-Encoding: binary');
   header('Content-Length: '.filesize($archivo));

   readfile($archivo);
} else {
    exit();
}

unlink( 'document.pdf');
exec( 'del C:\xampp\htdocs\TRAPdf-master\TRAPdf-translated.pdf /Q');
?>

Optimizaciones en la velocidad del código

añadir la función unset conforme las variables no se van a usar mas, cambiar bucle while por do-while que son mas rápidos, sustituir todas las comillas dobles por simple, uso de funciones de benchmark para ver cuanto tiempo duraba el proceso. todo estos consejos los sacamos de: <https://www.emezeta.com/articulos/funciones-php-optimizar-codigo

Servidor en el que tenemos instalado TRAPdf

lo instalamos en Windows 7 con XAMPP porque en Windows Server no funciona bien php ni aunque lo instales de 1000 maneras. Cambios en Windows: priorizar el rendimiento en todas los servicios apache, PdfToHtml por encima de las aplicaciones en primer plano. deshabilitar la mayor parte de las características de Windows, usar el mismo directorio para XAMPP y Github para una actualización de la web inmediata, instalación de prio para guardar la prioridad alta dada a los procesos que usan las herramientas de TRAPdf, en las opciones avanzadas de Windows configurarlo todo para priorizar rendimiento. server