Anteriormente en mi blog he escrito cómo crear y leer hojas de cálculo o documentos de Excel con PHP.

Hoy vamos a ver cómo crear documentos de Word (con extensión docx) en PHP, usando la librería PHPWord.

La librería de phpword permite varias cosas, y para comenzar veremos cómo:

  • Instalar la librería phpword usando composer (no hay pretexto para no usarlo en la actualidad)
  • Crear primer documento de Word, con propiedades, etiquetas, título, etcétera.
  • Agregar texto con distintas fuentes a documento de Word
  • Agregar títulos y alinear texto
  • Poner hipervínculos dentro de un documento de Word
  • Agregar saltos de línea y saltos de página
  • Guardar el documento de Word
  • Descargar el documento de Word

La librería permite más cosas, pero las veremos más tarde.

Instalar librería PHPWord

¿No has instalado Composer? aquí te digo cómo

Si ya cuentas con un proyecto, mira aquí cómo adaptarlo. En caso de que sea nuevo, inicia un proyecto con composer init.

1 - Iniciar proyecto si no existe

Ahora instala la librería con:

composer require phpoffice/phpword

2 - Instalar phpword y dependencias

Después de eso simplemente incluye el autoload:

require_once "vendor/autoload.php";

Estamos listos para comenzar.

Hola mundo con PHPWord

Para crear un documento de Word con PHP hay que crear una nueva instancia de la clase PhpOffice\PhpWord\PhpWord.

$documento = new \PhpOffice\PhpWord\PhpWord();

A ese documento se le pueden modificar las propiedades, agregar secciones, texto, imágenes, títulos, etcétera.

Para modificar las propiedades del documento hay que obtener a las mismas con $documento->getDocInfo() y luego modificarlas con métodos como setCreator, setLastModifiedBy, etcétera.

<?php
/**
 * Trabajar con documentos de Word y PHP usando PHPOffice
 *
 * Más tutoriales en: https://parzibyte.me/blog/
 *
 * Ejemplo 1:
 * Crear documento de word, poner propiedades,
 * guardar para versiones actuales y
 * establecer idioma
 */
require_once "vendor/autoload.php";
use PhpOffice\PhpWord\Style\Language;
$documento = new \PhpOffice\PhpWord\PhpWord();
$propiedades = $documento->getDocInfo();
$propiedades->setCreator("Parzibyte");
$propiedades->setCompany("Ninguna");
$propiedades->setTitle("Primer documento de Word creado con PHP");
$propiedades->setDescription("Este es un documento para mostrar cómo crear archivos de Word con PHP");
$propiedades->setCategory("Tutoriales");
$propiedades->setLastModifiedBy("Parzibyte");
$propiedades->setCreated(mktime());
$propiedades->setModified(mktime());
$propiedades->setSubject("Asunto");
$propiedades->setKeywords("documento, php, word");
# Para que no diga que se abre en modo de compatibilidad
$documento->getCompatibility()->setOoxmlVersion(15);
# Idioma español de México
$documento->getSettings()->setThemeFontLang(new Language("ES-MX"));
# Guardarlo
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($documento, "Word2007");
$objWriter->save("1-hola.docx");

Cuando creamos el documento y lo abrimos (al menos con Microsoft Word), se abre en modo compatibilidad. Para evitarlo se utiliza:

$documento->getCompatibility()->setOoxmlVersion(15);

El idioma se establece creando una instancia de Language, la cual está en PhpOffice\PhpWord\Style\Language;

$documento->getSettings()->setThemeFontLang(new Language("ES-MX"));

En este caso le he puesto el idioma español de México.

Finalmente, para guardar el documento, se crea un escritor o writer y se llama al método save.

Como vamos a guardarlo en el disco duro, indicamos la ruta del documento de salida. Recuerda que el documento no debe estar abierto por otra aplicación durante la creación, y si ya existe, se sobrescribe.

Todo esto que explico ya no lo explicaré en los demás apartados, pero tenemos que hacerlo para establecer el idioma y guardar correctamente el documento.

Descargar documento

Si queremos forzar a su descarga a través del navegador en lugar de guardarlo dentro del disco duro hay que enviar algunos encabezados (parecidos a los que se envían con readfile) y en la ruta de salida indicar a php output:

<?php
/**
 * Trabajar con documentos de Word y PHP usando PHPOffice
 *
 * Más tutoriales en: https://parzibyte.me/blog/
 *
 * Ejemplo 1.1:
 * Crear y descargar documento de word, poner propiedades,
 * guardar para versiones actuales y
 * establecer idioma
 */
require_once "vendor/autoload.php";
use PhpOffice\PhpWord\Style\Language;
$documento = new \PhpOffice\PhpWord\PhpWord();
$propiedades = $documento->getDocInfo();
$propiedades->setCreator("Parzibyte");
$propiedades->setCompany("Ninguna");
$propiedades->setTitle("Primer documento de Word creado con PHP");
$propiedades->setDescription("Este es un documento para mostrar cómo crear archivos de Word con PHP");
$propiedades->setCategory("Tutoriales");
$propiedades->setLastModifiedBy("Parzibyte");
$propiedades->setCreated(mktime());
$propiedades->setModified(mktime());
$propiedades->setSubject("Asunto");
$propiedades->setKeywords("documento, php, word");
# Para que no diga que se abre en modo de compatibilidad
$documento->getCompatibility()->setOoxmlVersion(15);
# Idioma español de México
$documento->getSettings()->setThemeFontLang(new Language("ES-MX"));
# Enviar encabezados para indicar que vamos a enviar un documento de Word
$nombre = "libro.docx";
header("Content-Description: File Transfer");
header('Content-Disposition: attachment; filename="' . $nombre . '"');
header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Expires: 0');
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($documento, "Word2007");
# Y lo enviamos a php://output
$objWriter->save("php://output");

Así tenemos dos opciones: guardarlo en el disco duro o enviarlo directamente de vuelta al usuario.

Agregar texto, hipervínculos y títulos al documento

Hasta el momento no hemos agregado contenido al documento, pero fue porque estaba explicando cómo es que funcionan las propiedades, el idioma y esas cosas.

Para agregar contenido se debe comenzar agregando una sección con:

$seccion = $documento->addSection();

Y a la sección se le puede agregar texto con addText. Esta función puede tomar varios parámetros pero aquí vemos dos (solo el primero es opcional), el texto y una fuente.

La fuente está representada por un arreglo que puede ser declarado con array o con [] usando la sintaxis corta.

<?php
/**
 * Trabajar con documentos de Word y PHP usando PHPOffice
 *
 * Más tutoriales en: https://parzibyte.me/blog/
 *
 * Ejemplo 2:
 * Agregar enlaces y texto con distintas fuentes y colores
 */
require_once "vendor/autoload.php";
use PhpOffice\PhpWord\Style\Language;
$documento = new \PhpOffice\PhpWord\PhpWord();
$propiedades = $documento->getDocInfo();
$propiedades->setCreator("Parzibyte");
$propiedades->setTitle("Texto");

# Agregar texto...
/*
Todos los textos deben estar dentro de una sección
 */

$seccion = $documento->addSection();
# Simple texto
$seccion->addText("Hola, esto es algo de texto");
# Con fuentes personalizadas
$fuente = [
    "name" => "Arial",
    "size" => 12,
    "color" => "8bc34a",
    "italic" => true,
    "bold" => true,
];
$seccion->addText("Hola, esto es algo de texto", $fuente);
# Hipervínculo
$fuenteHipervinculo = [
    "name" => "Arial",
    "size" => 12,
    "color" => "ff0000",
    "italic" => true,
];
$seccion->addLink("https://parzibyte.me/blog/", "Mi blog", $fuenteHipervinculo);

# Títulos. Solo modificando depth (el número)
$fuenteTitulo = [
    "name" => "Verdana",
    "size" => 20,
    "color" => "000000",
];
$documento->addTitleStyle(1, $fuenteTitulo);
$seccion->addTitle("Soy un título", 1);
# Texto bajo el título
$seccion->addText("Hola");
# Ahora un subtítulo con profundidad de 2
$fuenteSubtitulo = [
    "name" => "Verdana",
    "size" => 18,
    "color" => "000000",
];
$documento->addTitleStyle(2, $fuenteSubtitulo);
$seccion->addTitle("Soy un subtítulo", 2);

# Para que no diga que se abre en modo de compatibilidad
$documento->getCompatibility()->setOoxmlVersion(15);
# Idioma español de México
$documento->getSettings()->setThemeFontLang(new Language("ES-MX"));

# Guardarlo
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($documento, "Word2007");

$objWriter->save("2-texto.docx");

También se puede agregar un hipervínculo con addLink($link, $titulo, $fuente).

Los títulos se agregan con addTitle($titulo, $profundidad) en donde la profundidad indica, si lo queremos ver así, el número de título. Por ejemplo, un subtítulo sería un título con profundidad de 2.

Para agregar estilos de títulos (y así todos los títulos con esa profundidad los tendrán) se llama al método addTitleStyle($profundidad, $fuente)

El código de arriba habrá generado un documento como el que se ve aquí.

Nota: los colores están especificados en hexadecimal.

Texto con estilo, textrun

Para agregar texto con distintos formatos (incluso imágenes) en el mismo párrafo y para alinear al mismo, se debe crear un textRun con addTextRun pasando como argumento el estilo de ese párrafo.

En este caso el alignment es en Jc::BOTH para que sea lo mismo que justify o justificado, la constante se encuentra en PhpOffice\PhpWord\SimpleType\Jc por eso se indica su uso al inicio.

<?php
/**
 * Trabajar con documentos de Word y PHP usando PHPOffice
 *
 * Más tutoriales en: https://parzibyte.me/blog/
 *
 * Ejemplo 2.1:
 * Estilizar el texto con textRun
 * Nota: se utiliza la notación corta de arreglos [], que se pueden remplazar por array(),
 * más información en [https://parzibyte.me/blog/posts/sintaxis-corta-array-php/](https://parzibyte.me/blog/posts/sintaxis-corta-array-php/)
 */
require_once "vendor/autoload.php";
use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\Style\Language;
$documento = new \PhpOffice\PhpWord\PhpWord();
$propiedades = $documento->getDocInfo();
$propiedades->setCreator("Parzibyte");
$propiedades->setTitle("Texto con estilos");

# Agregar texto...
/*
Todos los textos deben estar dentro de una sección
 */

$seccion = $documento->addSection();
# Títulos. Solo modificando depth (el número)
$fuenteTitulo = [
    "name" => "Verdana",
    "size" => 20,
    "color" => "000000",
];
$documento->addTitleStyle(1, $fuenteTitulo);
$seccion->addTitle("Cotizaciones web", 1);
$textRun = $seccion->addTextRun([
    "alignment" => Jc::BOTH,
    "lineHeight" => 1, # Quedará muy pegado
]);
$fuente = [
    "name" => "Arial",
    "size" => 12,
    "color" => "8bc34a",
    "italic" => true,
    "bold" => true,
];

$textRun->addText("Un sistema web con PHP y MySQL que permite crear clientes y a partir de ellos cotizaciones con el costo automático, así como el tiempo de la cotización. ");
$textRun->addTextBreak(2);
$textRun->addText("Más tarde, eso se puede imprimir. Aparte de eso, se cuenta con el apartado de los ajustes, en donde se personalizan algunos mensajes");
$textRun->addTextBreak(2);
$textRun->addText("Hice el sistema porque personalmente necesitaba un software para cotizaciones que a veces son requeridas por mis clientes");

$textRun->addTextBreak(5);

$textRun->addText("Texto con una fuente, en el mismo párrafo ", $fuente);

$fuente = [
    "name" => "Verdana",
    "size" => 10,
    "color" => "00ff00",
];
$textRun->addText("que continúa aquí con otra fuente ", $fuente);
$fuente = [
    "name" => "Courier new",
    "size" => 8,
    "color" => "0000ee",
];
$textRun->addText("y sigue por aquí...", $fuente);

# Se pueden agregar más textruns, este va alineado al centro

$otroTextRun = $seccion->addTextRun([
    "alignment" => Jc::CENTER,
    "lineHeight" => 0.7,
]);
$fuente = [
    "name" => "Century Gothic",
    "size" => 15,
    "color" => "000000",
];

$otroTextRun->addText("Lorem ipsum dolor sit amet consectetur adipiscing, elit nulla et aptent ultricies inceptos, tristique torquent lacinia auctor integer. Facilisi eu tempus donec platea inceptos diam dis aliquam mi, vitae senectus ullamcorper nisi torquent auctor vehicula. Viverra rhoncus vestibulum ante bibendum dui volutpat duis auctor dictumst nulla, risus feugiat fusce nisl semper urna nullam aliquam.", $fuente);
$otroTextRun->addTextBreak(2);
$otroTextRun->addText("Pharetra pulvinar curae ac ante risus vestibulum mus diam neque, facilisi scelerisque dignissim velit suscipit ultrices nostra. Laoreet vivamus sem pretium nisi risus natoque magnis cubilia, aliquet eleifend posuere imperdiet dictum sociosqu vel fringilla, diam luctus penatibus eu at ultricies praesent. Nulla habitasse duis felis nostra senectus dapibus, sociosqu porttitor interdum scelerisque tortor donec pharetra, enim ligula dignissim hac nisl.", $fuente);
$otroTextRun->addTextBreak(2);

# Para que no diga que se abre en modo de compatibilidad
$documento->getCompatibility()->setOoxmlVersion(15);
# Idioma español de México
$documento->getSettings()->setThemeFontLang(new Language("ES-MX"));

# Guardarlo
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($documento, "Word2007");

$objWriter->save("2.1-textoEstilo.docx");

El valor de lineHeight es la separación de líneas, entre mayor sea, más separación habrá.

Cuando tenemos el textRun se puede agregar texto normalmente, con addText, pasando una fuente personalizada si se desea.

Cada llamada a addText es como una concatenación, es decir, no se crea un nuevo párrafo, sino que se agrega.

También se pueden agregar saltos con addTextBreak, y claramente se pueden agregar los textRun que se necesiten, no hay límite.

Gracias al textRun se pueden agregar párrafos de texto en donde haya distintas fuentes y estilos.

El resultado del ejemplo se puede ver aquí. Y si te lo preguntas, el texto es de un post de un sistema de cotizaciones.

Saltos de línea y saltos de página

Para terminar este post (habrá todavía más) veamos cómo agregar separaciones entre párrafos o entre páginas.

Si queremos agregar un salto de línea lo hacemos con $seccion->addTextBreak() indicando el número de saltos.

Para agregar un salto de página llamamos a $seccion->addPageBreak(), podemos hacerlo dentro de un ciclo en caso de querer saltar muchas veces.

<?php
/**
 * Trabajar con documentos de Word y PHP usando PHPOffice
 *
 * Más tutoriales en: https://parzibyte.me/blog/
 *
 * Ejemplo 3:
 * Saltos de párrafo y saltos de página
 */
require_once "vendor/autoload.php";
use PhpOffice\PhpWord\Style\Language;
$documento = new \PhpOffice\PhpWord\PhpWord();
$propiedades = $documento->getDocInfo();
$propiedades->setCreator("Parzibyte");
$propiedades->setTitle("Saltos");

# Agregar texto...
/*
Todos los textos deben estar dentro de una sección
 */

$seccion = $documento->addSection();
# Simple texto
$seccion->addText("Hola, esto es algo de texto");
# Agregar 5 saltos
$seccion->addTextBreak(5);
$seccion->addText("Texto 5 saltos después");
# Agregar un salto de página
$seccion->addPageBreak();
$seccion->addText("Estoy en una nueva línea");
$seccion->addPageBreak();
$seccion->addText("Estoy en una nueva línea");

# Para que no diga que se abre en modo de compatibilidad
$documento->getCompatibility()->setOoxmlVersion(15);
# Idioma español de México
$documento->getSettings()->setThemeFontLang(new Language("ES-MX"));

# Guardarlo
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($documento, "Word2007");

$objWriter->save("3-saltos.docx");

El documento creado se puede ver aquí.

Conclusión

Esta es la parte 1 de 3, en el siguiente post veremos cómo crear listas, tablas y agregar imágenes ya sea locales o de internet.

Por cierto, la documentación oficial está aquí.

Actualización: ya está aquí la parte 2.

Si el post ha sido de tu agrado te invito a que me sigas para saber cuando haya escrito un nuevo post, haya actualizado algún sistema o publicado un nuevo software. Facebook | X | Instagram | Telegram | También estoy a tus órdenes para cualquier contratación en mi página de contacto