Мова шаблонів calibre

Мова шаблонів calibre є специфічною для calibre мовою програмування, яка використовується у різних компонентах calibre. Її використовують для визначення шляхів до файлів, значення форматування та обчислення значень визначених користувачем стовпчиків. Приклади:

  • Визначення структури каталогів та назви файла при зберіганні файлів з бібліотеки calibre на диск або на пристрій для читання книг.

  • Визначення правил додавання піктограм і кольорів до списку книг calibre.

  • Визначення «віртуальних» стовпчиків, які містять дані з інших стовпчиків.

  • Розширені можливості пошуку у бібліотеці.

  • Розширені можливості з пошуку і заміни у метаданих.

Мову побудовано навколо поняття «шаблон». Шаблон задає, які метадані книги слід використати, обчислення над цими метаданими, і як їх слід форматувати.

Базові шаблони

Базовий шаблон містить один або декілька виразів шаблонів. Вираз шаблона складається з тексту і назв у фігурних дужках ({}), які згодом буде замінено на відповідні метадані із оброблюваної книги. Наприклад, типовим шаблоном, який використано для збереження книг на пристроях у calibre, містить 4 шаблонних вирази:

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

Для книги «The Foundation» автора «Isaac Asimov» ми отримаємо:

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

Символи похилих рисок не є шаблонними виразами, оскільки їх обмежено {}. Такі фрагменти тексту після обробки лишаються на місці. Наприклад, якщо шаблоном є такий:

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

для «The Foundation» шаблон дасть:

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

Шаблон може отримувати доступ до усіх метаданих, які доступні у calibre, зокрема нетипових стовпчиків (стовпчиків, які створено користувачем), за їхньою назвою для пошуку. Щоб визначити назву для пошуку для стовпчика (який іноді називають «полем»), наведіть вказівник миші на заголовок стовпчика у списку книг calibre. У назвах для пошуку нетипових стовпчиків першим символом завжди є #. Для стовпчиків типу циклів передбачено додаткове поле із назвою '#назва_для_пошуку'_index, яке містить індекс у циклі. Отже, якщо у вас є нетипове поле циклу із назвою #myseries, буде також і поле із назвою #myseries_index. Індекс стандартного стовпчика циклу має назву series_index.

Окрім полів, заснованих на вмісті стовпчиків, ви також можете використовувати:

  • {formats} — список форматів, доступних у бібліотеці calibre для книги

  • {identifiers:select(isbn)} — ISBN книги

Якщо метаданих поля певної книги не визначено, поле у шаблоні буде замінено на порожній рядок (''). Приклад:

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

Якщо книга Азімова «Second Foundation» є частиною циклу «Foundation», шаблон дасть:

Asimov, Isaac/Foundation/Second Foundation 3

Якщо для книги не введено циклу, шаблон дасть:

Asimov, Isaac/Second Foundation

Обробник шаблонів автоматично вилучає послідовності похилих рисок і початкові та завершальні пробіли.

Додаткові можливості з форматування

За допомогою шаблонів, окрім вставляння метаданих, можна виконувати набагато ширший спектр дій. Шаблони можуть включати додатковий текст, якщо виконуються певні умови, і керувати тим, як буде форматовано підставлені дані.

Умовне включення тексту

Іноді може виникнути потреба у виведенні тексту, лише якщо певне поле є непорожнім Типовим прикладом є series і series_index. У цьому прикладі нам потрібен буде або порожній рядок або два значення, які відокремлено дефісом. У calibre для обробки подібних випадків передбачено спеціальний синтаксис запису шаблона.

Наприклад, використовуючи наведений вище приклад із «Foundation», припустімо, вам потрібно, щоб застосування шаблона давало таке: Foundation - 3 - Second Foundation. Цей шаблон дає такий результат:

{series} - {series_index} - {title}

Втім, якщо книга не належить до циклу, шаблон виведе - - заголовок Ймовірно, це зовсім не те, що потрібно. Здебільшого, бажаним результатом буде просто заголовок, без зайвих дефісів. Для отримання такого результату слід скористатися такою синтаксичною конструкцією шаблонів:

{field:|текст_префікса|текст_суфікса}

Цей шаблонний вираз визначає, що якщо field має значення XXXX, результатом буде prefix_textXXXXXsuffix_text. Якщо field є порожнім (не має значення), результатом буде порожній рядок (нічого), оскільки префікс і суфікс буде проігноровано. Префікс і суфікс можуть містити пробіли.

Не використовуйте підлеглі шаблони (`{ … }`) або функції (див. нижче) у префіксі або суфіксі.

За допомогою цієї синтаксичної конструкції ми можемо розв’язати поставлену вище задачу з шаблоном:

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

Дефіси буде включено, лише якщо для книги визначено індекс у циклі, а таке трапляється, лише якщо метадані містять запис циклу. Продовжуючи приклад із «Foundation», шаблон дасть Foundation - 1 - Second Foundation.

Нотатки:

  • Вам слід включити двокрапку після назви для пошуку, якщо ви хочете скористатися префіксом або суфіксом.

  • Вам слід або не використовувати символи |, або використовувати обидва символи; користуватися одним з них, як у {field:| - }, не можна.

  • Можна не вказувати текст для префікса або суфікса, якось так: {series:|| - }. Шаблон {title:||} є еквівалентним до шаблона {title}.

Форматування

Припустімо, вам потрібно переконатися, що series_index завжди записуватиметься у форматі з трьох цифр із початковими нулями. Ось як цього досягти:

{series_index:0>3s} — три цифри із початковими нулями

Для кінцевих нулів скористайтеся такою конструкцією:

{series_index:0<3s} — три цифрви із завершальними нулями

Якщо ви використовуєте індекси у циклі із проміжними значеннями (наприклад, 11), вам може знадобитися вирівнювання десяткової крапки. Наприклад, бажаним буде запис індексів 1 і 2.5 як 01.00 і 02.50 для належного упорядковування на пристроях із лексичним упорядкуванням. Щоб досягти потрібного результату, скористайтеся таким шаблоном:

{series_index:0>5.2f} — п’ять символів складаються із двох цифр і початковими нулями, десяткової крапки і 2 цифр після десяткової крапки.

Якщо вам потрібні лише перші дві літери даних, скористайтеся такою конструкцією:

{author_sort:.2} — лише перші дві літери імені у впорядкованому списку авторів

Більша частина форматування мови шаблонів calibre є похідною від Python. Щоб дізнатися більше про синтаксичні конструкції таких дій з форматування, ознайомтеся із документацією з Python.

Використання шаблонів для визначення нетипових стовпчиків

Шаблони використовують для показу даних, яких немає у метаданих calibre або показу даних у форматуванні, яке відрізняється від стандартного форматування calibre. Наприклад, у вас може виникнути потреба бачити ISBN, поле, вміст якого calibre не показує. Для показу таких даних ви можете скористатися нетиповими стовпчиками. Створіть стовпчик із типом «Стовпчик, створений на основі інших стовпчиків» (надалі ми такі стовпчики називатимемо складеними) і введіть до нього шаблон. Результат: calibre показуватиме у стовпчику результат обчислення шаблона. Для показу ISBN створіть стовпчик і введіть {identifiers:select(isbn)} у поле шаблона. Щоб створити стовпчик, який міститиме значення двох нетипових стовпчиків циклів, скористайтеся шаблоном {#series1:||,}{#series2}.

Складені стовпчики можуть використовувати будь-які варіанти шаблонів, включно з форматуванням.

Зауваження: редагувати дані, які показано у складеному стовпчику не можна. Редагувати слід дані у стовпчиках-джерелах. Якщо ви спробуєте виконати редагування складеного стовпчика, наприклад, подвійним клацанням на даних у ньому, calibre відкриє для редагування шаблон, а не дані, які він виводить.

Шаблони і набори метаданих

Набори метаданих використовуються для внесення змін до метаданих, що записуються до книг під час дій із надсилання на пристрій і збереження на диск. Набір метаданих надає вам змогу вказати шаблон для надання даних, які слід записати до метаданих книги. Ви можете скористатися наборами метаданих для внесення змін до таких полів: authors, author_sort, language, publisher, tags, title, title_sort. Набори метаданих допомагають у використанні спеціалізованих метаданих для книг на пристрої з метою усування проблем із упорядковуванням книг або їхнім показом.

Під час створення набору метаданих ви визначаєте формат і пристрій, для яких використовуватиметься набір. Передбачено особливий пристрій, save_to_disk, який використовується для збереження книг у певних форматах (на відміну від надсилання книг на пристрій). Після вибору формату і пристрою вам слід вибрати поля метаданих для внесення змін і надати шаблони для визначення нових значень цих полів. Ці шаблони буде з’єднано з відповідними полями призначення. Ви, звичайно ж, можете використовувати у шаблонах наборів метаданих складені стовпчики.

Набори метаданих є доволі гнучкими — їх можна писати у режимі однієї функції, режимі програмування шаблонів, загальному режимі програмування та режимі шаблонів Python.

Якщо до метаданих може бути застосовано набір (на сервері даних, для збереження на диск або надсилання на пристрій), calibre шукає визначені набори метаданих і вибирає належний для заданого формату і пристрою. Наприклад, для того, щоб знайти належний набір метаданих для книги EPUB, яку надсилають на пристрій під керуванням ANDROID, calibre шукає набори метаданих у такому порядку:

  • набір метаданих із точною відповідністю за форматом і пристроєм, наприклад EPUB і ANDROID

  • набір метаданих із точним відповідником за форматом і спеціальним варіантом any device, наприклад EPUB і any device

  • набір метаданих із спеціальним варіантом any format і точною відповідністю за пристроєм, наприклад any format і ANDROID

  • набір метаданих any format і any device

Поля міток (tags) і авторів (authors) обробляються у особливий спосіб, оскільки у обох цих полях може міститися декілька записів. У книги може бути багато міток і багато авторів. Якщо ви визначаєте одне з цих двох полів як таке, вміст якого слід змінити, результати використання шаблона буде перевірено на наявність декількох записів. Для міток результат буде розділено на записи за знайденими calibre комами. Наприклад, якщо шаблон створює значення Трилер, Жахи, результатом буде дві мітки, Трилер і Жахи. Способів додавання коми у окрему мітку не передбачено.

Те саме стосується авторів, але для поділу на записи використовується інший символ, & (амперсанд), а не кома. Наприклад, якщо шаблоном створюється значення Blogs, Joe&Posts, Susan, для книги буде визначено двоє авторів, Blogs, Joe і Posts, Susan. Якщо ж шаблон створює значення Blogs, Joe;Posts, Susan, книга матиме один запис автора із доволі чудернацьким іменем.

Набори метаданих впливають на метадані, які записуються до книги, коли її зберігають на диск або записують на пристрій для читання. Набори метаданих не змінюють метадані, які використовуються діями зберегти на диск та надіслати на пристрій для створення назв файлів. Назви файлів будуються за допомогою шаблонів, які вказано у відповідних місцях вікна налаштувань.

Використання функцій у шаблонах — режим єдиної функції

Припустімо, вам потрібно показати значення поля літерами верхнього регістру, хоча зазвичай вміст цього поля записується регістром символів як у заголовку. Досягти цього можна за допомогою функцій шаблонів. Наприклад, для переведення літер у верхній регістр можна скористатися функцією uppercase, ось так: {title:uppercase()}. Для показу назви у форматі, типовому для заголовків, скористайтеся шаблоном {title:titlecase()}.

Функції вставляють у частину форматування шаблона Їх вписують після : і перед першим | або завершальним }, якщо не використано префікса або суфікса. Якщо вам потрібне одразу форматування і функції, функцію включають після ще одного :. Функції повертають значення стовпчика, вказаного у шаблоні, належним чином зміненим.

Синтаксична конструкція для використання функцій є однією з таких:

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

За назвами функцій завжди мають іти початкова і завершальна круглі дужки. Деяким функціям обов’язково слід передавати додаткові значення (аргументи), і ці аргументи слід вказувати у дужках. Аргументи відокремлюють комами. Самі коми (коми як текст, а не роздільник аргументів) слід екранувати символом зворотної похилої риски (\) . Останній (або єдиний) аргумент не може містити текстової завершальної дужки.

Функції обчислюють до специфікацій форматів та префіксів/суфіксів. Нижче наведено приклад одночасного використання форматування та функції.

Важливе зауваження: якщо ви маєте досвід програмування, будь ласка, зауважте, що синтаксис у цьому режимі (режимі єдиної функції) не зовсім такий, як ви очікуєте Для рядків не потрібні лапки. Пробіли мають значення. Усі аргументи мають бути сталим; проміжних обчислень не передбачено.

Не використовуйте підлеглих шаблонів (`{ … }`) у аргументах функцій. Замість них, використовуйте режим шаблонного програмування та загальний режим програмування.

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.

Використання функцій і форматування у одному шаблоні

Припустімо, у нас є нетиповий стовпчик із назвою #myint, у якому містяться цілі числа. Вам потрібно представити їх у форматі із початковими нулями, ось так: 003. Для цього можна було б скористатися форматуванням 0>3s. Втім, типово, якщо число (ціле або дійсне) дорівнює нулеві, значення буде показано як порожній рядок. Отже, нульові значення початкові значення призводитимуть до порожніх рядків, а не до записів 000. Якщо вам потрібні саме значення 000, вам слід скористатися одразу і рядком форматування, і функцією ifempty для заміни порожнього значення на нульове. Шаблон може бути таким:

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

Зауважте, що ви можете також використовувати префікс і суфікс. Якщо ви хочете, щоб число було показано як [003] або [000], скористайтеся таким шаблоном:

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

Загальний режим програмування

Загальний режим програмування замінює `вирази шаблонів програмою, яку написано мовою шаблонів. Синтаксис мови визначено такою граматикою:

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

Нотатки:

  • top_expression завжди має значення. Значення expression_list є значенням останнього top_expression у списку. Наприклад, значення списку виразів 1;2;'foobar';3 дорівнює 3.

  • У логічному контексті будь-яке непорожнє значення дорівнює True

  • У логічному контексті порожнє значення дорівнює False

  • Рядки і числа можна використовувати на рівних правах. Наприклад, 10 і '10' є тим самим значенням.

  • Коментарями вважаються усі рядки, на початку яких є символ «#». Підтримки коментарів, які починаються не з першого символу рядка, не передбачено.

Пріоритетність операторів

Пріоритетність операторів (порядок обчислення), від найвищої (обчислюються першими) до найнижчої (обчислюються останніми):

  • Виклики функцій, сталі, вирази у дужках, вирази інструкцій, вирази надання значення, посилання на поля.

  • Унарні плюс (+) і мінус (-). Обчислення для цих операторів виконується справа ліворуч.

    Ці і усі інші арифметичні операції повертають цілі числа, якщо обчислення виразу призводить до числа із дробовою частиною, яка рівна нулеві. Приклад: якщо результатом обчислення виразу є 3.0, його буде замінено на 3.

  • Множення (*) і ділення (/). Ці дії є асоціативними і обчислюються зліва праворуч. Скористайтеся дужками, якщо ви хочете змінити порядок обчислення.

  • Додавання (+) і віднімання (-). Ці дії є асоціативними, обчислення виконується зліва праворуч.

  • Порівняння чисел і рядків. Ці оператори повертають '1', якщо порівняння справджується, інакше повертають порожній рядок (''). Порівняння не є асоціативними: a < b < c — синтаксична помилка.

  • Поєднання рядків (&). Оператор & повертає рядок, який сформовано об’єднанням виразів ліворуч і праворуч від нього. Приклад: 'aaa' & 'bbb' повертає 'aaabbb'. Оператор є асоціативним і обчислюється зліва праворуч.

  • Унарне логічне заперечення (!). Цей оператор повертає '1', якщо вираз не є істинним (є порожнім рядком), інакше повертає ''.

  • Логічне «І» (&&). Цей оператор повертає „1“, якщо вирази ліворуч і праворуч від нього є істинними, або порожній рядок '', якщо хоча б одне з тверджень ліворуч і праворуч є хибним. Оператор є асоціативним, обчислюється зліва праворуч, і має властивість скороченого обчислення.

  • Логічне «АБО» (||) Цей оператор повертає '1', якщо твердження ліворуч або праворуч від нього є істинним, або '', якщо обидва твердження є хибними. Є асоціативним, обчислюється зліва праворуч і має властивість скороченого обчислення. Оператор є інклюзивним «АБО», тобто повертає '1', якщо вирази праворуч і ліворуч від нього є істинними.

Посилання на поля

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')

Вирази if

У виразах if спочатку обчислюється умова. Якщо умова справджується (обчислення дають непорожнє значення), обчислюється expression_list у інструкції then``. Якщо умова не справджується, обчислюється, якщо є, expression_list у інструкції elif або else, якщо існують такі інструкції. Частини elif і else є необов’язковими. Слова if, then, elif, else і fi є зарезервованими, ви не можете використовувати їх як назви ідентифікаторів. Ви можете вставляти розриви рядків та пробіли там, де захочете. Запис condition є top_expression, а не expression_list; не можна використовувати крапку з комою. Набори expression_lists є розділеними крапками з комами послідовності top_expression. Вираз if повертає результат останнього top_expression у обчисленому expression_list або порожній рядок, якщо жодного списку виразів не було обчислено.

Приклади:

* 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)

Приклад вкладеного``if``:

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

Як ми вже згадували вище, if дає значення. Це означає, що усі вказані нижче інструкції є еквівалентними:

* 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)

Вирази for

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.

Приклад: цей шаблон вилучає першу ієрархічну назву для кожного значення у полі «Жанр» (#genre), будуючи список із новими назвами:

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

Якщо початковим значенням поля «Жанр» було History.Military, Science Fiction.Alternate History, ReadMe, шаблон поверне Military, Alternate History, ReadMe. Ви можете скористатися цим шаблоном в інструменті calibre Пакетне редагування метаданих  →  Пошук із заміною ^ у полі Шукати слід вказати шаблон для вилучення першого рівня ієрархії і визначення значення-результату для поля «Жанр».

Зауваження: останній рядок у шаблоні, new_tags, у цьому випадку не є строго обов’язковим, оскільки for повертає значення останнього top_expression у списку виразів. Значенням виразу надання значення є значення виразу, тому значенням інструкції for є те, що записано до new_tags.

Визначення функції

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.

Аргументи є позиційними. Під час виклику функції надані аргументи заміщують собою зліва праворуч визначені параметри, значення аргументу стає значенням параметра. Надання більшої за кількість визначених параметрів кількості аргументів вважається помилкою. Параметри можуть мати типові значення, наприклад, a = 25. Якщо для параметра не надано аргументу, буде використано типове значення. Якщо такого типового значення немає, буде використано порожній рядок.

У локальній функції можна скористатися інструкцією return.

Перш ніж функцією можна буде скористатися, її слід визначити.

Приклад: цей шаблон обчислює приблизну тривалість періоду часу у роках, місяцях та днях на основі кількості днів. Для форматування обчислених значень використано функцію to_plural(). Зауважте, що у прикладі також використано оператор &:

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')

Оператори відношень

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

Реалізовано дві форми операторів відношень: порівняння рядків і порівняння чисел.

Порівняння рядків є порівнянням рядків без врахування регістру символів із використанням лексичного порядку. Підтримуваними операторами порівняння рядків є ==, !=, <, <=, >, >=, in, inlist та inlist_field. Для оператора in результат обчислення лівого операнда вважається формальним виразом взірця. Результатом обчислення in є True, якщо взірець у лівого операнда відповідає значенню правого операнда. Відповідність встановлюється без врахування регістру символів.

Оператор inlist повертає true, якщо формальний вираз ліворуч від нього відповідає одному із записів у списку праворуч від нього, де записи у списку відокремлено комами. Оператор field_inlist повертає true, якщо формальний вираз ліворуч від нього відповідає будь-якому зі записів у полі (стовпчику), назву якого вказано праворуч від оператора, з використанням роздільника, який визначено для поля. Зауваження: для використання оператора inlist_field вираз праворуч від нього має визначати назву поля, а для використання оператора inlist результ татом обчислення виразу праворуч від нього має бути рядок, що містить відокремлений комами список. Через цю різницю inlist_field є суттєво швидшим за inlist, оскільки не виконується ніяких перетворень рядків або побудови списків. Обробка формальних виразів виконується без врахування регістру символів.

Операторами числового порівняння є ==#, !=#, <#, <=#, >#, >=#. Результатом обчислення лівого і правого операндів мають бути числові значення з двома виключеннями: рядкове значення «None» (невизначене поле) і порожнє значення вважаються рівними нулеві.

Приклади:

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

  • program: 'f.o' in field('series') повертає '1', якщо цикл книги відповідає формальному виразу f.o (наприклад, foo, Off Onyx тощо), інакше повертає ''.

  • program: 'science' inlist $#genre повертає '1', якщо будь-яке зі значень, отриманих із запису жанрів книги, відповідає формальному виразу science, наприклад, Science, History of Science, Science Fiction тощо. У інших випадках буде повернуто ''.

  • program: '^science$' inlist $#genre повертає '1', якщо будь-який із записів жанрів книги точно відповідає формальному виразу ^science$, наприклад, Science. Якщо відповідність не буде встановлено, програма поверне ''. Жанри History of Science і Science Fiction вважаються невідповідними.

  • program: 'asimov' inlist $authors повертає '1', якщо будь-який із записів авторів відповідає формальному виразу asimov, наприклад, Asimov, Isaac або Isaac Asimov, якщо таких записів немає, повертає ''.

  • program: 'asimov' inlist_field 'authors' повертає '1', якщо будь-який із записів авторів відповідає формальному виразу asimov, наприклад, Asimov, Isaac або Isaac Asimov, якщо таких записів немає, повертає ''.

  • program: 'asimov$' inlist_field 'authors' повертає '1', якщо будь-який із записів авторів відповідає формальному виразу asimov$, наприклад, Isaac Asimov, якщо це не так, повертає ''. Не відповідає запису Asimov, Isaac через прив’язку $ у формальному виразі.

  • 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 'yes' else 'no' fi повертає 'no', оскільки оператор > виконує лексичне порівняння.

  • program: if 11 > 2 then 'yes' else 'no' fi повертає 'yes', оскільки оператор ># виконує числове порівняння.

Functions in General Program Mode

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

Нотатки:

  • 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).

Складніші програми у виразах шаблонів — режим програмування шаблонів.

Режим програмування шаблонів є сумішшю загального режиму програмування і режиму єдиної функції. Режим програмування шаблонів відрізняється від режиму єдиної функції тим, що у ньому можна писати вирази шаблонів, які посилаються на інші поля метаданих, використовувати вкладені функції, змінювати значення змінних і виконувати арифметичні дії. Він відрізняється від загального режиму програмування тим, що шаблон міститься між символами { і } і не починається зі слова program:. Програмна частина шаблона є списком виразів загального режиму програмування.

Почнімо з прикладу. Припустімо, що ви хочете, щоб ваш шаблон показував цикл для книги, яка належить до циклу, і значення нетипового поля #genre, якщо книга не належить до циклів. Зробити це за допомогою режиму єдиної функції неможливо, оскільки ви не можете послатися на інше поле метаданих у виразі шаблона. Але у програмному режимі ви можете це зробити. Спрацює такий вираз:

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

Приклад демонструє декілька речей:

  • Режим програмування шаблонів буде використано, якщо вираз починається з :' і завершується '}. Усі інші варіанти вважатимуться використанням режиму єдиної функції.

    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.

  • пробіли буде проігноровано; їх можна використовувати будь-де у виразі.

  • сталі рядки слід брати у відповідні лапки ' або ".

У режимі програмування шаблонів використання символів { і } у рядкових літералах режиму програмування шаблонів може призвести до помилок або неочікуваних результатів, оскільки ці символи заважають нормальній роботі обробника шаблонів. Він намагається обробити їх як межі виразу шаблона, а не символи. У деяких, але не в усіх випадках, ви можете замінити { на [[ і } на ]]. Загалом кажучи, якщо у вашій програмі є символи { і }, вам слід користуватися загальним режимом програмування.

Режим шаблонів 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.

Шаблон PTM розпочинається так:

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'

Ви можете додати наведений вище текст до вашого шаблона за допомогою контекстного меню, доступ до якого можна отримати за допомогою клацання правою кнопкою миші. Коментарі є необов’язковими — їх можна вилучити. Вам слід використовувати відступи, які використовують у Python.

Для об’єкта контексту передбачено підтримку str(контекст), яка повертає рядок вмісту контексту, та context.attributes, який повертає список назв атрибутів у контексті.

За допомогою атрибуту context.funcs можна викликати вбудовані функції та функції шаблонів користувача, а також збережені шаблони GPM/Python так, щоб їх можна було виконати безпосередньо у вашому коді. Функції буде отримано за назвами. Якщо виникне конфлікт назв із ключовим словом Python, додайте символ підкреслювання наприкінці назви. Приклади:

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

Нижче наведено приклад шаблона PTM, який створює список усіх авторів для циклу книг. Список буде збережено до «стовпчика побудованого на основі інших стовпчиків, який поводитиме себе як мітки». Його буде показано у Подробицях щодо книги, для нього буде використано позначений стан пункту в окремих рядках (на сторінці Налаштування → Вигляд та поведінка → Подробиці щодо книги). Використання цього пункту потребуватиме відокремлення записів у списку комами. Щоб виконати цю вимогу, шаблон перетворить коми у іменах авторів на крапки з комами, а потім побудує список відокремлених комами записів авторів. Записи авторів буде упорядковано, тому у шаблоні використано 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))

Виведені дані на панелі Подробиці щодо книги виглядатимуть так:

Вікно перетворення електронних книг

Збережені шаблони

У загальному режимі програмування і режимі шаблонів Python<python_mode> передбачено можливість збереження шаблонів загального режиму програмування та виклику цих шаблонів з інших шаблонів, подібно до виклику збережених функцій. Зберегти шаблони можна за допомогою пункту :guilabel:`Налаштування->Додатково->Шаблонні функції. у відповідному діалоговому вікні буде наведено довідкову інформації. Викликати шаблон можна у той самий спосіб, що і функцію, передавши йому, якщо потрібно, позиційні аргументи. Аргументом може бути будь-який вираз. Приклади виклику шаблону, де ми припускаємо, що збережений шаблон має назву foo:

  • foo() — виклик шаблону без передавання аргументів.

  • foo(a, b) — виклик шаблону з передаванням значень двох змінних — a і b.

  • foo(if field('series') then field('series_index') else 0 fi) — якщо книга належить до циклу, передати номер книги у циклі, інакше — передати значення 0.

Отримання аргументів відбувається за допомогою виклику збереженого шаблона за допомогою функції arguments. Ця функція одразу оголошує і ініціалізує локальні змінні, які виконують роль параметрів. Ці змінні є позиційними — їм надається значення, яке стоїть у виклику на відповідній позиції. Якщо відповідне значення змінної не було надано у виклику, arguments надає параметру типового значення. Якщо типове значення не вказано, змінній буде надано значення порожнього рядка. Наприклад, наведена нижче функція arguments оголошує 2 змінні — key, alternate:

arguments(key, alternate='series')

У наведених нижче прикладах знову припускаємо, що збережений шаблон має назву foo:

  • foo('#myseries') — аргумент key матиме значення 'myseries', а аргумент alternate матиме типове значення 'series'.

  • foo('series', '#genre') — змінній key буде надано значення 'series', а змінній alternate — значення '#genre'.

  • foo() -— змінній key буде надано значення порожнього рядка, а змінній alternate — значення 'series'.

У PTM аргументи буде передано у формі параметра arguments, який є списком рядків. Не існує способу вказати типові значення. Вам слід перевірити довжину списку arguments, щоб переконатися, що кількість аргументів є тією, на яку ви сподіваєтеся.

Простим способом тестування збережених шаблонів є використання вікна Засіб тестування шаблонів. Для полегшення доступу надайте цьому вікну клавіатурного скорочення за допомогою пункту Налаштування → Додатково → Клавіатурні скорочення → Засіб тестування шаблонів. Надання клавіатурного скорочення вікну Збережені шаблони допоможе вам швидше перемикатися між тестуванням та редагування початкового коду збереженого шаблону.

Надання додаткової інформації шаблонам

Розробник може передати додаткові відомості обробнику шаблонів, зокрема, специфічні для книги метадані або відомості про те, що слід виконати обробнику. Шаблон може отримувати доступ до цих відомостей і використовувати їх під час обробки.

Розробник: як передати додаткові відомості

Додатковими відомостями є словник Python, який містить пари назва_змінної: значення_змінної, де значеннями мають бути рядки. Шаблон може отримувати доступ до словника, створюючи локальні змінні із назвами назва_змінної, які міститимуть значення значення_змінної. Користувач не може змінювати назву, тому краще користуватися назвами, які не збігатимуться із локальними змінними інших шаблонів. Можна, наприклад, додати до усіх назв початковий символ підкреслювання.

Цей словник буде передано обробнику шаблонів (форматувальнику) за допомогою іменованого параметра global_vars=ваш_словник. Ось повний запис методу:

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

Розробник шаблонів: як отримати доступ до додаткових відомостей

Отримати доступ до додаткових відомостей (словника globals) у шаблоні можна за допомогою шаблонної функції:

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

де записом ідентифікатор є будь-яка коректна назва змінної. Ця функція перевіряє, чи у додаткових відомостях, наданих розробником, міститься відповідна назва. Якщо це так, функція призначає надане значення змінної локальній змінній із вказаною назвою. Якщо назву змінної не буде виявлено у додаткових відомостях і якщо вказано вираз, буде виконано обчислення виразу із призначенням результату локальній змінній. Якщо не буде знайдено ні значення, ні виразу, локальній змінній буде призначено порожній рядок ('').

Шаблон може встановлювати значення у словнику globals за допомогою шаблонної функції:

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

Ця функція встановлює значення пари словника globals ключ:значення ідентифікатор:значення, де значенням є значення локальної змінної шаблона ідентифікатор. Якщо такої локальної змінної не існує, значення буде встановлено для результату обчислення виразу.

Зауваження щодо відмінностей між режимами

У роботі трьох режимів програмування, режиму єдиної функції, режиму програмування шаблонів та загального режиму програмування є відмінності. Режим єдиної функції є «простим», тому у ньому багато можливостей програмування «приховано».

Відмінності:

  • У режимі єдиної функції значення стовпчика завжди передається як «невидимий» перший аргумент функції, включено до шаблона.

  • У режимі єдиної функції не передбачено підтримки розрізнення між змінними і рядками; усі значення є рядками.

  • У наведеному нижче шаблоні режиму єдиної функції шаблон повертає або назву циклу, або рядок «no series»:

    {series:ifempty(no series)}
    

    Еквівалентний шаблон у режимі програмування шаблонів:

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

    Еквівалентний шаблон у режимі загального програмування:

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

    Першим аргументом ifempty є значення поля series, а другим аргументом — рядок no series. У режимі єдиної функції перший аргумент, значення поля, передається автоматично (невидимий аргумент).

  • Декілька шаблонних функцій, зокрема booksize() і current_library_name(), не приймають аргументів. Через наявність невидимого аргументу ви не зможете скористатися цими функціями у режимі єдиної функції.

  • У режимі єдиної функції не можна скористатися вкладеними функціями, коли одна з функцій викликає іншу функцію для обчислення свого аргументу. Наприклад, цей шаблон, який призначено для отримання перших 5 літер значення назви циклу у верхньому регістрі, не працюватиме у режимі єдиної функції:

    {series:uppercase(substr(0,5))}
    
  • У шаблонному режимі програмування та загальному режимі програмування передбачено підтримку вкладених функцій. Наведений вище шаблон у шаблонному режимі програмування можна записати так:

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

    У загальному режимі програмування це було б:

    program: uppercase(substr(field('series'), 0,5))
    
  • Як було зауважено вище у розділі Режим програмування шаблонів, використання символів { і } у рядкових літералах режиму програмування шаблонів може призвести до помилок або неочікуваних результатів, оскільки ці символи заважають нормальній роботі обробника шаблонів. Він намагається обробити їх як межі шаблона, а не символи. У деяких, але не в усіх випадках, ви можете замінити { на [[ і } на ]]. Загалом кажучи, якщо у вашій програмі є символи { і }, вам слід користуватися загальним режимом програмування.

Визначені користувачем шаблонні функції Python

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.

Спеціальні нотатки щодо шаблонів збереження або надсилання

До шаблонів, які використовуються для дій зберегти на диск і надіслати на пристрій, застосовується спеціальна обробка. Значення полів буде очищено із заміною символів, які мають спеціальне значення у файлових системах, зокрема символів похилих рисок, на символи підкреслювання. Це означає, що текст поля не можна використовувати для створення тек. Втім, похилі риски не замінюватимуться у рядках префікса і суфікса, отже похилі риски у цих рядках можна використати для створення тек. Таким чином, ви можете створити структуру тек довільного рівня вкладеності.

Наприклад, припустімо ми хочемо створити структуру тек цикл/номер_у_циклі - назва, із умовою, що якщо циклу не існує, назву слід розташувати у верхній теці. Шаблон для виконання цього завдання:

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

Похила риска і дефіс мають з’являтися, лише якщо цикл не є порожнім.

Функція lookup надає вам змогу виконувати виконувати навіть ще вишуканішу обробку. Наприклад, припустімо, що якщо книга належить до циклу, ми хочемо мати структуру тек цикл/номер у циклі - назва.формат. Якщо книга не належить до циклу, нам потрібна структура тек жанр/упорядковане ім’я автора/назва.формат. Якщо ж книга не належить до певного жанру, слід скористатися текою „Unknown“. Нам потрібно два повністю різних шляхи, залежно від значення параметра циклу.

Для виконання цього завдання ми:

  1. Створимо складене поле (надавши йому назву #aa), що міститиме дані {series}/{series_index} - {title}. Якщо значення циклу (series) є непорожнім, такий шаблон дасть нам цикл/номер_у_циклі - назва.

  2. Створимо складене поле (надавши йому назву #bb) із вмістом {#genre:ifempty(Unknown)}/{author_sort}/{title}. Цей шаблон дає жанр/упорядковані_автори/назва. Якщо немає даних щодо жанру, запис жанру буде замінено на Unknown.

  3. Встановимо шаблон збереження {series:lookup(.,#aa,#bb)}. Цей шаблон вибирає складене поле #aa, якщо значення запису циклу є непорожнім, і складене поле #bb, якщо значення запису циклу є порожнім. Таким чином, ми матимемо повністю різні шляхи для збереження книг, залежно від того, чи є запис series порожнім.

Підказки

  • Для перевірки шаблонів скористайтеся засобом перевірки шаблонів. Додайте пункт засобу до контекстного меню для книг у бібліотеці і/або встановіть для нього клавіатурне скорочення.

  • У шаблонах можна використовувати інші шаблони шляхом посилання на складені стовпчики, які побудовано за допомогою бажаних шаблонів. Крім того, ви можете скористатися «Збереженими шаблонами».

  • У наборі метаданих ви можете вказати порожні значення поля (або вказати будь-який еквівалент порожнього поля) за допомогою спеціального шаблона {}. Цей шаблон завжди обробляється як порожній рядок.

  • Описана вище методика для показу чисел працює зі стандартним полем series_index, навіть якщо вміст відповідного поля є порожнім.

Template function reference