Guía 59 – Convertir HTML a Documento Word

Es relativamente frecuente que tengamos textos en HTML y que necesitemos convertir esos textos a documentos, en este caso, documentos Word.

He creado múltiples soluciones para producir facturas, informes, etc., en Word, pero siempre partiendo de texto «plano», no texto enriquecido como es el caso del HTML.

Objetivo

Capturar texto en formato HTML y después, crear un documento Word, pasando ese texto de HTML al documento Word.

DEMO: https://fhumanes.com/html_word/

Solución Técnica

Como en los últimos artículos, he utilizado PHPWord  https://github.com/PHPOffice/PHPWord Composición de los documentos Word partiendo de plantillas, pero este caso he tenido que investigar, pues no encontré solución en internet para convertir el HTML en el conjunto de elementos del documento Word y una vez obtenidos los elementos, pegarlos en el documento Plantilla.

Como el número de elementos de conversión depende del contenido del HTML para estos campos he definido bloques y los he «clonado» tantas veces cómo elementos de conversión he obtenido.

Tenemos que tener en cuenta que no todo el HTML es válido para este tipo de conversión. Algunos de los elementos que no son válidos son:

  • Enlaces de URL
  • Imágenes, videos u objetos externos al fichero HTML.
  • H1, …, Al no disponer de hoja de estilo, tampoco hay una buena transformación
  • Otros pendientes de descubrir.

En el fichero «PeticionWord.php«, está todo el código, que es:

<?php
@ini_set("display_errors","1");
@ini_set("display_startup_errors","1");

require_once("../include/dbcommon.php");

// Load the PHPWord library classes
require_once __DIR__ . '/phpword_1.0.0/autoload.php'; 
use PhpOffice\PhpWord\Element\TextRun;
use PhpOffice\PhpWord\Element\Section;

// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

$id= $_SESSION['id'] ; // identificación de la Petición

$decimal = new \NumberFormatter("es-ES", \NumberFormatter::DECIMAL); 
$decimal->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, 2);
$decimal->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, 2); // by default some locales got max 2 fraction digits
$entero = new \NumberFormatter("es-ES", \NumberFormatter::DECIMAL);
$entero->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, 0);
$entero->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, 0); 

// Template processor instance creation
$template_word = __DIR__.'/TemplatePetition.docx';
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor($template_word);

// -------------------- ^ cabecera necesaria para las plantillas de Word ------------------
$sql="SELECT id, name, date, expose, request FROM html_word WHERE id = $id";
$resql=DB::Query($sql);
$data=$resql->fetchAssoc();

$templateProcessor->setValue('name', $data['name']);  

// Date Local completed
$myDate = DateTime::createFromFormat('Y-m-d', $data['date']);
$formatter = new IntlDateFormatter('es_ES', IntlDateFormatter::LONG, IntlDateFormatter::LONG);
$formatter->setPattern("d 'de' MMMM 'de' yyyy");
$myDate = $formatter->format($myDate);
$templateProcessor->setValue('date', $myDate);

// ------------------------------HTML "expose" -------------------------------------
$phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection();
\PhpOffice\PhpWord\Shared\Html::addHtml($section, $data['expose'], false, false);
$elements_ar = $section->getElements();
$count = count($elements_ar); // Número de elementos generados por el HTML
$templateProcessor->cloneBlock('BEXPOSE',$count, true, true);

for ($i = 1; $i <= $count; $i++) {
    $tag = 'expose#'.$i;
    $templateProcessor->setComplexBlock($tag , $elements_ar[$i-1]);
}
// ------------------------------HTML "request" -------------------------------------
$section2 = $phpWord->addSection();
\PhpOffice\PhpWord\Shared\Html::addHtml($section2, $data['request'], false, false);
$elements_ar = $section2->getElements();
$count = count($elements_ar); // Número de elementos generados por el HTML
$templateProcessor->cloneBlock('BREQUEST',$count, true, true);

for ($i = 1; $i <= $count; $i++) {
    $tag = 'request#'.$i;
    $templateProcessor->setComplexBlock($tag , $elements_ar[$i-1]);
}

$temp_file = tempnam(sys_get_temp_dir(), 'Word');
$templateProcessor->saveAS($temp_file);

// ------------------ Operation with file result -------------------------------------------
$document = file_get_contents($temp_file);
unlink($temp_file);  // delete file tmp
header("Content-Disposition: attachment; filename= petition.docx");
header('Content-Type: application/docx');
echo $document;

Actualización 09/05/2023

He seleccionado el editor TinyMCE para este ejemplo, porque es el que mejor HTML genera para el conversor.  La personalización de este editor de HTML se explica en la guía 60.

Para cualquier duda o lo que necesitéis, contactar conmigo a través de mi email: [email protected]

Como siempre, os dejo los fuentes para que podáis probar en vuestros PC’s.

Adjuntos

Archivo Tamaño de archivo Descargas
zip PHPRunner 10.7 + backup de base de datos - Actualizado 09/05/2023 595 KB 213

Blog personal para facilitar soporte gratuito a usuarios de React y PHPRunner