GLIFO/GLYPH, una función con nombre para generar miniaturas a partir de texto en tus hojas de cálculo

Hacía ya un tiempo que quería dedicar unas líneas a las funciones con nombre de las hojas de cálculo de Google en este espacio.

Y creo sinceramente que este es un buen momento, puesto que fue precisamente hace un año (24 de agosto de 2022) cuando el equipo de desarrollo de Google Sheets nos hacía uno de los mayores y más excitantes regalos 🎁 que recuerdo en la historia de esta herramienta. Si como yo 🙋‍♂️ eres un o una friki de las hojas de cálculo en general y de las de Google en particular, cosa bastante probable si te pasas por aquí de vez en cuando, estoy seguro de que lo recordarás perfectamente.

Las funciones con nombre formaban parte de ese regalo. Y ojo porque a lo largo de 2023 han llegado a Google Sheets más novedades jugosas. A veces pienso que las hojas de cálculo de Google han evolucionado más a lo largo de este último año que durante toda la pasada década. ¡Bienvenidos sean los cambios cuando son para bien!

En este artículo se introducen brevemente las funciones con nombre de Google Sheets y se presenta la implementación de una función capaz de generar pequeñas miniaturas gráficas a partir de elementos de texto sin utilizar Apps Script.

TABLA DE CONTENIDOS

¿Qué son las funciones con nombre?

Gracias a las funciones con nombre podemos enriquecer el lenguaje de fórmulas de las hojas de cálculo de Google con funciones propias, que pueden ser utilizadas exactamente del mismo modo que las disponibles de manera nativa.

De este modo, tenemos la posibilidad de construir funciones nuevas que realicen cálculos o procesos frecuentes para simplificar nuestras fórmulas, encapsular su complejidad y facilitar su reutilización en múltiples hojas de cálculo.

Muy conveniente, ¿no te parece? Pero veamos un ejemplo.

Una función con nombre que devuelve el número de la última fila no vacía de un intervalo de celdas o matriz de datos.

Cuando se utilizan fórmulas que operan sobre conjuntos de datos "abiertos", es decir, que contienen un número indeterminado de filas, a menudo resulta útil conocer cuál es la última fila que contiene realmente información.

Por esa razón, aquí he definido la función con nombre ULTIMAFILADATOS(), que devuelve el número de la última fila no vacía de un intervalo de datos, tanto si este se ha especificado mediante un rango de celdas como si es el resultado de una expresión de tipo matricial.

=LET(
ultimaFila;
ArrayFormula(MAX(SI(intervalo <> ""; SEQUENCE(FILAS(intervalo)); 0)));
SI(
ESERROR(FILA(intervalo));
ultimaFila;
FILA(intervalo) + ultimaFila - 1
)
)

Es posible que te estés preguntando si esto no se podía conseguir ya con las funciones personalizadas (custom functions) de Apps Script. Pues sí. Y de hecho, las podemos seguir utilizando exactamente del mismo modo.

¿Cuáles debemos usar entonces de manera preferente para expandir el repertorio de funciones disponibles inicialmente en las hojas de cálculo de Google?

Lo cierto es que esta pregunta no tiene una respuesta rápida, ni probablemente inapelable.

👍 En términos generales, las funciones con nombre parecen un recurso o estrategia más natural que las funciones personalizadas Apps Script, por varias razones:

  • Su desarrollo, a través de un intuitivo panel disponible en el editor de hojas de cálculo que nos va guiando en el proceso, es más inmediato. Este panel además nos brinda facilidades para dotarlas de una ayuda contextual indistinguible de la que muestran el resto de funciones de Google Sheets.
  • Reutilizar funciones con nombre también resulta ligeramente más cómodo puesto que su editor incluye un práctico procedimiento de importación desde otras hojas de cálculo, hojas que pueden actuar como repositorios de funciones. No necesitaremos andar cortando y pegando código o empaquetar nuestras funciones personalizadas Apps Script en complementos para hojas de cálculo.
    Se echa de menos la posibilidad de definir funciones con nombre globales, autoactualizables, pero esto no está nada mal.
  • Las funciones con nombre pueden leer datos procedentes de otras hojas de cálculo usando internamente la función IMPORTRANGE(), en tanto que las funciones personalizadas desarrolladas con Apps Script solo pueden hacerlo de la hoja de cálculo en la que se están utilizando.
  • A diferencia de las funciones personalizadas Apps Script, las funciones con nombre pueden devolver como resultado una o más imágenes, algo que como puedes suponer resultará crucial en el caso de uso que te voy a mostrar en un momento. 
  • Las funciones personalizadas Apps Script fallan devolviendo un error si su tiempo de ejecución supera los 30 segundos. Esto es algo que en principio no afecta a las funciones con nombre, que no dejan de comportarse como las nativas. En cualquier caso, una hoja de cálculo aquejada de tiempos de recálculo tan extremos es algo que no vas a querer y que pide a gritos algún tipo de optimización o rediseño.
  • En ocasiones, y de manera intermitente, evidencian un comportamiento poco robusto que nos obliga a esperar más tiempo del que sería deseable para obtener resultados o incluso a forzar la recarga de la hoja de cálculo.

No me malinterpretes. Adoro las las funciones personalizadas Apps Script. He hablado con frecuencia de ellas en este espacio, y seguro que lo volveré a hacer. Y es que...

👎 Nuestras flamantes funciones con nombre no están exentas de algún que otro inconveniente:

  • Las funciones con nombre no soportan de manera directa argumentos opcionales, que sin embargo sí están disponibles en las funciones personalizadas Apps Script (pero si tienes paciencia te explicaré un truco para sortear esta limitación 😏).
  • De hecho, los procesos de validación de los parámetros de entrada de una función casera pueden realizarse de un modo más potente y versátil —aunque también más complicado, claro está— cuando se implementan usando Apps Script.
  • Las funciones con nombre no admiten comentarios en el código (¡ooooh!). Y todos nos hemos encontrado alguna vez, en los confines de alguna hoja de cálculo, con una de esas fórmulas monstruosas que dan miedo, mucho miedo, y cuyos oscuros detalles de implementación son fácilmente olvidados, incluso por sus a menudo ajetreados creadores. Es cierto que con la introducción de la función LET() las cosas han mejorado notablemente, pero la posibilidad de introducir comentarios, como en Coda y en N... (¡huy, casi se me escapa 🤭!) no es en absoluto desdeñable.
    Un tremebundo y amenazador "muro" de fórmulas, también para mí, que lo he parido, antes del tratamiento con LET().
  • Apps Script no deja de ser JavaScript moderno que se escribe dentro de un editor potente y moderno y se ejecuta en los servidores de Google. Aunque la potencia del lenguaje de fórmulas de las hojas de cálculo de Google ha subido muchos enteros en los últimos meses 💪, hay cosas que simplemente se implementan de un modo más cómodo y directo mediante un lenguaje de programación convencional (evitaré utilizar la expresión "lenguaje de verdad"). Cualquiera de las funciones personalizadas Apps Script que encontrarás en este blog (te las he enlazado más arriba), o esta otra, que permite parsear estructuras de texto JSON contenidas dentro de un intervalo de celdas, son prueba de ello. Y encima, con comentarios.
  • Por último, las funciones creadas con Apps Script son capaces de hurgar en ciertos recovecos de las hojas de cálculo que las funciones con nombre no siempre alcanzan. Esto les permite hacer cosas únicas, por ejemplo, extraer los URL de los enlaces insertados como texto enriquecido dentro de una celda. 

☝ Mi consejo general es que utilices de manera preferente las funciones con nombre, y solo te plantees recurrir a las funciones personalizadas Apps Script cuando las primeras no te permitan alcanzar tus objetivos de diseño de un modo lo suficientemente satisfactorio.

La primera función GLIFO

La idea de construir la función GLIFO() surgió mientras daba mis primeros pasos con AppSheet.

Descubriendo Google AppSheet en 48 horas

Como explicaba en cierta sección del mencionado artículo, resulta que AppSheet dispone de una interesante función denominada TEXT_ICON():

TEXT_ICON(cadena_de_texto)

Esta función, que a menudo se utiliza de manera conjunta con INITIALS(), devuelve una pequeña imagen rectangular en color gris (bastante anodina, todo sea dicho) generada a partir del texto que se le pasa como parámetro, por ejemplo:

TEXT_ICON(INITIALS("Google Sheets"))
Está claro para qué sirve INITIALS(), ¿verdad?

En una de tantas ocasiones en las que iba y venía entre el editor de AppSheet y la hoja de cálculo de Google que usaba como almacén de datos para la pequeña app de prueba que estaba construyendo, reparé en esto:

¿AppSheet confiando en un servicio externo para una funcionalidad propia?

Esas pequeñas imágenes tan prácticas parecían estar siendo obtenidas "al vuelo" desde un servicio externo haciendo una petición GET a cierta URL con una serie de parámetros de ruta y consulta, absolutamente explícitos, utilizados para establecer tanto el tamaño de la imagen como el texto a generar.

La cosa quedó totalmente confirmada al prestar un poco más de atención al asistente de expresiones en el editor de fórmulas de AppSheet.

Más "pistas"(que yo no vi inicialmente). En efecto, soy de los que no encuentran nada en la nevera.

😵 Si te estás preguntando por qué demonios AppSheet está confiando en un servicio externo vinculado a un dominio propiedad de lo que parece ser una empresa de alquiler de almacenes industriales... pues ya somos dos. Raro, raro.

Jugando un poco con esos parámetros del URL fue inmediato dar con un modo de generar imágenes con distintos colores de fondo y texto:

https://via.placeholder.com/alto/ancho/color_fondo/color_texto?text=TEXTO

El color de fondo y texto se expresa como un valor RGB en hexadecimal (sin el prefjo habitual #). Además, si el texto contiene espacios es necesario sustituirlos por %20.

via.placeholder.com/100x75/fbbc04/ffffff/?text=GS
via.placeholder.com/100/0c1a78/46bdc6/?text=GS
via.placeholder.com/100x75/00a000/ffffff/?text=G%20S

A partir de ahí, parametrizar la construcción de estos URL mágicos, pasárselos a la función IMAGE() de Google Sheets y montar una función con nombre para empaquetarlo todo (y ponerle un lazo 🎀) fue simplemente... inevitable 😬.

Hazte una copia aquí de la hoja de cálculo donde encontrarás todo este tinglado:

Las funciones GLIFO() / GLYPH() v1 en acción.

👀 bit.ly/fx-glifo-sheets

☝ ¿Podríamos haber implementado la función GLIFO por medio de una función personalizada Apps Script? Negativo. En estos momentos, nuestras queridas funciones personalizadas no son capaces de devolver una imagen como resultado.

🤷 ¿Y ya está?

🙅‍♂️ ¡Por supuesto que no está!

¿No pensarías que iba a dar esto por concluido sin que nos ensuciáramos un poco las manos con el barro de la implementación, verdad?

Pero para eso tenemos una segunda versión, más avanzada, de esta función.

La segunda función GLIFO

Esto de usar un servicio externo para obtener imágenes de ciertas dimensiones con texto personalizado en su interior despertó mi interés, así que investigué un poco más.

Resulta que hay docenas de ellos. Se trata de servicios que suelen utilizarse para construir maquetas y prototipos de productos visuales. Lo cierto es que algunos son de lo más bizarro, capaces de surtirte de una fuente aparentemente inagotable de fotos de cosas que echar a las brasas en una barbacoa, tipos con barba más o menos pintorescos o, cómo no, también adorables gatitos 😸. Una locura.

https://placekitten.com/300/300
https://loremflickr.com/300/300
https://baconmockup.com/300/300
https://placebeard.it/300
https://placebear.com/300/300
https://picsum.photos/300

⚠️ Me he encontrado con que un número de servicios que tal vez podría considerarse alarmantemente alto ya no parecen estar operativos, lo que en principio no inspira demasiada confianza. Llámame negativo, pero no dejo de pensar en todas esas apps de AppSheet cuyas miniaturas tal vez podrían irse al carajo en cualquier momento.

Pero volvamos a transitar por la senda del optimismo 🦄.

También he dado con un par de generadores, betterplaceholder.com y placehold.co, que admiten diferentes tipos de letra, lo que necesariamente me ha llevado a preparar una segunda versión de mi función de generación de miniaturas de texto.

El último de ellos, placehold.com, me ha parecido el más completo, así que me he basado en él para construir esta nueva versión 2 de la función GLIFO.

Este servicio permite:

  • Especificar el tamaño, en píxeles, como /anchoxalto o simplemente /lado, lo que generará una imagen perfectamente cuadrada. El tamaño máximo parece ser 4000x4000 y el mínimo 20x20.
  • Escoger el formato de la imagen, se admiten los valores svg, png, jpg, jpeg y webp. El formato SVG (predeterminado) resulta ideal cuando se trata de generar miniaturas de elementos de texto que se van a insertar en el interior de las celdas de una hoja de cálculo porque al modificar su tamaño crecerán o encogerán sin perder un ápice de calidad. Por esa razón, me ha parecido adecuado prescindir en mi función de la parametrización del formato de salida, de modo que las imágenes generadas siempre serán de tipo SVG. Por tanto, los parámetros de tamaño (ancho y alto) solo servirán para determinar las proporciones de la imagen generada. Sí, vas a poder omitirlos fácilmente.
  • Indicar el color de fondo y de texto, tanto mediante valores hexadecimales (con uno o dos dígitos para cada componente de color) o como nombres de color css (por ejemplo, "cyan").
  • Obtener texto con espacios o en varias líneas.
  • Escoger entre 10 tipografías distintas para el texto.
  • Generar imágenes de alta densidad (retina). Esta posibilidad no la he implementado.
  • Generar vídeos. Esta tampoco.

Por ejemplo, un URL valido para este servicio de generación de imágenes puede ser:

https://placehold.co/100/0c1a78/cyan?text=G+S\nF+N&font=Lora

Obtén aquí la hoja de cálculo del tinglado v2:

Las funciones GLIFO() / GLYPH() v2 en acción.

👀 bit.ly/fx-glifo-sheets-v2

¿Hablamos ahora de la implementación?

Implementando GLIFO (v2)

Vamos a Datos Funciones con nombre.

Diseñar funciones personalizadas medianamente complejas dentro del editor de funciones con nombre no es la cosa más cómoda del mundo.

Es cierto que desde hace algún tiempo (no sabría precisar exactamente desde cuándo 🤔) contamos con un discreto tirador en el panel lateral del editor para hacerlo más ancho y poder trabajar así con mayor comodidad.

Las pequeñas mejoras de usabilidad siempre son bienvenidas.

Aún así, el cajetín de edición no es multilínea —ni siquiera a medias— ni tampoco disponemos allí de las mismas funciones de ayuda contextual (sintaxis de funciones, emparejado de paréntesis, coloreado de sintaxis completo) que sí disfrutamos al escribir fórmulas dentro de cualquier celda.

Por esa razón, yo prefiero escribir y depurar mis funciones con nombre como si de fórmulas convencionales se tratase.

¿Qué pasa entonces con los marcadores de posición de los argumentos que se utilizan para parametrizar la función?

Estableciendo el nombre, los marcadores de posición de argumentos y la fórmula de una función con nombre.

💡 Idea feliz: Mi consejo es que utilices intervalos con nombre para designar los parámetros de tu función. De esa forma solo tendrás que definir otros tantos marcadores de posición de argumentos con idénticos nombres y copiar y pegar tu fórmula en el cajetín de edición de funciones con nombre una vez que hayas comprobado que funciona correctamente.

Para ello puedes montarte un pequeño Entorno de Diseño de Funciones con Nombre (EDFN™) como este: 

Casero, pero bastante funcional.

Fíjate en los marcadores de posición de la función con nombre, tienen exactamente la misma nomenclatura que los intervalos con nombre que hemos usado mientras construíamos la función como si de una fórmula convencional se tratase.

Las funciones con nombre utilizan marcadores de posición (algo así como variables) para identificar sus parámetros de entrada.

Vamos ahora a echarle un vistazo rápido a la implementación de esta 2ª versión de la función GLIFO.


=MAP(intervalo; LAMBDA(elemento;
  SI.ERROR(
    IMAGE(
      "https://placehold.co/" &
      IFS(
        Y(ESNUMERO(anchura); ESNUMERO(altura)); anchura & "x" & altura;
        ESNUMERO(anchura); anchura;
        ESNUMERO(altura); altura;
        NO(O(ESNUMERO(anchura); ESNUMERO(altura))); 100
      )  & "/" &
      LET(cf; MINUSC(color_fondo);
        SI(
          REGEXMATCH(cf; "^(?:([0-9a-f]{6})|(?:[0-9a-f]{3})|(?:[a-z]{3,20}))$");
          cf;
          "0c1a78"
        )
      ) & "/" &
      LET(ct; MINUSC(color_texto);
        SI(
          REGEXMATCH(ct; "^(?:([0-9a-f]{6})|(?:[0-9a-f]{3})|(?:[a-z]{3,20}))$");
          ct;
          "46bdc6"
        )
      )  & "/svg?text=" &
      SI(
        SI(ESLOGICO(solo_iniciales); solo_iniciales; VERDADERO);
        ArrayFormula(JOIN(; IZQUIERDA(SPLIT(elemento; " "); 1)));
        SUSTITUIR(SUSTITUIR(elemento; " "; "+"); CARACTER(10); "\n")
      ) &  "&font=" &
      SWITCH(
        fuente;
        1; "Lato";
        2; "Lora";
        3 ;"Montserrat";
        4; "Open+Sans";
        5; "Oswald";
        6; "Playfair+Display";
        7; "PT+Sans";
        8; "Raleway";
        9; "Roboto";
        10; "Source+Sans+Pro";
        "Roboto"
      )
    );
  "😖")
))

1️⃣ Comenzamos en la línea 1 con una estructura MAP() / LAMBDA() para que la función pueda generar:

  • Una sola miniatura a partir de una cadena de texto única.
  • Todas las miniaturas  correspondientes a un conjunto de cadenas de texto resultantes de la evaluación de una expresión matricial o simplemente dispuestas en un intervalo de celdas.

El identificador elemento representa cada una de las cadenas de texto que se van a procesar.

2️⃣ Con SI.ERROR() en la línea 2 cazaremos cualquier circunstancia no controlada durante su evaluación. En caso de problemas, la función devolverá "😖" en lugar de la anhelada imagen, que siempre puede quedar mejor que un feo mensaje de error.

3️⃣ A partir de la línea 3 se va contruyendo el URL conforme a las especificaciones del servicio disponible en placehold.co, insertando cuidadosamente los argumentos de altura, anchura, color de texto y fondo, extracción de iniciales y tipografía. Este URL se pasará como argumento a la función IMAGE() para desencadenar la generación de las miniaturas.

En un buen momento para hablar de esos elusivos argumentos opcionales de los que te decía hace un rato que no estaban soportados, por ahora, en las funciones con nombre.

Sin embargo, mi función GLIFO, cuya sintaxis es:

GLIFO(
intervalo; solo_iniciales; color_texto; color_fondo; fuente; anchura; altura
)

...admite ser utilizada también así:

=GLIFO(C4:C8;;;;2;;)

En este caso se omiten y establecen determinados valores por defecto para los argumentos de:

  • Selección del modo de extracción de iniciales de palabras → VERDADERO.
  • Color de texto → 46bdc6  (azul claro).
  • Color de fondo → 0c1a78 (azul oscuro).
  • Anchura → 100.
  • Altura → 100.

En el ejemplo se ha seleccionado de manera explícita una tipografía (2Lora), pero de no haberlo hecho la función hubiera utilizado de manera predeterminado Roboto.

Evidentemente, esto resulta un tanto menos elegante que poder omitir totalmente todos los argumentos finales contiguos en los que no estamos interesados, por ejemplo:

=GLIFO(C4:C8)
Casi, pero no. Tal vez algún día.

Pero seguro que estarás de acuerdo conmigo en que a pesar de eso, esta alternativa no deja de suponer una mejora considerable sobre la situación inicial (especificar todos los parámetros de manera obligatoria ). Esperemos que el equipo de desarrollo de Google Sheets escuche nuestras súplicas y ponga oportuno remedio a esto 🙏.

¿Dónde está el truco pues? ¿Con qué artimaña se ha persuadido a nuestra pizpireta función con nombre para que acepte que ciertos parámetros son seudoopcionales?

Veamos cómo se tratan los argumentos utilizados para establecer la altura y anchura de la miniatura (líneas 5 - 10):

IFS(
Y(ESNUMERO(anchura); ESNUMERO(altura)); anchura & "x" & altura;
ESNUMERO(anchura); anchura;
ESNUMERO(altura); altura;
NO(O(ESNUMERO(anchura); ESNUMERO(altura))); 100
)

Fíjate, se hace un estudio de casos mediante la función IFS():

  • Si altura y anchura son números se utilizan ambos parámetros para construir el URL.
  • Si solo uno de ellos es un número, se utiliza como valor para generar una imagen perfectamente cuadrada.
  • Si ni anchura ni altura son valores numéricos, que es justo lo que pasará si nos los "saltamos" usando un punto y coma [;], se genera a piñón fijo una imagen de 100 píxeles de lado.

Cuando se omite un argumento de una función con nombre escribiendo un punto y coma, el marcador de posición correspondiente se considera vacío, por tanto:

ESBLANCO(marcador) → VERDADERO
ESNUMERO(marcador) → FALSO
ESTEXTO(marcador) → FALSO
ESLOGICO(marcador) → FALSO

⚠️ Utilizar la función ESBLANCO sobre el valor que se traslada a la función con nombre cuando nos saltamos un argumento devolverá un valor lógico verdadero. No obstante, debes tener en cuenta que si comparas un argumento omitido con 0 el resultado será también verdadero. Esto puede ocasionar confusiones cuando el argumento admita, por su naturaleza, valores iguales a 0 en un uso normal.

4️⃣ A continuación, en las líneas 11 - 24 se tratan los argumentos que definen los colores de fondo y texto de la imagen.

LET(cf; MINUSC(color_fondo);
SI(
REGEXMATCH(cf; "^(?:([0-9a-f]{6})|(?:[0-9a-f]{3})|(?:[a-z]{3,20}))$");
  cf;
  "0c1a78"
)
)

En el caso del color de fondo se utiliza la función REGEXMATCH() en la prueba lógica de una expresión condicional para determinar si estos argumentos se han indicado utilizando secuencias de texto (más o menos) aceptables. Lógicamente, si alguno de ellos ha sido omitido, la prueba fallará y se asignarán entonces un color por defecto (0c1a78 en el caso del fondo).

El tratamiento del parámetro que establece el color del texto es análogo, así que me lo salto.

5️⃣ En las líneas 25 - 29 se determina si deben extraerse las iniciales de la secuencia de texto facilitada, suponiendo que contenga varias palabras. De un modo análogo al caso de los argumentos de tamaño de la imagen, aquí se utiliza ESLOGICO() para comprobar si tenemos un argumento válido (verdadero o falso). En caso contrario se forzará la extracción de iniciales.

SI(
SI(ESLOGICO(solo_iniciales); solo_iniciales; VERDADERO);
ArrayFormula(JOIN(; IZQUIERDA(SPLIT(elemento; " "); 1)));
SUSTITUIR(SUSTITUIR(elemento; " "; "+"); CARACTER(10); "\n")
)

Además, aquí se realiza el tratamiento oportuno de los espacios en blanco y los saltos de línea que pudiera haber en el texto.

6️⃣ Ya solo nos queda por gestionar el parámetro usado para establecer la fuente del texto (líneas 30 - 43). Para ello se utiliza la función SWITCH(), que dispone de un valor por defecto. Ya sabes como va esto, si se omite el parámetro que permite escoger la fuente no se producirá ninguna coincidencia con alguno de los aceptados (1...10) y se seleccionará la fuente Roboto de manera predeterminada.

SWITCH(
fuente;
1; "Lato";
2; "Lora";
3 ;"Montserrat";
4; "Open+Sans";
5; "Oswald";
6; "Playfair+Display";
7; "PT+Sans";
8; "Raleway";
9; "Roboto";
10; "Source+Sans+Pro";
"Roboto"
)

Para evitar errores y facilitar el uso de la función, como puedes ver he optado por utilizar una codificación numérica para las tipografías.

Prepara una buena ayuda contextual para tus funciones con nombre que aclare este tipo de cosas. Tus usuarios lo agradecerán.

Comentarios finales y siguientes pasos

Espero que todo lo que te contado sobre las funciones con nombre te haya parecido interesante. Como te decía al comienzo del artículo, me apetecía escribir sobre ellas, así que no he podido dejar pasar la ocasión de hacerlo en el primer aniversario de su lanzamiento 🚀.

Después de unos meses en los que he creado docenas de funciones con nombre para simplificar mis fórmulas, ya puedo afirmar sin sombra de duda alguna que resultan de enorme ayuda a la hora de mejorar el trabajo con las hojas de cálculo de Google.

Creo que a lo largo del artículo habrás podido comprobar lo fácil que resulta añadir funciones al lenguaje de fórmulas de las hojas de cálculo de Google utilizando las estupendas funciones con nombre.

Si aún no te has animado a utilizarlas de manera habitual, mi consejo es que lo hagas cuanto antes. No hay vuelta atrás. Puedes inspirarte en esta sorprendente colección que contiene docenas de funciones con nombre creadas por el inagotable Max Makhrov.

☝ Con respecto a mi pequeño juguete de hoy, la función GLIFO, se me ocurren un par de mejoras que tal vez te resulte apetecible abordar.

La primera tiene que ver con la introducción de un argumento adicional para seleccionar otros proveedores de marcadores de posición. El que he utilizado es el más completo que he encontrado, pero no obstante puede ser conveniente contar con algún otro por si este falla de manera temporal o, esperemos que no, cae permanente. Creo que betterplaceholder.com puede ser una buena alternativa.

En este sentido, tengo que manifestar nuevamente mi extrañeza al comprobar que una herramienta de tomo y lomo como AppSheet depende del servicio montado por un tercero, que aparentemente nada tiene que ver con Google, para cimentar una funcionalidad propia 😱. En fin, cosas más raras se han visto en este mundo-como-servicio, supongo. 

Por otro lado, puesto que la función siempre genera imágenes en formato SVG, que son escaladas automáticamente y sin pérdida de calidad al modificar el tamaño de las celdas de la hoja de cálculo, tal vez podrían pulirse un poco los argumentos usados para establecer el tamaño, quizás reduciéndolos a un único parámetro decimal para controlar la relación de aspecto.

Nada más por hoy, hasta la próxima 👋.