php

PHPWord: agregar marca de agua, TOC y gráficas a documento de Word con PHP

En este post vamos a ver cómo trabajar con documentos de Word usando el lenguaje de programación PHP y la librería PHPWord, parte de la familia de PHPOffice.

Lo que veremos será cómo:

  1. Agregar marca de agua a un documento
  2. Agregar un índice o tabla de contenido
  3. Trabajar con gráficas de barras, de pastel, de línea, etcétera

Este tutorial es la tercera parte de una serie de tutoriales sobre documentos de Word y PHP.

Recuerda ver la primera parte para ver cómo instalar la librería y su uso básico, así como la segunda parte para trabajar con listas, tablas e imágenes.

Marca de agua a documento de Word con PHP

Agregar una marca de agua es tan sencillo como agregar una imagen; solo que la misma se agrega a un encabezado o header, no a una sección.

Para agregar un encabezado se crea una sección, y después se llama al método addHeader.

Más tarde, se llama a $encabezado->addWatermark para poner una marca de agua; con dos argumentos: la ruta de la imagen y un arreglo con el estilo de la misma.

<?php
/**
 * Trabajar con documentos de Word y PHP usando PHPOffice
 *
 * Más tutoriales en: parzibyte.me/blog
 *
 * Ejemplo 7:
 * Agregar marca de agua
 * 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\Style\Language;
$documento = new \PhpOffice\PhpWord\PhpWord();
$propiedades = $documento->getDocInfo();
$propiedades->setCreator("Luis Cabrera Benito");
$propiedades->setTitle("Marca de agua");

$seccion = $documento->addSection();
$encabezado = $seccion->addHeader();
$encabezado->addWatermark("conejo.jpg", [
    "width" => 500,
]);
# Títulos. Solo modificando depth (el número)
$fuenteTitulo = [
    "name" => "Verdana",
    "size" => 20,
    "color" => "000000",
];
$documento->addTitleStyle(1, $fuenteTitulo);
$seccion->addTitle("Gopher", 1);
$seccion->addText("Los geómidos son una familia de roedores castorimorfos conocidos vulgarmente como tuzas, taltuzas o ratas de abazones. Se encuentran en Canadá, Estados Unidos, México, América Central y Colombia. En México habitan seis especies que se encuentran en peligro de extinción. ");

$seccion->addTitle("Conejo", 1);
$seccion->addText("El conejo común o conejo europeo es una especie de mamífero lagomorfo de la familia Leporidae, y el único miembro actual del género Oryctolagus. Está incluido en la lista 100 de las especies exóticas invasoras más dañinas del mundo​ de la Unión Internacional para la Conservación de la Naturaleza.");


# 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("7-marca-de-agua.docx");

Después de haber agregado la marca de agua se puede agregar todo tipo de contenido a la sección: títulos, texto, más imágenes, etcétera.

Marca de agua a documento de Word con PHPWord

Nota: puedes ver el documento generado aquí.

Tabla de contenidos o índice

Para agregar un TOC o tabla de contenido addTOC de una sección.

Recuerda que existirá una tabla de contenido si hay títulos, así que debes separar el documento por título.

En el ejemplo le pasamos dos argumento a addTOC: en primer lugar un arreglo con la fuente y en segundo lugar un arreglo de opciones, el cual solo lleva la forma en la que separa el título del número de páginas, y lo hace a través de guiones bajos.

<?php
/**
 * Trabajar con documentos de Word y PHP usando PHPOffice
 *
 * Más tutoriales en: parzibyte.me/blog
 *
 * Ejemplo 8:
 * Agregar contenido y un índice
 * 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\Style\Language;
use PhpOffice\PhpWord\Style\TOC;
$documento = new \PhpOffice\PhpWord\PhpWord();
$propiedades = $documento->getDocInfo();
$propiedades->setCreator("Luis Cabrera Benito");
$propiedades->setTitle("Tabla de contenidos");

/*
Todos los textos deben estar dentro de una sección
 */
$seccion = $documento->addSection();
# Registrar el estilo del título
$fuenteTitulo = [
    "name" => "Verdana",
    "size" => 20,
    "color" => "000000",
];
$documento->addTitleStyle(1, $fuenteTitulo);
# Agregar el título del índice
$seccion->addTitle("Índice", 1);
# Aquí agregamos la tabla de contenidos
$fuenteTablaContenidos = [
    "name" => "Arial",
    "size" => 20,
    "color" => "881111",
];
$estiloTablaDeContenidos = [
    "tabLeader" => TOC::TABLEADER_UNDERSCORE,
];
$seccion->addTOC($fuenteTablaContenidos, $estiloTablaDeContenidos);

$seccion->addTitle("Lorem", 1);
# Texto bajo el título
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");
$seccion->addTitle("Ipsum", 1);
# Texto bajo el título
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");
$seccion->addTitle("Dolor", 1);
# Texto bajo el título
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");
# 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);
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");

$seccion->addTitle("Soy un subtítulo", 2);
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");
$seccion->addTitle("Otro título", 1);
$seccion->addTitle("Otro subtítulo", 2);
# Texto bajo el título
$seccion->addText("Lorem ipsum dolor sit amet consectetur adipiscing elit, cursus facilisi id risus aliquet enim, varius ultricies dictum suspendisse mollis non.");

# 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("8-tabla-de-contenidos.docx");

Estos separadores están definidos en la clase PhpOffice\PhpWord\Style\TOC.

Dentro del ejemplo se agrega un índice al inicio, y más adelante se agregan múltiples encabezados para que la tabla tenga contenido.

El resultado es el siguiente:

Tabla de contenido con PHPWord

Puedes ver el documento completo en este enlace.

Gráficas a documento de Word con PHP

Gracias a PHPWord podemos agregar múltiples gráficas y datos de una manera sencilla. Vamos a ver cómo trabajar con gráficas de dona (o como se diga en español), gráficas de pastel, de área (o como se diga en español), de barras y de líneas.

Gráfica de pastel con PHPWord

Las gráficas se agregan a una sección a través del método addChart, el cual recibe 3 argumentos:

  1. El tipo de gráfica que puede ser pie, doughnut, bar, column, line, area, scatter, radar, stacked_bar, percent_stacked_bar, stacked_column y percent_stacked_column.
  2. Las “categorías” o etiquetas, que son, si esto fuera un clave y valor, la clave. Aquí mostramos la etiqueta de cada valor, o su título. Va en forma de arreglo.
  3. Series o datos, que son los valores reales. Uno por cada etiqueta. Igualmente va en forma de arreglo
  4. Opciones de la gráfica como sus medidas o si son 3d. No todas las gráficas se pueden dibujar en 3d.

Adicionalmente hay algunas gráficas que soportan multiseries o múltiples datos, para agregar más datos a la gráfica debemos guardar lo que regresa addChart y luego llamar al método addSeries, con dos argumentos: las etiquetas y los valores que se agregan.

<?php
/**
 * Trabajar con documentos de Word y PHP usando PHPOffice
 *
 * Más tutoriales en: parzibyte.me/blog
 *
 * Ejemplo 9:
 * Gráficas
 * 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\Shared\Converter;
use PhpOffice\PhpWord\Style\Language;
$documento = new \PhpOffice\PhpWord\PhpWord();
$propiedades = $documento->getDocInfo();
$propiedades->setCreator("Luis Cabrera Benito");
$propiedades->setTitle("Gráficas");

$seccion = $documento->addSection();

# Títulos. Solo modificando depth (el número)
$fuenteTitulo = [
    "name" => "Verdana",
    "size" => 20,
    "color" => "000000",
];
$documento->addTitleStyle(1, $fuenteTitulo);
$estilo = [
    "width" => Converter::cmToEmu(17),
    "height" => Converter::cmToEmu(10),
];
# Gráfica con una sola línea
$seccion->addTitle("Ventas del año actual", 1);
$etiquetas = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"];
$series = [600, 700, 999, 1000, 2800, 3000, 5000, 3000, 6000, 6000, 5000, 7000];
# Gráfica con 3 líneas
$grafica = $seccion->addChart("line", $etiquetas, $series, $estilo);
$seccion->addTitle("Comparación de ventas", 1);
$etiquetas = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"];
$series = [600, 700, 999, 1000, 2800, 3000, 5000, 3000, 6000, 6000, 5000, 7000];
$grafica = $seccion->addChart("line", $etiquetas, $series, $estilo);
# Agregar más datos...
$grafica->addSeries($etiquetas, [500, 132, 32, 432, 332, 456, 212, 5333, 4568, 123, 879, 4544]);
$grafica->addSeries($etiquetas, [999, 4848, 4544, 7833, 4549, 454, 212, 666, 121, 999, 454, 335]);
# La misma de arriba, pero no de tipo line, sino bar
$seccion->addTitle("Comparación de ventas", 1);
$etiquetas = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"];
$series = [600, 700, 999, 1000, 2800, 3000, 5000, 3000, 6000, 6000, 5000, 7000];
$grafica = $seccion->addChart("bar", $etiquetas, $series, $estilo);
# Agregar más datos...
$grafica->addSeries($etiquetas, [500, 132, 32, 432, 332, 456, 212, 5333, 4568, 123, 879, 4544]);
$grafica->addSeries($etiquetas, [999, 4848, 4544, 7833, 4549, 454, 212, 666, 121, 999, 454, 335]);
# La misma de arriba, pero no de tipo bar, sino column
$seccion->addTitle("Comparación de ventas", 1);
$etiquetas = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"];
$series = [600, 700, 999, 1000, 2800, 3000, 5000, 3000, 6000, 6000, 5000, 7000];
$grafica = $seccion->addChart("column", $etiquetas, $series, $estilo);
# Agregar más datos...
$grafica->addSeries($etiquetas, [500, 132, 32, 432, 332, 456, 212, 5333, 4568, 123, 879, 4544]);
$grafica->addSeries($etiquetas, [999, 4848, 4544, 7833, 4549, 454, 212, 666, 121, 999, 454, 335]);
# La misma de arriba, pero no de tipo column, sino area
$seccion->addTitle("Comparación de ventas", 1);
$etiquetas = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"];
$series = [600, 700, 999, 1000, 2800, 3000, 5000, 3000, 6000, 6000, 5000, 7000];
$grafica = $seccion->addChart("area", $etiquetas, $series, $estilo);
# Agregar más datos...
$grafica->addSeries($etiquetas, [500, 132, 32, 432, 332, 456, 212, 5333, 4568, 123, 879, 4544]);
$grafica->addSeries($etiquetas, [999, 4848, 4544, 7833, 4549, 454, 212, 666, 121, 999, 454, 335]);

# Una de pastel
$seccion->addTitle("Gastos por categoría", 1);
$etiquetas = ["Comida", "Escuela", "Tecnología"];
$series = [123, 456, 789];
$grafica = $seccion->addChart("pie", $etiquetas, $series, $estilo);
# Una de dona
$seccion->addTitle("Gastos por categoría", 1);
$etiquetas = ["Comida", "Escuela", "Tecnología"];
$series = [123, 456, 789];
$grafica = $seccion->addChart("doughnut", $etiquetas, $series, $estilo);
# La primera, pero en 3d
$estilo = [
    "width" => Converter::cmToEmu(17),
    "height" => Converter::cmToEmu(10),
    "3d" => true,
];
$seccion->addTitle("Ventas del año actual", 1);
$etiquetas = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"];
$series = [600, 700, 999, 1000, 2800, 3000, 5000, 3000, 6000, 6000, 5000, 7000];
$grafica = $seccion->addChart("line", $etiquetas, $series, $estilo);



# 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("9-graficas.docx");

En el código usamos un conversor de medidas de la clase PhpOffice\PhpWord\Shared\Converter, llamando al método cmToEmu, centímetros a Emu (una unidad de medida especialmente para los estándares de estos documentos)

Gráfica con múltiples datos con PHPWord

Además de las gráficas podemos agregar títulos o texto como normalmente se hace.

Para indicar que una gráfica sea 3d ponemos la clave del arreglo “3d” en true.

Gráfica 3d a documento de Word con PHPWord

Mira todas las gráficas y el documento en este enlace.

Conclusión

Así concluyo esta serie de posts sobre PHPWord para la creación de documentos de Word usando PHP.

Por el momento no hay métodos documentados para leer un documento de Word, es decir, hacer el proceso inverso (al menos con esta librería)

Supongo que la complejidad es que a diferencia de las hojas de cálculo, un documento de Word no tiene una estructura definida (no solo tiene celdas).

Tal vez tiene secciones y las secciones tienen muchas cosas, pero será un lío hacer una manera fácil de leer los datos.

En fin, tal vez en un futuro se agreguen estas características para leer un documento de Word, aunque no lo veo tan necesario; pero tal vez a un cliente se le ocurra, uno nunca sabe.

La parte 1 de este tutorial está aquí, y la parte 2 aquí. El código con los ejemplos y archivos generados en mi GitHub, la librería también en GitHub y la documentación en readthedocs.

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

  • Que tal, yo quisiera saber si se puede crear varios archivos de Word al mismo tiempo. Todos los comandos que se utilizan para crear el documentos estand dentro de un for o un foreach que debería de crear un nuevo documento recorriendo un arreglo con la información que necesito. Es decir, quiero crear un nuevo documento con una misma plantilla donde solo me cambie el nombre del documento y un dato de la información que contiene la plantilla dentro de esta..

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.