Fullmenu null

 

25 January 2018

El supuesto que vamos a plantear en este documento es la creación de un catálogo de productos de una tienda.

Dicho catálogo consistirá en un Pdf donde mostraremos, para cada artículo de interés, la descripción, el precio y una imagen del producto.

Para la generación de este catálogo nos vamos a basar en Asciidoctor. Para una consulta rápida sobre esta herramienta puedes consultar el siguiente tutorial, el cual es una guía básica pero completa sobre esta materia.
En este caso vamos a utilizar imágenes en nuestro local pero podemos apuntar a links donde tengamos nuestras imágenes . En nuestro ejemplo el id de cada item corresponderá con el nombre de imagen a usar para el artículo.
El schema de nuestra base de datos será algo muy simple, consistente en una única tabla con el id, descripción, precio de cada artículo.

Organización

Nuestro script necesitará conectarse a una base de datos de donde obtendrá la información de cada artículo, y para cada uno de ellos generará una "página" del Pdf. Para ello usaremos un fichero plantilla común a todos, aunque utilizar uno diferente en base a la categoría, precio, etc de cada artículo es una modificación trivial.

product.tpl
.Ref: ${sku}
----
Descripcion: ${description}

Referencia ${sku}, ${price}
----

image::${sku}.png[${description}]
catalog.tpl
= Catalogo de Productos
Miguel Rueda <miguel.rueda.garcia@gmail.com>
:idprefix:
:icons: font
:imagesdir: ./images

Este es el catálogo de nuestros productos a día de hoy. En el puede encontrar las referencias,
junto con precios e imágenes, de cada uno de ellos.

Preparación del entorno

template=new File('./product.tpl').text
catalog= new File('./catalog.tpl').text
query='select * from products where sku < 3'

new File('.').eachFileMatch(~/.*.adoc/) { file ->
    file.delete()
}

engine = new groovy.text.SimpleTemplateEngine()

Para empezar a trabajar lo primero es cargar las plantillas anteriormente mencionadas (las del producto: product.tpl y la del catálogo general: catalog.tpl), al igual que la query para la base de datos junto con la condición que requiera el cliente.

El siguiente paso será limpiar todos los posibles ficheros con extensión adoc que podamos tener en la ruta de ejecución de nuestro script

Por último inicializaremos nuestra variable engine que nos servirá para tratar las plantillas.

Carga de información y creación de los productos

A continuación pasamos a explicar como cargamos de la base de datos los productos y como posteriormente generamos partiendo de un fichero base nuestros productos.

Carga de información

Para ello utilizaremos la función generateProducts() la cual se conecta a la base de datos que le indiquemos, ejecutará la query anteriormente definida y por cada producto llamará al método createProductAdoc encargado del hacer el .adoc del producto que recibirá por parámetro:

generateProducts
void generateProducts(){
    Sql sql = Sql.newInstance( "jdbc:mysql://localhost:3306/origen?jdbcCompliantTruncation=false",
            "user",
            "password",
            "com.mysql.jdbc.Driver")
    sql.eachRow(query){item->
        createProductAdoc item
    }
}

Creación de cada producto

Mediante el engine previamente inicializado con la plantilla correspondiente conseguiremos generar un fichero para cada artículo donde se sustituyan las variables de la plantilla con los valores del producto en cuestión;

createProductAdoc
def createProductAdoc(item){
    def txt = engine.createTemplate(template).make([
            'description':item.description,
            'sku':item.sku,
            'price':item.price
    ]).toString()
    new File("${item.sku}.adoc").text = txt
}

Para la construcción del catálogo en sí usaremos la misma técnica que para cada producto, utlizando esta vez una plantilla catalog.tpl De esta forma podremos parametrizar el aspecto inicial del catálogo.

Sobre el fichero generado iremos añadiendo directivas asciidoctor para incluir los adoc generados para cada artículo tal como se detalla en el apartado anterior. El resultado final será el fichero catalogo.adoc con tantos includes como artículos hemos obtenido en la consulta:

generateCatalog
void generateCatalog() {

    def txt = engine.createTemplate(catalog).make([
            'today': new Date().format('dd/MM/yyyy HH:mm')
    ]).toString()
    new File("catalogo.adoc").text = txt

    Sql sql = Sql.newInstance("jdbc:mysql://localhost:3306/origen?jdbcCompliantTruncation=false",
            "user",
            "password",
            "com.mysql.jdbc.Driver")
    sql.eachRow(query) { item ->
        new File("catalogo.adoc") << "include::${item.sku}.adoc[]\n"
    }
}

PDF

Por último nos resta invocar a Asciidoctor para que tomando como base los ficheros generados (catalogo y sus includes) nos genere un Pdf catalogo.pdf

cretatePDF
void generatePdf(){
    asciidoctor = Factory.create();
    attributes = AttributesBuilder.attributes(). // (1)
            docType('book').
            tableOfContents(true).
            sectionNumbers(true).
            sourceHighlighter("coderay").
            get()

    options = OptionsBuilder.options(). // (2)
            backend('pdf').
            attributes(attributes).
            safe(SafeMode.UNSAFE).
            get()

    asciidoctor.convertFile(new File("catalogo.adoc"), options) // (3)
}
  1. Creamos los atributos que contendrá nuestro fichero asciidoctor. En los cuales podemos indicar el tipo de documento si tendrá o no tabla de contenidos…​

  2. Indicamos que opciones para crear nuestro catálogo entre ellas backend('pdf') ya que es el formato que deseamos obtener

  3. Conversión del adoc a pdf

Personalización del pdf

Si quisiéremos personalizar aún más nuestro pdf podemos crear un "tema" para nuestro pdf y con ello aumentar las características del mismo. Para ello tenemos que crear un fichero con la extensión yml en el que podemos incluir el tipo de fuente, el tamaño, imágenes de fondo y un largo de etcétera de caraterísticas. Vamos a ver un ejemplo de tema:

title_page: // (1)
  align: left
base:
  font_family: Times-Roman // (2)
  font_size: 12  // (3)
  1. Indicamos que el titulo de nuestra imagen está alineado a la izquierda

  2. La fuente de nuestro texto es Times-Roman

  3. El tamaño de letra de es 12

Para que Asciidoctor utilize este tema simplemente hay que indicarlo en los atributos a la hora de su invocación:

    asciidoctor = Factory.create();
    attributes = AttributesBuilder.attributes().attribute("pdf-style", "tema.yml").
        docType('book').
        tableOfContents(true).
        sectionNumbers(true).
        get()

Script
@Grapes([
        @Grab(group='org.asciidoctor', module='asciidoctorj', version='1.5.6'),
        @Grab(group='org.asciidoctor', module='asciidoctorj-pdf', version='1.5.0-alpha.16'),
        @Grab(group='org.jruby', module='jruby-complete', version='9.1.15.0'),
        @Grab('mysql:mysql-connector-java:5.1.6')
])
@GrabConfig(systemClassLoader=true)

import groovy.sql.Sql
import org.asciidoctor.OptionsBuilder
import org.asciidoctor.AttributesBuilder
import org.asciidoctor.SafeMode
import org.asciidoctor.Asciidoctor.Factory
//tag::prepararEntorno[]

template=new File('./product.tpl').text
catalog= new File('./catalog.tpl').text
query='select * from products where sku < 3'

new File('.').eachFileMatch(~/.*.adoc/) { file ->
    file.delete()
}

engine = new groovy.text.SimpleTemplateEngine()

//end::prepararEntorno[]

generateProducts()
generateCatalog()
generatePdf()

//tag::generateProducts[]
void generateProducts(){
    Sql sql = Sql.newInstance( "jdbc:mysql://localhost:3306/origen?jdbcCompliantTruncation=false",
            "user",
            "password",
            "com.mysql.jdbc.Driver")
    sql.eachRow(query){item->
        createProductAdoc item
    }
}
//end::generateProducts[]


//tag::create_adoc[]
def createProductAdoc(item){
    def txt = engine.createTemplate(template).make([
            'description':item.description,
            'sku':item.sku,
            'price':item.price
    ]).toString()
    new File("${item.sku}.adoc").text = txt
}
//end::create_adoc[]


//tag::create_catalog[]
void generateCatalog() {

    def txt = engine.createTemplate(catalog).make([
            'today': new Date().format('dd/MM/yyyy HH:mm')
    ]).toString()
    new File("catalogo.adoc").text = txt

    Sql sql = Sql.newInstance("jdbc:mysql://localhost:3306/origen?jdbcCompliantTruncation=false",
            "user",
            "password",
            "com.mysql.jdbc.Driver")
    sql.eachRow(query) { item ->
        new File("catalogo.adoc") << "include::${item.sku}.adoc[]\n"
    }
}
//end::create_catalog[]

//tag::create_pdf[]
void generatePdf(){
    asciidoctor = Factory.create();
    attributes = AttributesBuilder.attributes(). // (1)
            docType('book').
            tableOfContents(true).
            sectionNumbers(true).
            sourceHighlighter("coderay").
            get()

    options = OptionsBuilder.options(). // (2)
            backend('pdf').
            attributes(attributes).
            safe(SafeMode.UNSAFE).
            get()

    asciidoctor.convertFile(new File("catalogo.adoc"), options) // (3)
}
//end::create_pdf[]