php

Crear documentos de Word con PHP y PHPWord

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: 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("Luis Cabrera Benito");
$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("Luis Cabrera Benito");
$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: 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("Luis Cabrera Benito");
$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("Luis Cabrera Benito");
$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: 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("Luis Cabrera Benito");
$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: 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/2018/10/11/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("Luis Cabrera Benito");
$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: 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("Luis Cabrera Benito");
$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.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

  • Como podría establecer mi reporte en una sola hoja de Word tengo encabezados y pie de pagina, además de una tabla de datos extraída desde MySQL con php, pero me los muestra en hojas separadas, una hoja para encabezado, una hoja para mi tabla, y una mas para el pie de pagina, Saludos

  • Buenas. He estado trabajando con esta clase para generar documentos Word y me ha resultado muy interesante. El único problema es que no he podido solucionar un problema que tengo con el alineado de los textos y los estilos. He seguido los ejemplos al pie de la letra y si bien no tengo errores no obtengo los resultados que necesito.

    Por otro lado si bien puedo generar documentos de Open Office y PDFs sin problemas el maquetado y los estilos no son los mismos, incluso he perdido elementos en la conversión.

    Es muy frustrante porque PHP Office parecía tener todas las respuestas a mis problemas y no he podido implementarlo correctamente.

    ¿Alguien ha tenido problemas similares?
    Saludos.

  • Hola, PHPWord es compatible con PHP 7? porque he visto que se han presentado algunos comentarios al respecto, saludos

    • Hola. Me parece que lo he probado con esa versión y no he tenido problemas. Para asegurarse puede probarlo por usted mismo; estoy seguro de que funcionará correctamente
      Saludos :)

  • buenas como lo puedo hacer para poner un titulo en el medio... como si fuera un text aling : center?? No me funciona...... lo hago de esta manera i nada.....

    $seccion = $documento->addSection();
    # Títulos. Solo modificando depth (el número)
    $fuenteTitulo = [
    "name" => "Arial",
    "size" => 14,
    "color" => "000000",
    ];
    $documento->addTitleStyle(1, $fuenteTitulo);
    $seccion->addTitle("Regidora de Benestar Social", 1);
    $documento->addTitleStyle(2, $fuenteTitulo);
    $seccion->addTitle("INFORME PRESTACIÓ ECONÒMICA", 2);
    $textRun = $seccion->addTextRun([
    "alignment" => Jc::CENTER,
    // 'textAlign' => 'center',
    // "lineHeight" => 1, # Quedará muy pegado
    ]);

    • Me pasa exactamente lo mismo. Es muy frustrante porque todos parecen tener solucionado el problema.

  • muchas gracias, ya le entendí un poco mejor a esto, porque con el manual no pude, por otro lado tendrás tutoriales sobre phpword en cuanto a encabezados y pies de pagina?, gracias

  • Excelente tutorial...
    yo tengo este codigo, me genera un word a partir de una plantilla...
    -------------------------------------------
    require_once "vendor/autoload.php";
    $templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('Template.docx');
    $templateProcessor->setValue('date', date("d-m-Y"));
    $templateProcessor->setValue('name', 'John Doe');
    $templateProcessor->setValue(
    ['city', 'street'],
    ['Sunnydale, 54321 Wisconsin', '123 International Lane']);
    $templateProcessor->saveAs('MyWordFile.docx');
    -----------------------------------------------------------------
    me gustaria saber como hacer 2 cosas...
    Que lo descargue preguntando donde debe ir y que al guardar, lo guarden con el nombre de la persona ejemplo: John Doe.docx

    seria posible que me ayudara en eso?

    • al final no me resulto, creo que no se puede trabajando con una plantilla... solo haciendo desde el documento.

      asi quedo el codigo:
      setValue('date', date("d-m-Y"));
      $templateProcessor->setValue('name', 'John Doe 2');
      $templateProcessor->setValue(
      ['city', 'street'],
      ['Sunnydale, 54321 Wisconsin', '123 International Lane']);
      $nombreDelDocumento = "Jhon Doe.docx";
      header("Content-Description: File Transfer");
      header('Content-Disposition: attachment; filename="' . $nombreDelDocumento . '"');
      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');

      $writer = IOFactory::createWriter($documento, 'docx');
      $writer->save('php://output');
      exit;

    • Si lo intente ;setValue('date', date("d-m-Y"));
      $templateProcessor->setValue('name', 'John Doe');
      $templateProcessor->setValue(
      ['city', 'street'],
      ['Sunnydale, 54321 Wisconsin', '123 International Lane']);
      $nombreDelDocumento = "Jhon Doe.docx";
      header('Content-Disposition: attachment; filename="'.$nombreDelDocumento.'"');

      $templateProcessor->saveAs($nombreDelDocumento);

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

17 horas hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

1 semana hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

1 semana hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

1 semana hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

1 semana hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

1 semana hace

Esta web usa cookies.