El lenguaje de plantillas de calibre

El lenguaje de plantillas de calibre es un lenguaje específico de calibre que se utiliza en todo calibre para tareas como especificar rutas de archivo, dar formato a valores y calcular el valor de las columnas especificadas por el usuario. Ejemplos:

  • Especifar la estructura de carpetas y los nombres de archivo al guardar archivos de la biblioteca de calibre en el disco o en el lector de libros electrónicos.

  • Definir reglas para añadir iconos y colores a la lista de libros de calibre.

  • Definir columnas virtuales que contienen datos de otras columnas.

  • Búsqueda avanzada en la bibliotecas.

  • Búsqueda y sustitución avanzada de metadatos.

El lenguaje está construido en torno al concepto de una «plantilla», que especifica qué metadatos del libro se usan, las operaciones que se realizan sobre los metadatos y qué formato se aplica.

Plantillas básicas

Una plantilla básica consiste en una o varias expresiones de plantilla. Una expresión de plantilla consiste en texto y nombres entre llaves ({}) que se sustituyen por los metadatos correspondientes del libro que está siendo procesado. Por ejemplo, la plantilla predeterminada de calibre para guardar libros en un dispositivo tiene 4 expresiones de plantilla:

{author_sort}/{title}/{title} - {authors}

Para el libro «The Foundation» de «Isaac Asimov» da lugar a:

Asimov, Isaac/The Foundation/The Foundation - Isaac Asimov

Las barras no son expresiones de plantilla porque no están entre {}. Este tipo de texto se mantiene donde aparece. Por ejemplo, si la plantilla es:

{author_sort} Some Important Text {title}/{title} - {authors}

entonces para «The Foundation» la plantilla produce:

Asimov, Isaac Some Important Text The Foundation/The Foundation - Isaac Asimov

Una expresión de plantilla tiene acceso a todos los metadatos disponibles en calibre, incluidas las columnas personalizadas que haya creado, usando su el nombre de búsqueda de una columna. Para obtener el nombre de búsqueda o consulta de una columna (también llamada campo), pase el cursor sobre el encabezado de la columna en la lista de libros de calibre. Los nombres de búsqueda para las columnas personalizadas siempre empiezan por #. Para columnas personalizadas de tipo serie, siempre hay un campo adicional llamado #nombre de búsqueda_index que es el índice de serie para el libro en la serie. Por ejemplo, si tiene una columna personalizada de serie llamada #miserie, también habrá una columna llamada #miserie_index. La columna de índice para la serie normal se llama series_index.

Además de los campos basados en columnas normales, también puede usar:

  • {formats} - Una lista de los formatos disponibles en la biblioteca de calibre para un libro

  • {identifiers:select(isbn)} - El ISBN del libro

Si un campo de metadatos para un libro determinado no está definido, entonces el campo en la plantilla se sustituye por un texto vacío (''). Por ejemplo, considere la siguiente plantilla:

{author_sort}/{series}/{title} {series_index}

Si el libro de Asimov «Second Foundation» está en la serie «Foundation», la plantilla produce:

Asimov, Isaac/Foundation/Second Foundation 3

Si no se ha introducido la serie para el libro, la plantilla produce:

Asimov, Isaac/Second Foundation

El procesador de plantillas elimina automáticamente barras múltiples y espacios iniciales o finales.

Formato avanzado

Además de sustituir metadatos, las plantillas pueden incluir texto adicional de manera condicional y controlar el formato de los datos sustituidos.

Incluir texto de mane condicional

A veces puede querer que aparezca un texto en la salida sólo si un campo no está vacío. Un caso común es series y series_index, para los que puede querer o nada o ambos valores separados por un guión. calibre tiene en cuenta este caso usando una sintaxis de expresión de plantilla especial.

Por ejemplo, y usando el caso anterior de la serie «Foundation», suponga que quiere que la plantilla produzca Foundation - 3 - Second Foundation. Esta plantilla cumple el cometido:

{series} - {series_index} - {title}

Sin embargo, si un libro no tiene serie, la plantilla producirá - - el título, que probablemente no sea lo que deseaba. Normalmente preferiría que el resultado fuese el título sin los guiones. Puede conseguir esto usando la siguiente sintaxis de plantilla:

{campo:|prefijo|sufijo}

Esta expresión de plantilla dice que si campo tiene el valor XXXX, el resultado será prefijoXXXXXsufijo. Si campo está vacío (no tiene ningún valor) el resultado será un texto vacío (nada), porque el prefijo y el sufijo se ignoran. Tanto el prefijo como el sufijo pueden contener espacios.

No use subplantillas ({…}) o funciones (ver más adelante) en el prefijo o el sufijo.

Usando esta sintaxis, podemos resolver el problema anterior de los libros sin serie con esta plantilla:

{series}{series_index:| - | - }{title}

Los guiones se incluirán solamente si el libro tiene índice de serie, que sólo tendrá si tiene una serie. Continuando con el ejemplo de la serie «Foundation», la plantilla producirá Foundation - 1 - Second Foundation.

Notas:

  • Debe incluir los dos puntos tras el nombre de búsqueda si usa un prefijo o sufijo.

  • Debe incluir ambos caracteres | o ninguno. No está permitido usar sólo uno como en {campo:| - }.

  • Es posible asignar un texto vació para el prefijo o sufijo, como en {series:|| - }. La plantilla {title:||} es lo mismo que {title}.

Formato

Supongamos que queremos asegurarnos de que series_index aparezca con dígitos, con ceros a la izquierda. Se consigue de esta manera:

{series_index:0>3s} - Tres dígitos con ceros a la izquierda

Para obtener ceros a la derecha use:

{series_index:0<3s} - Tres dígitos con ceros a la derecha

Si usa índices de serie con decimales (por ejemplo 1.1), puede querer asegurarse de que los decimales queden alineados. Por ejemplo, puede que los índices 1 y 2.5 aparezcan como 01.00 y 02.50 para que se ordenen correctamente en un dispositivo que utilice un orden lexicográfico. Para ello, use:

{series_index:0>5.2f} - Cinco caracteres que incluyen dos dígitos con cero a la izquierda, un punto decimal y dos dígitos tras el punto decimal.

Si quiere sólo las dos primeras letras de los datos, use:

{author_sort:.2} - Sólo las primeras dos letras del orden de autor

Gran parte del formato del lenguaje de plantillas de calibre proviene de Python. Para obtener más detalles sobre la sintaxis de estas operaciones de formato avanzadas, vea la documentación de Python (en inglés).

Usar plantillas para definir columnas personalizadas

Las plantillas pueden usarse para mostrar información que no está en los metadatos de calibre o para mostrar los metadatos de una manera diferente al formato normal de calibre. Por ejemplo, puede querer mostrar el ISBN, un campo que calibre no muestra. Puede conseguirlo creando una columna personalizadad de tipo columna generada a partir de otras columnas (en lo sucesivo llamadas columnas compuestas), e introduciendo una plantilla para generar el texto mostrado. La columna mostrará el resultado de evaluar la plantilla. Por ejemplo, para mostrar el ISBN, cree la columna e introduzca {identifiers:select(isbn)} en el cuadro de plantilla. Para mostrar una columna que contenga los valores de dos campos personalizados de serie separados por una coma, use {#serie1:||,}{#serie2}.

Las columnas compuestas pueden utilizar cualquier opción de plantilla, incluidas las de formato.

Nota: No puede modificar los datos mostrados en una columna compuesta, debe modificar las columnas de origen. Si modifica una columna compuesta, por ejemplo pulsando dos veces sobre ella, calibre abrirá la plantilla para modificarla, no los datos resultantes.

Plantillas y controles de metadatos

Los paneles de conexiones se usan para cambiar los metadatos escritos en los libros durante las operaciones de guardado en disco y de envío a dispositivo. Un panel de conexiones le permite especificar una plantilla para suministrar los datos que se escribirán en los metadatos del libro. Puede usar los paneles de conexiones para modificar los siguientes campos: authors, author_sort, language, publisher, tags, title, title_sort. Esa función es útil para los que quieren usar metadatos diferentes en los libros de los dispositivos, para solucionar problemas de ordenación o de visualización.

Cuando cree un panel de conexiones, especifique el formato y dispositivo para los que se usará. Hay un dispositivo especial save_to_disk, que se usa para guardar formatos (en lugar de enviarlos a un dispositivo). Una vez que ha elegido el formato y dispositivo, elija los campos de metadatos para cambiar, y suministre plantillas para obtener los nuevos valores. Estas plantillas están conectadas con sus campos de destino, de ahí el nombre panel de conexiones. Por supuesto, puede usar columnas compuestas en estas plantillas.

Los paneles de conexiones son muy flexibles y pueden escribirse en modo de función única, en modo programación de plantillas, en modo de programación general o en modo de plantilla de Python.

Cuando un panel de conexiones pueda aplicarse (servidor de contenido, guardado en disco o envío a dispositivo), calibre busca los paneles definidos para elegir el correcto según el formato y dispositivo. Por ejemplo, para encontrar el panel de conexiones apropiado para enviar un libro EPUB a un dispositivo ANDROID, calibre busca en los paneles en el siguiente orden:

  • un panel de conexiones con una coincidencia exacta de formato y dispositivo, por ejemplo: EPUB y ANDROID

  • un panel de conexiones con una coincidencia exacta de formato y el dispositivo especial any device, por ejemplo EPUB y any device

  • un panel de conexiones con el formato especial any format y una coincidencia exacta de dispositivo, por ejemplo: any format y ANDROID

  • un panel de conexiones con any format y any device

Los campos etiquetas y autores tienen un trato especial, debido a que ambos pueden tener más de un elemento. Un libro puede poseer varias etiquetas y varios autores. Cuando indique que desea cambiar uno de estos campos, la plantilla se examina para comprobar si hay más de un elemento. Para las etiquetas, el resultado se divide dondequiera que calibre encuentre una coma. Por ejemplo, si la plantilla produce el valor Intriga, Terror, el resultado serán dos etiquetas: Intriga y Terror. No existe manera de poner una coma dentro de una etiqueta.

Lo mismo ocurre con los autores, pero usando un carácter diferente para el corte, un signo «&» en lugar de una coma. Por ejemplo, si la plantilla produce el valor Blogs, Joe&Posts, Susan, el libro acabará con dos autores, Blogs, Joe y Posts, Susan. Si la plantilla produce el valor Blogs, Joe;Posts, Susan, el libro tendrá un autor con un nombre peculiar.

Los paneles de conexiones afectan a los metadatos escritos en el libro cuando se guarda en disco o se escribe en un dispositivo. Los paneles de conexiones no afectan a los metadatos usados por las funciones Guardar en el disco Enviar al dispositivo para crear los nombres de archivo. En lugar de ello, los nombres de archivo se construyen usando las plantillas introducidas en la ventana de preferencias correspondiente.

Usar funciones en plantillas: modo de función única

Supongamos que desea mostrar el valor de un campo en mayúsculas, aunque normalmente el valor del campo tiene sólo las iniciales en mayúscula. Puede conseguir esto usando las funciones de plantilla. Por ejemplo, para mostrar el título en mayúsculas use la función uppercase, como en {title:uppercase()}. Para mostrarlo con las iniciales en mayúscula use {title:titlecase()}.

Las funciones van en la parte del formato de la plantilla, después de : y antes del primer | o del } de cierre si no se usa ningún prefijo o sufijo. Si tiene tanto un formato como una referencia de función, la función va después de otro :. Las funciones devuelven el valor de la columna especificada en la plantilla, modificado de manera adecuada.

La sintaxis para usar funciones es una de:

{lookup_name:function(arguments)}
{lookup_name:format:function(arguments)}
{lookup_name:function(arguments)|prefix|suffix}
{lookup_name:format:function(arguments)|prefix|suffix}

Los nombres de función deben is siempre seguidos de paréntesis de apertura y cierre. Algunas funciones requieren valores adicionales (argumentos), y éstos van dentro de los paréntesis. Los argumentos se separan por comas. Una coma literal (como texto, no como separador de argumentos) debe ir precedida de una barra invertida (\). El último (o único) argumento no puede contener un paréntesis de cierre textual.

Las funciones se evalúan antes de las especificaciones de formato y del prefijo y sufijo. Véase más abajo un ejemplo de cómo usar un formato y una función.

Importante: Si tiene experiencia en programación, tenga en cuenta que la sintaxis del modo de función única no es la que podría esperarse. Los textos van sin comillas y los espacios son importantes. Todos los argumentos se interpretan como constantes; no hay expresiones.

No use subplantillas ({…}) como argumentos de función. En su lugar, use el modo de programación de plantilla o el modo de programación general.

Notes on calling functions in Single Function Mode:

  • When functions are used in Single Function Mode, the first parameter, value, is automatically replaced by the content of the field specified in the template. For example, when the template {title:capitalize()} is processed, the content of the title field is passed as the parameter value to the capitalize function.

  • In the function documentation, the notation [something]* means that something can be repeated zero or more times. The notation [something]+ means that the something is repeated one or more times (must exist at least one time).

  • Some functions use regular expressions. In the template language regular expression matching is case-insensitive.

Functions are documented in Template function reference. The documentation tells you what arguments the functions require and what the functions do. For example, here is the documentation of the ifempty function.

  • ifempty(value, text_if_empty) – if the value is not empty then return that value, otherwise return text_if_empty.

You see that the function requires two arguments, value and text_if_empty. However, because we are using Single Function Mode, we omit the value argument, passing only text_if_empty. For example, this template:

{tags:ifempty(No tags on this book)}

shows the tags for a book, if any. If it has no tags then it show No tags on this book.

The following functions are usable in Single Function Mode because their first parameter is value.

  • capitalize(value) – returns the value with the first letter in upper case and the rest lower case.

  • ceiling(value) – returns the smallest integer greater than or equal to value.

  • cmp(value, y, lt, eq, gt) – compares value and y after converting both to numbers.

  • contains(value, pattern, text_if_match, text_if_not_match) – checks if the value is matched by the regular expression pattern

  • date_arithmetic(value, calc_spec, fmt) – Calculate a new date from value using calc_spec.

  • floor(value) – returns the largest integer less than or equal to value.

  • format_date(value, format_string) – format the value, which must be a date string, using the format_string, returning a string.

  • format_number(value, template) – interprets the value as a number and formats that number using a Python formatting template such as {0:5.2f} or {0:,d} or ${0:5,.2f}.

  • fractional_part(value) – returns the part of the value after the decimal point.

  • human_readable(value) – expects the value to be a number and returns a string representing that number in KB, MB, GB, etc.

  • ifempty(value, text_if_empty) – if the value is not empty then return that value, otherwise return text_if_empty.

  • language_strings(value, localize) – return the language names for the language codes (see here for names and codes) passed in value.

  • list_contains(value, separator, [ pattern, found_val, ]* not_found_val) – interpret the value as a list of items separated by separator, checking the pattern against each item in the list.

  • list_count(value, separator) – interprets the value as a list of items separated by separator and returns the number of items in the list.

  • list_count_matching(value, pattern, separator) – interprets value as a list of items separated by separator, returning the number of items in the list that match the regular expression pattern.

  • list_item(value, index, separator) – interpret the value as a list of items separated by separator, returning the “index’th item.

  • list_sort(value, direction, separator) – return value sorted using a case-insensitive lexical sort.

  • lookup(value, [ pattern, key, ]* else_key) – The patterns will be checked against the value in order

  • lowercase(value) – returns the value in lower case.

  • mod(value, y) – returns the floor of the remainder of value / y.

  • rating_to_stars(value, use_half_stars) – Returns the value as string of star () characters.

  • re(value, pattern, replacement) – return the value after applying the regular expression.

  • re_group(value, pattern [, template_for_group]*) – return a string made by applying the regular expression pattern to value and replacing each matched instance

  • round(value) – returns the nearest integer to value.

  • select(value, key) – interpret the value as a comma-separated list of items with each item having the form id:id_value (the calibre identifier format).

  • shorten(value, left_chars, middle_text, right_chars) – Return a shortened version of the value

  • str_in_list(value, separator, [ string, found_val, ]+ not_found_val) – interpret the value as a list of items separated by separator then compare string against each value in the list.

  • subitems(value, start_index, end_index) – This function breaks apart lists of tag-like hierarchical items such as genres.

  • sublist(value, start_index, end_index, separator) – interpret the value as a list of items separated by separator, returning a new list made from the items from start_index to end_index.

  • substr(value, start, end) – returns the start’th through the end’th characters of value

  • swap_around_articles(value, separator) – returns the value with articles moved to the end.

  • swap_around_comma(value) – given a value of the form B, A, return A B.

  • switch(value, [patternN, valueN,]+ else_value) – for each patternN, valueN pair, checks if the value matches the regular expression patternN

  • test(value, text_if_not_empty, text_if_empty) – return text_if_not_empty if the value is not empty, otherwise return text_if_empty.

  • titlecase(value) – returns the value in title case.

  • transliterate(value) – Return a string in a latin alphabet formed by approximating the sound of the words in value.

  • uppercase(value) – returns the value in upper case.

Usar funciones y formato en la misma plantilla

Supongamos que tiene una columna personalizada con números enteros #myint, que quiere mostrar con ceros a la izquierda, como en 003. Una posibilidad es usa como formato 0>3s. Sin embargo, de manera predeterminada, si un número (entero o decimal) es igual a cero, el valor se muestra como un texto vacío, así que el valor cero producirá un texto vacío, no 000. Si quiere ver los valores como 000, debe usar tanto el texto de formato como la función ifempty para cambiar el valor vacío a cero de nuevo, la plantilla sería:

{#myint:0>3s:ifempty(0)}

Tenga en cuenta que puede usar también prefijo y sufijo. Si desea que el número aparezca como [003] o [000], use la plantilla:

{#myint:0>3s:ifempty(0)|[|]}

Modo de programación general

El Modo de programación general (MPG) sustituye las expresiones de plantilla por un programa escrito en el lenguaje de plantillas. La sintaxis del lenguaje está definida por la siguiente gramática:

program         ::= 'program:' expression_list
expression_list ::= top_expression [ ';' top_expression ]*
top_expression  ::= or_expression
or_expression   ::= and_expression [ '||' and_expression ]*
and_expression  ::= not_expression [ '&&' not_expression ]*
not_expression  ::= [ '!' not_expression ]* | concatenate_expr
concatenate_expr::= compare_expr [ '&' compare_expr ]*
compare_expr    ::= add_sub_expr [ compare_op add_sub_expr ]
compare_op      ::= '==' | '!=' | '>=' | '>' | '<=' | '<' |
                    'in' | 'inlist' | 'inlist_field' |
                    '==#' | '!=#' | '>=#' | '>#' | '<=#' | '<#'
add_sub_expr    ::= times_div_expr [ add_sub_op times_div_expr ]*
add_sub_op      ::= '+' | '-'
times_div_expr  ::= unary_op_expr [ times_div_op unary_op_expr ]*
times_div_op    ::= '*' | '/'
unary_op_expr   ::= [ add_sub_op unary_op_expr ]* | expression
expression      ::= identifier | constant | function | assignment | field_reference |
                    if_expr | for_expr | break_expr | continue_expr |
                    '(' expression_list ')' | function_def
field_reference ::= '$' [ '$' ] [ '#' ] identifier
identifier      ::= id_start [ id_rest ]*
id_start        ::= letter | underscore
id_rest         ::= id_start | digit
constant        ::= " string " | ' string ' | number
function        ::= identifier '(' expression_list [ ',' expression_list ]* ')'
function_def    ::= 'def' identifier '(' top_expression [ ',' top_expression ]* ')' ':'
                    expression_list 'fed'
assignment      ::= identifier '=' top_expression
if_expr         ::= 'if' condition 'then' expression_list
                    [ elif_expr ] [ 'else' expression_list ] 'fi'
condition       ::= top_expression
elif_expr       ::= 'elif' condition 'then' expression_list elif_expr | ''
for_expr        ::= for_list | for_range
for_list        ::= 'for' identifier 'in' list_expr
                    [ 'separator' separator_expr ] ':' expression_list 'rof'
for_range       ::= 'for' identifier 'in' range_expr ':' expression_list 'rof'
range_expr      ::= 'range' '(' [ start_expr ',' ] stop_expr
                    [ ',' step_expr [ ',' limit_expr ] ] ')'
list_expr       ::= top_expression
break_expr      ::= 'break'
continue_expr   ::= 'continue'
separator_expr  ::= top_expression
start_expr      ::= top_expression
stop_expr       ::= top_expression
step_expr       ::= top_expression
limit_expr      ::= top_expression

Notas:

  • una top_expression siempre tiene un valor. El valor de una expression_list es el valor de la última top_expression de la lista. Por ejemplo, el valor de la expression_list 1;2;'blabla';3 es 3.

  • En un contexto lógico, cualquier valor que no sea vacío es True (verdadero)

  • En un contexto lógico, el valor vacío es False (falso)

  • Textos y números pueden usarse indistintamente. Por ejemplo, 10 y '10' son lo mismo.

  • Los comentarios son líneas que empiezan por un carácter #. No se permiten comentarios que empiecen a mitad de línea.

Prioridad de operadores

La prioridad de los operadores (orden de evaluación) de mayor (se evalúa primero) a menor (se evalúa el último) es:

  • Llamadas a función, constantes, expresiones entre paréntesis, expresiones de declaración, expresiones de asignación, referencias de campo.

  • Más (+) y menos (-) unarios. Estos operadores se evalúan de derecha a izquierda.

    Estas y todas las otras operaciones aritméticas devuelven enteros si la expresión resulta en una parte decimal igual a cero. Por ejemplo, si la expresión devuelve 3.0 se cambia a 3.

  • Multiplicación (*) y división (/). Estas operaciones son asociativas y se evalúan de izquierda a derecha. Use paréntesis si quiere cambiar el orden de evaluación.

  • Suma (+) y resta (-). Estas operaciones son asociativas y se evalúan de izquierda a derecha.

  • Comparaciones numéricas y de textos. Estas operaciones devuelven '1' si la comparación tiene éxito, en caso contrario un texto vacío (''). Las comparaciones no son asociativas: a < b < c es un error de sintaxis.

  • Concatenación de textos (&). El operador & devuelve un texto formado por concatenación de las expresiones a su izquierda y derecha. Ejemplo: 'aaa' & 'bbb' devuelve 'aaabbb'. El operador es asociativo y se evalúa de izquierda a derecha.

  • Negación lógica unaria (!). Esta operación devuelve '1' si la expresión es False (da como resultado un texto vacío), en caso contrario ''.

  • Y lógico (&&). Esta operación devuelve '1' si ambas expresiones a derecha e izquierda son True, o un texto vacío ('') si alguna es False. Es asociativa, se evalúa de izquierda a derecha y con cortocircuito https://chortle.ccsu.edu/java5/Notes/chap40/ch40_2.html.

  • O lógico (||). Esta operación devuelve '1' si alguna de las expresiones a derecha e izquierda es True, o un '' si ambas son False. Es asociativa, se evalúa de izquierda a derecha y con cortocircuito https://chortle.ccsu.edu/java5/Notes/chap40/ch40_8.html.

Referencias de campo

A field_reference evaluates to the value of the metadata field named by lookup name that follows the $ or $$. Using $ is equivalent to using the field function. Using $$ is equivalent to using the raw_field function. Examples:

* $authors ==> field('authors')
* $#genre ==> field('#genre')
* $$pubdate ==> raw_field('pubdate')
* $$#my_int ==> raw_field('#my_int')

Expresiones condicionales

Las expresiones condicionales (if_expression) evalúan primero la condition. Si la condition es True (un valor no vacío) entonces se evalúa expression_list en la sentencia then. Si es False, entonces se evalúa expression_list en la sentencia elif o else, si existen. Las partes elif y else son opcionales. Las palabras if, then, elif, else y fi están reservadas, no pueden usarse como nombres de identificador. Se pueden añadir espacios y saltos de línea donde sea conveniente. La condition es una top_expression, no usa expression_list, no se permite punto y coma. Los elementos de tipo expression_list son secuencias de top_expression separadas por punto y coma. Una expresión condicional devuelve el resultado de la última top_expression en la expression_list evaluada, o un texto vacío si no se evaluó ninguna expression_list.

Ejemplos:

* program: if field('series') then 'yes' else 'no' fi
* program:
      if field('series') then
          a = 'yes';
          b = 'no'
      else
          a = 'no';
          b = 'yes'
      fi;
      strcat(a, '-', b)

Ejemplo de if anidado:

program:
  if field('series') then
    if check_yes_no(field('#mybool'), '', '', '1') then
      'yes'
    else
      'no'
    fi
  else
    'no series'
  fi

Como se ha mencionado, un if produce un valor. Esto significa que todas las siguientes expresiones son equivalentes:

* program: if field('series') then 'foo' else 'bar' fi
* program: if field('series') then a = 'foo' else a = 'bar' fi; a
* program: a = if field('series') then 'foo' else 'bar' fi; a

For example, this program returns the value of the series column if the book has a series, otherwise the value of the title column:

program: field(if field('series') then 'series' else 'title' fi)

Expresiones de bucle

The for expression iterates over a list of values, processing them one at a time. The list_expression must evaluate either to a metadata field lookup name e.g., tags or #genre, or to a list of values. The range generates a list of numbers. If the result is a valid lookup name then the field’s value is fetched and the separator specified for that field type is used. If the result isn’t a valid lookup name then it is assumed to be a list of values. The list is assumed to be separated by commas unless the optional keyword separator is supplied, in which case the list values must be separated by the result of evaluating the separator_expr. A separator cannot be used if the list is generated by range(). Each value in the list is assigned to the specified variable then the expression_list is evaluated. You can use break to jump out of the loop, and continue to jump to the beginning of the loop for the next iteration.

Ejemplo: Esta plantilla elimina el primer nombre jerárquico para cada valor de Género (#genre`), construyendo una lista con los nuevos nombres.

program:
  new_tags = '';
  for i in '#genre':
    j = re(i, '^.*?\.(.*)$', '\1');
    new_tags = list_union(new_tags, j, ',')
  rof;
  new_tags

Si el Género original es Historia.Militar, Ciencia ficción.Historia alternativa, Léeme, la plantilla devolverá Militar, Historia alternativa, Léeme. Puede usar esta plantilla en Modificar metadatos en masa > Buscar y sustituir con Buscar establecido en plantilla para eliminar el primer nivel de la jerarquía y asignar el valor resultante a Género.

Nota: la última línea en la plantilla, new_tags, no es estrictamente necesaria en este caso, porque for devuelve el valor de la última top_expression en expression_list. El valor de una asignación (assignment) es el valor de su expresión, así que el valor del for es lo que se asignó a new_tags.

Definición de funciones

If you have repeated code in a template then you can put that code into a local function. The def keyword starts the definition. It is followed by the function name, the argument list, then the code in the function. The function definition ends with the fed keyword.

Los argumentos son posicionales. Cuando se invoca una función, los argumentos proporcionados se hacen corresponder de izquierda a derecha con los parámetros definidos, con el valor del argumento asignado al parámetro. Es un error proporcionar más argumentos que parámetros definidos. Los parámetros pueden tener valores predeterminados, como a = 25. Si no se proporciona un argumento para un parámetro, se usará el valor predeterminado, en caso contrario el parámetro será un texto vacío.

La instrucción return se puede usar en una función local.

Una función debe definirse antes de poder usarse.

Ejemplo: Esta plantilla calcula una duración aproximada en años, meses y días a partir de un número de días. La función to_plural() da formato a los valores calculados. Tenga en cuenta que el ejemplo también usa el operador &:

program:
      days = 2112;
      years = floor(days/360);
      months = floor(mod(days, 360)/30);
      days = days - ((years*360) + (months * 30));

      def to_plural(v, str):
              if v == 0 then return '' fi;
              return v & ' ' & (if v == 1 then str else str & 's' fi) & ' '
      fed;

      to_plural(years, 'year') & to_plural(months, 'month') & to_plural(days,'day')

Operadores de relación

Relational operators return '1' if the comparison is true, otherwise the empty string ('').

Hay dos formas de operadores de relación: comparaciones de texto y comparaciones numéricas.

Las comparaciones de cadenas realizan comparaciones de cadenas sin distinguir entre mayúsculas y minúsculas utilizando el orden léxico. Los operadores de comparación de cadenas admitidos son ==, !=, <, <=, >, >=, in, inlist y inlist_field. Para el operador in, el resultado de la expresión de la izquierda se interpreta como un patrón de expresión regular. El operador in es Verdadero si el valor de la expresión regular de la izquierda coincide con el valor de la expresión de la derecha.

El operador inlist es verdadero si la expresión regular de la izquierda coincide con cualquiera de los elementos de la lista de la derecha, donde los elementos de la lista están separados por comas. El operador inlist_field es verdadero si la expresión regular de la izquierda coincide con cualquiera de los elementos del campo (columna) nombrado por la expresión de la derecha, utilizando el separador definido para el campo. NB: el operador inlist_field requiere que la expresión de la derecha evalúe un nombre de campo, mientras que el operador inlist requiere que la expresión de la derecha evalúe una cadena que contenga una lista separada por comas. Debido a esta diferencia, inlist_field es sustancialmente más rápido que inlist porque no se realizan conversiones de cadenas ni construcciones de listas. Las expresiones regulares no distinguen entre mayúsculas y minúsculas.

Los operadores de comparación numérica son ==#, !=#, <#, <=#, ># y >=#. Las expresiones de la izquierda y la derecha deben resultar en valores numéricos con dos excepciones: Tanto el valor textual None (campo no definido) como el texto vacío equivalen al valor cero.

Ejemplos:

  • program: field('series') == 'foo' returns '1' if the book’s series is foo, otherwise ''.

  • program: 'l.a' in field('series') devuelve '1' si la serie del libro coincide con la expresión regular l..a (por ejemplo, lala, El asesino de reyes, etc.), en caso contrario devuelve ''.

  • program: 'ciencia' inlist $#genre devuelve '1' si alguno de los valores recuperados de los géneros del libro coincide con la expresión regular ciencia, por ejemplo, Ciencia, Historia de la ciencia, Ciencia ficción, etc., de lo contrario ''.

  • program: '^ciencia$' inlist $#genre devuelve '1' si alguno de los géneros del libro coincide exactamente con la expresión regular ^ciencia$, p. ej., Ciencia, de lo contrario ''. Los géneros Historia de la ciencia y Ciencia ficción no coinciden.

  • program: 'asimov' inlist $authors devuelve '1' si algún autor coincide con la expresión regular asimov, por ejemplo, Asimov, Isaac` o `Isaac Asimov`, en caso contrario ``' '.

  • program: 'asimov' inlist_field 'autores' devuelve '1' si algún autor coincide con la expresión regular asimov, por ejemplo, Asimov, Isaac` o `Isaac Asimov`, en caso contrario `` ''.

  • program: 'asimov$' inlist_field 'authors' devuelve '1' si algún autor coincide con la expresión regular asimov$, p. ej., Isaac Asimov, de lo contrario ''. No coincide con Asimov, Isaac` debido al ancla ``$ en la expresión regular.

  • program: if field('series') != 'foo' then 'bar' else 'mumble' fi returns 'bar' if the book’s series is not foo. Otherwise it returns 'mumble'.

  • program: if field('series') == 'foo' || field('series') == '1632' then 'yes' else 'no' fi returns 'yes' if series is either foo or 1632, otherwise 'no'.

  • program: if '^(foo|1632)$' in field('series') then 'yes' else 'no' fi returns 'yes' if series is either foo or 1632, otherwise 'no'.

  • program: if 11 > 2 then 'sí' else 'no' fi devuelve 'no' porque el operador > realiza una comparación lexicográfica.

  • program: if 11 ># 2 then 'sí' else 'no' fi devuelve 'sí' porque el operador ># realiza una comparación numérica.

Functions in General Program Mode

See Template function reference for the list of functions built into the template language.

Notas:

  • As opposed to Single Function Mode, in General Program Mode you must specify the first parameter value.

  • All parameters are expression_lists (see the grammar above).

Programas más complejos en expresiones de plantilla: modo de programación de plantillas

El modo de programación de plantillas (MPP) es una mezcla entre el modo de programación general y el modo de función única. El MPP se diferencia del modo de función única en que permite escribir expresiones de plantilla que hacen referencia a otros campos de metadatos, usar funciones anidadas, modificar variables y hacer operaciones aritméticas. Se diferencia del modo de programación general en que la plantilla está entre caracteres { y } y no empieza por la palabra program. La parte del programa de la plantilla es una expression_list del modo de programación general.

Un ejemplo: supongamos que quiere una plantilla que muestre la serie de un libro si la tiene, y en caso contrario muestre el valor del campo personalizado #genre. Esto no se puede hacer en el modo de función única, porque no se puede hacer referencia a otro campo en la expresión de la plantilla. En el MPP sí se puede. La siguiente expresión lo demuestra:

{series_index:0>7.1f:'ifempty($, -5)'}

El ejemplo muestra varias cosas:

  • El MPP se usa si la expresión empieza por :' y termina en '}. Cualquier otra cosa se supone que corresponde al modo de función única.

    If the template contains a prefix and suffix, the expression ends with '| where the | is the delimiter for the prefix. Example:

    {series_index:0>7.1f:'ifempty($, -5)'|prefix | suffix}
    
  • Functions must be given all their arguments. For example, the standard built-in functions must be given the initial parameter value.

  • The variable $ is usable as the value argument and stands for the value of the field named in the template, series_index in this case.

  • los espacios en blanco se ignoran y se pueden utilizar en cualquier lugar dentro de la expresión.

  • los textos constantes se encierran en comillas del mismo tipo, ya sea ' o ".

En el MPP, usar los caracteres { y } en textos literales puede conducir a errores o resultados inesperados porque confunden al procesador de plantillas. Éste intenta interpretarlos como límites de expresiones de plantilla y no como caracteres. En algunos casos, pero no todos, puede sustituir un { por [[ y un } por ]]. En general, si un program contiene caracteres { y }, debería usar el modo de programación general.

Modo de plantilla de Python

Python Template Mode (PTM) lets you write templates using native Python and the calibre API. The database API will be of most use; further discussion is beyond the scope of this manual. PTM templates are faster and can do more complicated operations but you must know how to write code in Python using the calibre API.

Una plantilla MPPy empieza por:

python:
def evaluate(book, context):
    # book is a calibre metadata object
    # context is an instance of calibre.utils.formatter.PythonTemplateContext,
    # which currently contains the following attributes:
    # db: a calibre legacy database object.
    # globals: the template global variable dictionary.
    # arguments: is a list of arguments if the template is called by a GPM template, otherwise None.
    # funcs: used to call Built-in/User functions and Stored GPM/Python templates.
    # Example: context.funcs.list_re_group()

    # your Python code goes here
    return 'a string'

Puede añadir el texto anterior a la plantilla usando el menú contextual, al que normalmente se accede pulsando el botón derecho. Los comentarios no son importantes y pueden eliminarse. Debe usar la sangría de Python.

El objeto de contexto admite str(context), que devuelve un texto con el contenido del contexto y context.attributes, que devuelve una lista con los nombres de los atributos del contexto.

El atributo context.funcs le permite invocar funciones incorporadas y de usuario, así como plantillas guardadas MPG o Python, de manera que puede ejecutarlas directamente en el código. Las funciones se identifican por sus nombres. Si el nombre entra en conflicto con una palabra clave de Python, añada un guión bajo al final. Ejemplos:

context.funcs.list_re_group()
context.funcs.assert_()

He aquí un ejemplo de una plantilla MPPy que genera una lista de todos los autores de una serie. La lista se guarda en una Columna generada a partir de otras columnas, funciona como etiquetas. Se muestra en los detalles del libro y tiene la opción en líneas separadas activada (en Preferencias > Apariencia > Detalles del libro). Esta opción requiere que la lista esté separada por comas. Para satisfacer este requisito la plantilla convierte las comas en los nombres de autor a punto y coma y luego construye una lista de autores separados por comas. Los autores se ordenan, razón por la cual la plantilla usa author_sort.

python:
def evaluate(book, context):
    if book.series is None:
        return ''
    db = context.db.new_api
    ans = set()
    # Get the list of books in the series
    ids = db.search(f'series:"={book.series}"', '')
    if ids:
        # Get all the author_sort values for the books in the series
        author_sorts = (v for v in db.all_field_for('author_sort', ids).values())
        # Add the names to the result set, removing duplicates
        for aus in author_sorts:
            ans.update(v.strip() for v in aus.split('&'))
    # Make a sorted comma-separated string from the result set
    return ', '.join(v.replace(',', ';') for v in sorted(ans))

La salida en los detalles del libro tiene este aspecto:

Diálogo de conversión de libros electrónicos

Plantillas guardadas

El modo de programación general y el modo de plantilla de Python permiten guardar plantillas e invocarlas desde otra plantilla, casi como invocar funciones guardadas. Puede guardar plantillas en Preferencias > Avanzado > Funciones de plantilla. Hay más información en el diálogo correspondiente. Puede invocar una plantilla de la misma manera que una función, pasando argumentos por posición si lo desea. Un argumento puede ser una expresión. Ejemplos para invocar una plantilla, suponiendo que la plantilla guardada se llame foo:

  • lala() – invoca la plantilla sin pasar argumentos.

  • lala(a, b) – invoca la plantilla pasando los valores de las variables a y b.

  • lala(if field('series') then field('series_index') else 0 fi) – si el libro tiene series, pasa el valor de series_index, en caso contrario pasa el valor 0.

En el MPG, puede obtener el valor de los argumentos pasados en la invocación de la plantilla guardada usando la función arguments. Esta función declara e inicializa las variables locales, parámetros en la práctica. Las variables son posicionales: obtienen el valor del argumento usado en la misma posición en la invocación. Si el argumento correspondiente no aparece en la invocación, arguments asigna a esa variable el valor predeterminado suministrado. Si no hay valor predeterminado, será un texto vacío. Por ejemplo, la siguiente función arguments declara dos variables: key y ``alternate`:

arguments(key, alternate='series')

Ejemplos, suponiendo de nuevo que la plantilla guardada se llame lala:

  • lala('#miserie') – a la variable key se le asigna el valor '#miserie' y a la variable alternate se le asigna el valor predeterminado 'serie'.

  • lala('series', '#genero') a la variable key se le asigna el valor 'series' y a la variable alternate se le asigna el valor '#genero'.

  • lala() – a la variable key se le asigna un texto vacío y a la variable alternate se le asigna el valor predeterminado 'series'.

En el MPPy los argumentos se pasan en el parámetro arguments, que es una lista de textos. No existe la posibilidad de especificar valores predeterminados. Debe comprobar la longitud de la lista arguments para asegurarse de que el número de argumentos es el esperado.

Una buena manera de probar las plantillas guardadas es usar el diálogo Prueba de plantillas. Para facilitar su acceso, asígnele un atajo de teclado en Preferencias > Avanzado > Atajos > Prueba de plantillas. Si le asigna un atajo de teclado podrá alternar rápidamente entre la prueba de plantillas y la modificación del código de programa de la plantilla.

Pasar información adicional a las plantillas

Un desarrollador puede decidir pasar información adicional al procesador de plantillas, como metadatos del libro específicos para una aplicación o información sobre lo que se quiere que haga el procesador. Una plantilla puede acceder a esta información y usarla durante la evaluación.

Desarrollador: cómo pasar información adicional

La información adicional es un diccionario Python que contiene pares nombre_de_variable: valor de variable donde los valores deberían ser textos. La plantilla puede acceder al diccionario, creando variables locales con nombre nombre_de_variable y valor valor de variable. El usuario no puede cambiar el nombre, así que lo mejor es usar nombres que no entren en conflicto con otras variables locales, por ejemplo añadiendo un guión bajo al principio del nombre.

El diccionario se pasa al procesador de plantillas (el formatter) usando el argumento global_vars=diccionario. La forma completa del método es:

def safe_format(self, fmt, kwargs, error_value, book,
                column_name=None, template_cache=None,
                strip_results=True, template_functions=None,
                global_vars={})

Creador de plantillas: cómo acceder a la información adicional

Puede acceder a la información adicional (el diccionario globals) en una plantilla usando la función de plantilla:

globals(id[=expression] [, id[=expression]]*)

donde id es cualquier nombre de variable permitido. Esta función comprueba si la información adicional prevista por el desarrollador contiene el nombre. Si es así, la función asigna el valor a una variable local con ese nombre. Si el nombre no está presente en la información adicional y se suministra una expresión, se evalua dicha expresión y el resultado se asigna a la variable local. Si no existe ni un valor ni una expresión, la función asigna el texto vacío ('') a la variable local.

Una plantilla puede establecer un valor en el diccionario globals usando la función de plantilla:

set_globals(id[=expression] [, id[=expression]]*)

Esta función asigna la pareja id:valor del diccionario globals, donde valor es el valor de la variable local de la plantilla id. Si la variable no existe, valor se toma como el resultado de evaluar expresión.

Notas sobre las diferencias entre modos

Los tres modos de programación, el modo de función única (MFU), el modo de programación de plantillas (MPP) y el modo de programación general (MPG) funcionan de distinta manera. MFU pretende ser «sencillo» y oculta muchos elementos del lenguaje de programación.

Diferencias:

  • En el MFU el valor de la columna se pasa siempre como un primer argumento «invisible» a la función que se incluye en la plantilla.

  • El MFU no hace distinciones entre variables y textos; todos los valores son textos.

  • La siguiente plantilla MFU devuelve el nombre de la serie o el texto 'no series':

    {series:ifempty(no series)}
    

    La plantilla equivalente en MPP es:

    {series:'ifempty($, 'no series')'}
    

    La plantilla equivalente en MPG es:

    program: ifempty(field('series'), 'no series')
    

    El primer argumento de ifempty es el valor del campo series. El segundo argumento es el texto no series. En MFU el primer argumento, el valor, se pasa automáticamente (es el argumento invisible).

  • Varias funciones de plantilla, como booksize() y current_library_name(), no admiten argumentos. Debido al «argumento invisible», no puede usar estas funciones en MFU.

  • Las funciones anidadas, en las que una función invoca a otra función para evaluar un argumento, no pueden usarse en MFU. Por ejemplo, esta plantilla, que pretende devolver los 5 primeros caracteres del valor de la serie convertidos en mayúsculas, no funcionará en MFU:

    {series:uppercase(substr(0,5))}
    
  • MPP y MPG permiten usar funciones anidadas. La plantilla anterior en MPP sería:

    {series:'uppercase(substr($, 0,5))'}
    

    En MPG sería:

    program: uppercase(substr(field('series'), 0,5))
    
  • Como se mencionó ateriormente en la sección modo de programación de plantillas, en el MPP, usar los caracteres { y } en textos literales puede conducir a errores o resultados inesperados porque confunden al procesador de plantillas. Éste intenta interpretarlos como límites de plantilla y no como caracteres. En algunos casos, pero no todos, puede sustituir un { por [[ y un } por ]]. En general, si un program contiene caracteres { y }, debería usar el modo de programación general.

Funciones de plantilla Python definidas por el usuario

You can add your own Python functions to the template processor. Such functions can be used in any of the three template programming modes. The functions are added by going to Preferences  →  Advanced  →  Template functions. Instructions are shown in that dialog. Note that you can use Python Templates for a similar purpose. As calling user-defined functions is faster than calling a Python template, user-defined functions might be more efficient depending on the complexity of what the function or template does.

Notas especiales para plantillas de guardado o envío

Cuando una plantilla se usa como plantilla de «guardado a disco» o de «envío a dispositivo», ocurre un procesado especial. Los valores de los campos se sanean, eliminando caracteres especiales para los sistemas operativos por guiones bajos, incluyendo barras. Esto significa que el texto de los campos no puede usarse para crear carpetas. Sin embargo, las barras no se modifican en los textos de prefijo o sufijo, por lo que las barras en estos textos harán que se creen carpetas. Gracias a esto, es posible crear estructuras de carpetas de profundidad variable.

Por ejemplo, supongamos que quiere una estructura de carpetas serie/índice de serie - título, con la salvedad de que si la serie no existe el título debe estar en la carpeta superior. La plantilla para conseguir esto es:

{series:||/}{series_index:|| - }{title}

La barra y el guión sólo aparecen si la serie no está vacía.

La función lookup() nos permite realizar un procesado aún más complejo. Por ejemplo, supongamos que si un libro tiene una serie, entonces queremos una estructura de carpetas serie/índice de serie - título. Si el libro no tiene una serie, entonces queremos la estructura género/orden de autor/título. Si el libro no tiene género, queremos que use «Desconocido». Queremos seguir dos caminos completamente distintos según el valor de la serie.

Para lograr esto:

  1. Creamos un campo compuesto (démosle el nombre de consulta #aa) que contiene {series}/{series_index} - {title}. Si la serie no está vacía, esta plantilla produce serie/número_de_serie - título.

  2. Creamos un campo compuesto (démosle el nombre de consulta #bb) que contenga {#genre:ifempty(Desconocido)}/{author_sort}/{title}. Esta plantilla produce género/orden de autor/título, donde un género vacío se sustituye por Desconocido.

  3. Establecemos la plantilla de guardado en {series:lookup(.,#aa,#bb)}. Esta plantilla elige el campo compuesto #aa si la serie no está vacía y el campo compuesto #bb si la serie está vacía. Obtenemos por lo tanto dos rutas de guardado completamente diferentes según el campo series esté o no vacío.

Consejos

  • Use la prueba de plantillas para probar plantillas. Añada esta función al menú contextual para libros en lea biblioteca o asígnele un atajo de teclado.

  • Las plantillas pueden usar otras plantillas haciendo referencia a columnas compuestas construidas con la plantilla deseada. También puede usar plantillas guardadas.

  • En un panel de conexiones, puede establecer un campo como vacío (o lo que sea equivalente a vacío) utilizando la plantilla especial {}. Esta plantilla siempre producirá un texto vacío.

  • La técnica descrita anteriormente para mostrar los números incluso si son cero funciona con el campo estándar series_index.

Template function reference