calibre 模板语言

calibre模板语言是calibre特定的语言,用于在calibre中执行任务,例如指定文件路径、格式化值和计算用户指定列的值。示例:

  • 在将文件从calibre书库保存到磁盘或电子书阅读器时,指定文件夹结构和文件名。

  • 定义规则以将图标和颜色添加到calibre图书列表中。

  • 定义包含来自其他列的数据的“虚拟列”。

  • 高级书库搜索。

  • 高级元数据搜索和替换。

该语言是围绕“模板”的概念构建的,它指定要使用的图书元数据、元数据的计算以及如何格式化。

基本模板

基本模板由一个或多个“模板表达式”组成。 “模板表达式”由大括号 (“{}”) 中的文本和名称组成,并由正在处理的书籍中的相应元数据替换。 例如,calibre 中用于将书籍保存到设备的默认模板有 4 个“模板表达式”:

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

对于“艾萨克·阿西莫夫”所著的《基础》一书,将变为:

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 图书列表中的列标题上。 自定义列的查找名称始终以“#”开头。 对于系列类型列,有一个名为“#lookup name_index”的附加字段,它是该系列中该书的系列索引。 例如,如果您有一个名为“#myseries”的自定义系列列,那么还将有一个名为“#myseries_index”的列。 标准系列列的索引名为“series_index”。

除了基于标准列的字段之外,您还可以使用:

  • {formats} - calibre 书库中书籍可用的格式列表

  • {identifiers:select(isbn)} - 书籍的 ISBN

If the metadata for the field for a given book is not defined then the field in the template is replaced by the empty string (''). For example, consider the following template:

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

If Asimov's book "Second Foundation" is in the series "Foundation" then the template produces:

Asimov, Isaac/Foundation/Second Foundation 3

If a series has not been entered for the book then the template produces:

Asimov, Isaac/Second Foundation

The template processor automatically removes multiple slashes and leading or trailing spaces.

高级格式

In addition to metadata substitution, templates can conditionally include additional text and control how substituted data is formatted.

Conditionally including text

有时,您希望仅当字段不为空时文本才出现在输出中。 常见的情况是“series”和“series_index”,其中您不需要任何内容或用连字符分隔两个值。 calibre 使用特殊的“模板表达式”语法来处理这种情况。

For example and using the above Foundation example, assume you want the template to produce Foundation - 3 - Second Foundation. This template produces that output:

{series} - {series_index} - {title}

但是,如果一本书没有系列,模板将生成“- - 标题”,这可能不是您想要的。 一般来说,人们希望结果是没有无关连字符的标题。 您可以使用以下模板语法来完成此操作:

{field:|prefix_text|suffix_text}

这个“模板表达式”表示,如果“field”的值为“XXXX”,那么结果将为“prefix_textXXXXXsuffix_text”。 如果“field”为空(没有值),那么结果将是空字符串(什么也没有),因为前缀和后缀被忽略。 前缀和后缀可以包含空格。

Do not use subtemplates (`{ ... }`) or functions (see below) in the prefix or the suffix.

Using this syntax, we can solve the above no-series problem with the template:

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

仅当该书具有系列索引时才会包含连字符,而该索引仅当它具有系列时才包含。 再次继续 Foundation 示例,模板将生成“Foundation - 1 - Second Foundation”。

Notes:

  • You must include the colon after the lookup name if you are using a prefix or a suffix.

  • You must either use either no or both | characters. Using one, as in {field:| - }, is not allowed.

  • It is OK to provide no text for either the prefix or the suffix, such as in {series:|| - }. The template {title:||} is the same as {title}.

Formatting

Suppose you want the series_index to be formatted as three digits with leading zeros. This does the trick:

{series_index:0>3s} - Three digits with leading zeros

For trailing zeros, use:

{series_index:0<3s} - Three digits with trailing zeros

如果您使用带有小数值的系列索引,例如 1.1,您可能希望小数点对齐。 例如,您可能希望索引 1 和 2.5 显示为 01.00 和 02.50,以便它们在进行词法排序的设备上正确排序。 为此,请使用:

{series_index:0>5.2f} - Five characters consisting of two digits with leading zeros, a decimal point, then 2 digits after the decimal point.

If you want only the first two letters of the data, use:

{author_sort:.2} - Only the first two letters of the author sort name

大部分 calibre 模板语言格式都来自 Python。 有关这些高级格式化操作的语法的更多详细信息,请参阅“Python 文档 <https://docs.python.org/3/library/string.html#formatstrings>”_。

Using templates to define custom columns

模板可用于显示 calibre 元数据中没有的信息,或以不同于 calibre 正常格式的方式显示元数据。 例如,您可能想要显示“ISBN”,这是 calibre 不显示的字段。 您可以通过创建类型为“从其他列构建的列”(以下称为“复合列”)的自定义列来完成此操作,并提供一个模板来生成显示的文本。 该列将显示评估模板的结果。 例如,要显示 ISBN,请创建列并在模板框中输入“{identifiers:select(isbn)}”。 要显示包含两个系列自定义列的值(以逗号分隔)的列,请使用``{#series1:||,}{#series2}``。

Composite columns can use any template option, including formatting.

注意:您无法编辑复合列中显示的数据。 相反,您可以编辑源列。 如果您编辑复合列,例如通过双击它,calibre 将打开模板进行编辑,而不是基础数据。

Templates and plugboards

插件板用于在发送到设备和保存到磁盘操作期间更改写入书籍的元数据。 插件板允许您指定一个模板来提供要写入书籍元数据的数据。 您可以使用插件板修改以下字段:作者、author_sort、语言、出版商、标签、书名、title_sort。 此功能可以帮助想要在设备上使用图书中的不同元数据的人们解决排序或显示问题。

创建插接板时,您可以指定插接板要使用的格式和设备。 提供了一个特殊的设备“save_to_disk”,在保存格式时使用(而不是将它们发送到设备)。 选择格式和设备后,您可以选择要更改的元数据字段,并提供模板来提供新值。 这些模板“连接”到其目标字段,因此称为“插件板”。 您当然可以在这些模板中使用复合列。

Plugboards are quite flexible and can be written in Single Function Mode, Template Program Mode, General Program Mode, or Python Template mode.

当插件板可能适用时(内容服务器、保存到磁盘或发送到设备),calibre 会搜索定义的插件板,为给定的格式和设备选择正确的插件板。 例如,要为发送到 ANDROID 设备的 EPUB 图书找到合适的插件板,calibre 使用以下搜索顺序搜索插件板:

  • a plugboard with an exact match on format and device, e.g., EPUB and ANDROID

  • a plugboard with an exact match on format and the special any device choice, e.g., EPUB and any device

  • a plugboard with the special any format choice and an exact match on device, e.g., any format and ANDROID

  • a plugboard with any format and any device

标签和作者字段有特殊处理,因为这两个字段都可以容纳多个项目。 一本书可以有多个标签和多个作者。 当您指定要更改这两个字段之一时,将检查模板的结果以查看是否存在多个项目。 对于标签,只要 calibre 找到逗号,结果就会被分割。 例如,如果模板生成值“Thriller, Horror”,则结果将是两个标签“Thriller”和“Horror”。 无法在标签中间放置逗号。

作者也会发生同样的情况,但使用不同的字符进行剪切,即“&”(与号)而不是逗号。 例如,如果模板生成值“Blogs, Joe&Posts, Susan”,则该书最终将有两个作者:“Blogs, Joe”和“Posts, Susan”。 如果模板生成值“博客,乔;帖子,苏珊”,那么这本书将有一位作者的名字相当奇怪。

当将书籍保存到磁盘或写入设备时,插件板会影响写入书中的元数据。 插件板不会影响“保存到磁盘”和“发送到设备”用来创建文件名的元数据。 相反,文件名是使用在适当的首选项窗口中输入的模板构建的。

Using functions in templates - Single Function Mode

假设您想要以大写形式显示某个字段的值,而该字段通常为标题大写形式。 您可以使用“模板函数”来完成此操作。 例如,要以大写形式显示标题,请使用“uppercase”函数,如“{title:uppercase()}”。 要以标题大小写显示它,请使用``{title:titlecase()}``。

函数进入模板的格式部分,在 : 之后和第一个 | 之前,或者如果没有使用前缀/后缀,则在结束的 } 之前。 如果您同时具有格式和函数引用,则该函数位于第二个 : 后面。 函数返回模板中指定的列的值,经过适当修改。

The syntax for using functions is one of:

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

函数名称后面必须始终跟有左括号和右括号。 有些函数需要额外的值(参数),这些值位于括号内。 参数之间用逗号分隔。 文字逗号(逗号作为文本,而不是参数分隔符)前面必须有反斜杠 (\) 。 最后一个(或唯一的)参数不能包含文本右括号。

Functions are evaluated before format specifications and the prefix/suffix. See further down for an example of using both a format and a function.

重要:如果您有编程经验,请注意“单函数模式”中的语法不是您所期望的。 字符串不加引号,并且空格很重要。 所有参数都被视为常量; 没有表达式。

Do not use subtemplates (`{ ... }`) as function arguments. Instead, use Template Program Mode and General Program Mode.

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 模板函数参考. 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.

Using functions and formatting in the same template

假设您有一个整数自定义列“#myint”,您希望显示前导零,如“003”。 实现此目的的一种方法是使用“0>3s”的格式。 但是,默认情况下,如果数字(整数或浮点数)等于零,则该值将显示为空字符串,因此零值将生成空字符串,而不是“000”。 如果您想查看“000”值,则可以使用格式字符串和“ifempty”函数将空值更改回零。 模板将是:

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

Note that you can use the prefix and suffix as well. If you want the number to appear as [003] or [000], then use the template:

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

General Program Mode

General Program Mode (GPM) replaces template expressions with a program written in the template language. The syntax of the language is defined by the following grammar:

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

Notes:

  • a top_expression always has a value. The value of an expression_list is the value of the last top_expression in the list. For example, the value of the expression list 1;2;'foobar';3 is 3.

  • In a logical context, any non-empty value is True

  • In a logical context, the empty value is False

  • Strings and numbers can be used interchangeably. For example, 10 and '10' are the same thing.

  • Comments are lines starting with a '#' character. Comments beginning later in a line are not supported.

Operator precedence

The operator precedence (order of evaluation) from highest (evaluated first) to lowest (evaluated last) is:

  • Function calls, constants, parenthesized expressions, statement expressions, assignment expressions, field references.

  • Unary plus (+) and minus (-). These operators evaluate right to left.

    These and all the other arithmetic operators return integers if the expression results in a fractional part equal to zero. For example, if an expression returns 3.0 it is changed to 3.

  • Multiply (*) and divide (/). These operators are associative and evaluate left to right. Use parentheses if you want to change the order of evaluation.

  • Add (+) and subtract (-). These operators are associative and evaluate left to right.

  • Numeric and string comparisons. These operators return '1' if the comparison succeeds, otherwise the empty string (''). Comparisons are not associative: a < b < c is a syntax error.

  • String concatenation (&). The & operator returns a string formed by concatenating the left-hand and right-hand expressions. Example: 'aaa' & 'bbb' returns 'aaabbb'. The operator is associative and evaluates left to right.

  • Unary logical not (!). This operator returns '1' if the expression is False (evaluates to the empty string), otherwise ''.

  • Logical and (&&). This operator returns '1' if both the left-hand and right-hand expressions are True, or the empty string '' if either is False. It is associative, evaluates left to right, and does short-circuiting.

  • 逻辑或 (||)。 如果左侧表达式或右侧表达式为 True,则此运算符返回 '1';如果均为 False,则返回 ''。 它是关联的,从左到右求值,并进行“短路”<https://chortle.ccsu.edu/java5/Notes/chap40/ch40_2.html>`_。 它是一个“包含或”,如果左侧和右侧表达式都为 True,则返回“'1””。

Field references

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 expressions

If 表达式首先评估``condition``。 如果“condition”为 True(非空值),则评估“then”子句中的“expression_list”。 如果为 False,则评估“elif”或“else”子句中的“expression_list”。 elifelse 部分是可选的。 单词“if”、“then”、“elif”、“else”和“fi”被保留; 您不能将它们用作标识符名称。 您可以在任何有意义的地方放置换行符和空格。 “condition”是“top_expression”,而不是“expression_list”; 不允许使用分号。 “表达式列表”是以分号分隔的“top_表达式”序列。 if 表达式返回已计算的 expression_list 中最后一个 top_expression 的结果,如果没有计算表达式列表,则返回空字符串。

Examples:

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

Nested if example:

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

As said above, an if produces a value. This means that all the following are equivalent:

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

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.

Example: This template removes the first hierarchical name for each value in Genre (#genre), constructing a list with the new names:

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”的值。

Function definition

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”。 如果没有为该参数提供参数,则使用默认值,否则该参数将设置为空字符串。

The return statement can be used in a local function.

A function must be defined before it can be used.

Example: This template computes an approximate duration in years, months, and days from a number of days. The function to_plural() formats the computed values. Note that the example also uses the & operator:

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

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

There are two forms of relational operators: string comparisons and numeric comparisons.

String comparisons do case-insensitive string comparison using lexical order. The supported string comparison operators are ==, !=, <, <=, >, >=, in, inlist, and inlist_field. For the in operator, the result of the left hand expression is interpreted as a regular expression pattern. The in operator is True if the value of left-hand regular expression matches the value of the right hand expression.

The inlist operator is true if the left hand regular expression matches any one of the items in the right hand list where the items in the list are separated by commas. The inlist_field operator is true if the left hand regular expression matches any of the items in the field (column) named by the right hand expression, using the separator defined for the field. NB: the inlist_field operator requires the right hand expression to evaluate to a field name, while the inlist operator requires the right hand expression to evaluate to a string containing a comma-separated list. Because of this difference, inlist_field is substantially faster than inlist because no string conversions or list constructions are done. The regular expressions are case-insensitive.

The numeric comparison operators are ==#, !=#, <#, <=#, >#, >=#. The left and right expressions must evaluate to numeric values with two exceptions: both the string value "None" (undefined field) and the empty string evaluate to the value zero.

例如:

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

  • program: 'f.o' in field('series') returns '1' if the book's series matches the regular expression f.o (e.g., foo, Off Onyx, etc.), otherwise ''.

  • program: 'science' inlist $#genre returns '1' if any of the values retrieved from the book's genres match the regular expression science, e.g., Science, History of Science, Science Fiction etc., otherwise ''.

  • program: '^science$' inlist $#genre returns '1' if any of the book's genres exactly match the regular expression ^science$, e.g., Science, otherwise ''. The genres History of Science and Science Fiction don't match.

  • program: 'asimov' inlist $authors returns '1' if any author matches the regular expression asimov, e.g., Asimov, Isaac or Isaac Asimov, otherwise ''.

  • program: 'asimov' inlist_field 'authors' returns '1' if any author matches the regular expression asimov, e.g., Asimov, Isaac or Isaac Asimov, otherwise ''.

  • program: 'asimov$' inlist_field 'authors' returns '1' if any author matches the regular expression asimov$, e.g., Isaac Asimov, otherwise ''. It doesn't match Asimov, Isaac because of the $ anchor in the regular expression.

  • 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 returns 'no' because the > operator does a lexical comparison.

  • program: if 11 ># 2 then 'yes' else 'no' fi returns 'yes' because the ># operator does a numeric comparison.

Functions in General Program Mode

See 模板函数参考 for the list of functions built into the template language.

Notes:

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

More complex programs in template expressions - Template Program Mode

“模板程序模式”(“TPM”) 是“通用程序模式 <general_mode>”和“单功能模式 <single_mode>”的混合。 “TPM”与单函数模式的不同之处在于它允许编写引用其他元数据字段的模板表达式、使用嵌套函数、修改变量和进行算术。 它与“通用程序模式”的不同之处在于,模板包含在“{”和“}”字符之间,并且不以单词“program:”开头。 模板的程序部分是通用程序模式表达式列表。

示例:假设您希望模板显示一本书的系列,如果有,否则显示自定义字段 #genre 的值。 您无法在“单函数模式 <single_mode>”中执行此操作,因为您无法引用模板表达式中的另一个元数据字段。 在“TPM”中,您可以,如以下表达式所示:

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

The example shows several things:

  • TPM is used if the expression begins with :' and ends with '}. Anything else is assumed to be in Single Function Mode.

    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.

  • white space is ignored and can be used anywhere within the expression.

  • constant strings are enclosed in matching quotes, either ' or ".

在“TPM”中,在字符串文字中使用“{”和“}”字符可能会导致错误或意外结果,因为它们会混淆模板处理器。 它尝试将它们视为模板表达式边界,而不是字符。 在某些情况下,但不是所有情况下,您可以将 { 替换为 [[ ,将 } 替换为 ]] 。 一般来说,如果您的程序包含“{”和“}”字符,那么您应该使用“通用程序模式”。

Python Template Mode

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.

A PTM template begins with:

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'

You can add the above text to your template using the context menu, usually accessed with a right click. The comments are not significant and can be removed. You must use python indenting.

The context object supports str(context) that returns a string of the context's contents, and context.attributes that returns a list of the attribute names in the context.

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

The output in Book details looks like this:

电子书转换对话

保存的模板

“通用程序模式 <general_mode>”和“Python 模板模式 <python_mode>”都支持保存模板并从另一个模板调用这些模板,就像调用存储的函数一样。 您可以使用“首选项->高级->模板功能”保存模板。 该对话框中提供了更多信息。 您可以像调用函数一样调用模板,并根据需要传递位置参数。 参数可以是任何表达式。 调用模板的示例,假设存储的模板名为“foo”:

  • foo() -- call the template passing no arguments.

  • foo(a, b) call the template passing the values of the two variables a and b.

  • foo(if field('series') then field('series_index') else 0 fi) -- if the book has a series then pass the series_index, otherwise pass the value 0.

在 GPM 中,您可以使用“arguments”函数检索在调用存储模板时传递的参数。 它声明并初始化局部变量,即有效的参数。 变量是位置变量; 他们在同一位置获取调用中给出的参数值。 如果调用中未提供相应的参数,则“arguments”会为该变量分配提供的默认值。 如果没有默认值,则该变量将设置为空字符串。 例如,以下“arguments”函数声明 2 个变量“key”、“alternate”:

arguments(key, alternate='series')

Examples, again assuming the stored template is named foo:

  • foo('#myseries') -- argument key is assigned the value 'myseries' and the argument alternate is assigned the default value 'series'.

  • foo('series', '#genre') the variable key is assigned the value 'series' and the variable alternate is assigned the value '#genre'.

  • foo() -- the variable key is assigned the empty string and the variable alternate is assigned the value 'series'.

在PTM中,参数在“arguments”参数中传递,该参数是字符串列表。没有任何方法可以指定默认值。您必须检查“参数”列表的长度,以确保参数的数量符合您的期望。

测试存储模板的一种简单方法是使用“模板测试器”对话框。 为了便于访问,请在“首选项->高级->键盘快捷键->模板测试器”中为其提供键盘快捷键。 为“存储的模板”对话框提供快捷方式将有助于在测试器和编辑存储的模板的源代码之间更快地切换。

Providing additional information to templates

开发人员可以选择将附加信息传递给模板处理器,例如应用程序特定的书籍元数据或有关处理器被要求执行什么的信息。模板可以访问此信息并在评估期间使用它。

Developer: how to pass additional information

附加信息是一个包含“变量名:变量值”对的 Python 字典,其中值必须是字符串。 模板可以访问字典,创建名为“variable_name”的模板局部变量,其中包含值“variable_value”。 用户无法更改名称,因此最好使用不会与其他模板局部变量冲突的名称,例如在名称前添加下划线。

This dictionary is passed to the template processor (the formatter) using the named parameter global_vars=your_dict. The full method signature is:

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

Template writer: how to access the additional information

You access the additional information (the globals dictionary) in a template using the template function:

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

其中“id”是任何合法的变量名称。 该函数检查开发者提供的附加信息中是否包含该名称。 如果是,则该函数将提供的值分配给具有该名称的模板局部变量。 如果附加信息中没有该名称,并且提供了“表达式”,则计算“表达式”并将结果分配给局部变量。 如果既没有提供值也没有提供表达式,函数会将空字符串 ('') 分配给局部变量。

A template can set a value in the globals dictionary using the template function:

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

此函数用于设置“全局范围”词典,key:value配对``id:value`` ,其中“value”是模板局部变量“id”的值。如果该局部变量不存在,则将“value”设置为“表达式”的求值结果。

Notes on the difference between modes

The three program modes, Single Function Mode (SFM), Template Program Mode (TPM), and General Program Mode (GPM), work differently. SFM is intended to be 'simple' so it hides a lot of programming language bits.

Differences:

  • In SFM the value of the column is always passed as an 'invisible' first argument to a function included in the template.

  • SFM doesn't support the difference between variables and strings; all values are strings.

  • The following SFM template returns either the series name or the string "no series":

    {series:ifempty(no series)}
    

    The equivalent template in TPM is

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

    The equivalent template in GPM is:

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

    The first argument to ifempty is the value of the field series. The second argument is the string no series. In SFM the first argument, the value of the field, is automatically passed (the invisible argument).

  • Several template functions, for example booksize() and current_library_name(), take no arguments. Because of the 'invisible argument' you cannot use these functions in SFM.

  • 嵌套函数,指其中一个函数调用另一个函数来计算参数,不能在SFM中使用。例如,此模板旨在返回升级后的系列值的前5个字符,在SFM中不起作用:

    {series:uppercase(substr(0,5))}
    
  • TPM and GPM support nested functions. The above template in TPM would be:

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

    In GPM it would be:

    program: uppercase(substr(field('series'), 0,5))
    
  • 如上面“模板程序模式 1”部分所述,在“TPM”字符串文字中使用“{”和“}”字符可能会导致错误或意外结果,因为它们会混淆模板处理器。 它尝试将它们视为模板边界,而不是字符。 在某些情况下,但不是所有情况下,您可以将 { 替换为 [[ ,将 } 替换为 ]] 。 一般来说,如果您的程序包含“{”和“}”字符,那么您应该使用“通用程序模式”。

User-defined Python template functions

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.

Special notes for save/send templates

当模板用于“保存到磁盘”或“发送到设备”模板时,会应用特殊处理。 字段的值被清理,用下划线替换文件系统特有的字符,包括斜杠。 这意味着字段文本不能用于创建文件夹。 但是,前缀或后缀字符串中的斜杠不会更改,因此这些字符串中的斜杠将导致创建文件夹。 因此,您可以创建可变深度的文件夹结构。

For example, assume we want the folder structure series/series_index - title, with the caveat that if series does not exist, then the title should be in the top folder. The template to do this is:

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

The slash and the hyphen appear only if series is not empty.

查找功能让我们可以进行更高级的处理。 例如,假设一本书有一个系列,那么我们需要文件夹结构“系列/系列索引 - title.fmt”。 如果这本书没有系列,那么我们需要文件夹结构“genre/author_sort/title.fmt”。 如果这本书没有类型,那么我们想使用“未知”。 我们想要两条完全不同的路径,具体取决于系列的值。

To accomplish this, we:

  1. Create a composite field (give it lookup name #aa) containing {series}/{series_index} - {title}. If the series is not empty, then this template will produce series/series_index - title.

  2. 创建一个包含“{#genre:ifempty(Unknown)}/{Author_sort}/{title}'的复合字段(为其查找名称#bb)。此模板生成“genre/Author_sort/title”,其中空的genre被“Unknown”替换。

  3. 将保存模板设置为“{series:lookup(.,#aa,#bb)}`”。如果系列不为空,则此模板选择复合字段“#aa”,如果系列为空,请选择复合字段。因此,根据“系列”是否为空,我们有两个完全不同的保存路径。

Tips

  • Use the Template Tester to test templates. Add the tester to the context menu for books in the library and/or give it a keyboard shortcut.

  • Templates can use other templates by referencing composite columns built with the desired template. Alternatively, you can use Stored Templates.

  • In a plugboard, you can set a field to empty (or whatever is equivalent to empty) by using the special template {}. This template will always evaluate to an empty string.

  • The technique described above to show numbers even if they have a zero value works with the standard field series_index.

模板函数参考