mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
String changes
This commit is contained in:
parent
3c7f856ddf
commit
1c5eeeae83
@ -360,7 +360,7 @@ by the conversion pipeline. There is a wizard to help you customize the regular
|
||||
your document. Click the magic wand beside the expression box, and click the 'Test' button after composing
|
||||
your search expression. Successful matches will be highlighted in Yellow.
|
||||
|
||||
The search works by using a python regular expression. All matched text is simply removed from
|
||||
The search works by using a Python regular expression. All matched text is simply removed from
|
||||
the document or replaced using the replacement pattern. The replacement pattern is optional, if left blank
|
||||
then text matching the search pattern will be deleted from the document. You can learn more about regular expressions
|
||||
and their syntax at :ref:`regexptutorial`.
|
||||
|
@ -294,10 +294,10 @@ The e-book-editor can be started as::
|
||||
|
||||
calibre-debug -t /path/to/be/edited
|
||||
|
||||
Using an interactive python interpreter
|
||||
Using an interactive Python interpreter
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can insert the following two lines of code to start an interactive python session at that point::
|
||||
You can insert the following two lines of code to start an interactive Python session at that point::
|
||||
|
||||
from calibre import ipython
|
||||
ipython(locals())
|
||||
@ -307,10 +307,10 @@ locally defined variables (variables in the local scope). The interactive prompt
|
||||
for object properties and you can use the various Python facilities for introspection, such as
|
||||
:func:`dir`, :func:`type`, :func:`repr`, etc.
|
||||
|
||||
Using the python debugger as a remote debugger
|
||||
Using the Python debugger as a remote debugger
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can use the builtin python debugger (pdb) as a remote debugger from the
|
||||
You can use the builtin Python debugger (pdb) as a remote debugger from the
|
||||
command line. First, start the remote debugger at the point in the calibre code
|
||||
you are interested in, like this::
|
||||
|
||||
@ -326,7 +326,7 @@ the debugging session::
|
||||
|
||||
calibre-debug -c "from calibre.rpdb import cli; cli()"
|
||||
|
||||
You can read about how to use the python debugger in the `python stdlib docs
|
||||
You can read about how to use the Python debugger in the `Python stdlib docs
|
||||
for the pdb module <https://docs.python.org/2/library/pdb.html#debugger-commands>`_.
|
||||
|
||||
.. note::
|
||||
@ -336,13 +336,13 @@ for the pdb module <https://docs.python.org/2/library/pdb.html#debugger-commands
|
||||
``cli(port=1234)``.
|
||||
|
||||
.. note::
|
||||
The python debugger cannot handle multiple threads, so you have to
|
||||
The Python debugger cannot handle multiple threads, so you have to
|
||||
call set_trace once per thread, each time with a different port number.
|
||||
|
||||
Using the debugger in your favorite python IDE
|
||||
Using the debugger in your favorite Python IDE
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
It is possible to use the builtin debugger in your favorite python IDE, if it
|
||||
It is possible to use the builtin debugger in your favorite Python IDE, if it
|
||||
supports remote debugging. The first step is to add the calibre src checkout to
|
||||
the ``PYTHONPATH`` in your IDE. In other words, the directory you set as
|
||||
``CALIBRE_DEVELOP_FROM`` above, must also be in the ``PYTHONPATH`` of your IDE.
|
||||
@ -353,15 +353,15 @@ remote debugger to calibre at the point of interest, for example in the main
|
||||
function. Then run calibre as normal. Your IDE should now be able to connect to
|
||||
the remote debugger running inside calibre.
|
||||
|
||||
Executing arbitrary scripts in the calibre python environment
|
||||
Executing arbitrary scripts in the calibre Python environment
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The :command:`calibre-debug` command provides a couple of handy switches to execute your own
|
||||
code, with access to the calibre modules::
|
||||
|
||||
calibre-debug -c "some python code"
|
||||
calibre-debug -c "some Python code"
|
||||
|
||||
is great for testing a little snippet of code on the command line. It works in the same way as the -c switch to the python interpreter::
|
||||
is great for testing a little snippet of code on the command line. It works in the same way as the -c switch to the Python interpreter::
|
||||
|
||||
calibre-debug myscript.py
|
||||
|
||||
|
@ -254,7 +254,7 @@ expressions in a single operation.
|
||||
Function mode
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Function mode allows you to write arbitrarily powerful python functions that
|
||||
Function mode allows you to write arbitrarily powerful Python functions that
|
||||
are run on every Find/replace. You can do pretty much any text manipulation you
|
||||
like in function mode. For more information, see :doc:`function_mode`.
|
||||
|
||||
|
@ -3,14 +3,14 @@ Function Mode for Search & Replace in the Editor
|
||||
|
||||
The :guilabel:`Search & replace` tool in the editor support a *function mode*.
|
||||
In this mode, you can combine regular expressions (see :doc:`regexp`) with
|
||||
arbitrarily powerful python functions to do all sorts of advanced text
|
||||
arbitrarily powerful Python functions to do all sorts of advanced text
|
||||
processing.
|
||||
|
||||
In the standard *regexp* mode for search and replace, you specify both a
|
||||
regular expression to search for as well as a template that is used to replace
|
||||
all found matches. In function mode, instead of using a fixed template, you
|
||||
specify an arbitrary function, in the
|
||||
`python programming language <https://docs.python.org/2.7/>`_. This allows
|
||||
`Python programming language <https://docs.python.org/2.7/>`_. This allows
|
||||
you to do lots of things that are not possible with simple templates.
|
||||
|
||||
Techniques for using function mode and the syntax will be described by means of
|
||||
@ -45,7 +45,7 @@ the editor leaves individual hyphens alone, so you can use the this function to
|
||||
replace them with em-dashes.
|
||||
|
||||
To create a new function, simply click the :guilabel:`Create/edit` button to create a new
|
||||
function and copy the python code from below.
|
||||
function and copy the Python code from below.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -53,7 +53,7 @@ function and copy the python code from below.
|
||||
return match.group().replace('--', '—').replace('-', '—')
|
||||
|
||||
Every :guilabel:`Search & replace` custom function must have a unique name and consist of a
|
||||
python function named replace, that accepts all the arguments shown above.
|
||||
Python function named replace, that accepts all the arguments shown above.
|
||||
For the moment, we wont worry about all the different arguments to
|
||||
``replace()`` function. Just focus on the ``match`` argument. It represents a
|
||||
match when running a search and replace. Its full documentation in available
|
||||
@ -206,7 +206,7 @@ HTML Table of Contents, ready to be pasted into :file:`toc.html`.
|
||||
|
||||
The function above is heavily commented, so it should be easy to follow. The
|
||||
key new feature is the use of another useful extra argument to the
|
||||
``replace()`` function, the ``data`` object. The ``data`` object is a python
|
||||
``replace()`` function, the ``data`` object. The ``data`` object is a Python
|
||||
*dict* that persists between all successive invocations of ``replace()`` during
|
||||
a single :guilabel:`Replace All` operation.
|
||||
|
||||
@ -223,7 +223,7 @@ you would be better off using the dedicated Table of Contents tool in
|
||||
The API for the function mode
|
||||
-----------------------------
|
||||
|
||||
All function mode functions must be python functions named replace, with the
|
||||
All function mode functions must be Python functions named replace, with the
|
||||
following signature::
|
||||
|
||||
def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
|
||||
@ -239,7 +239,7 @@ The ``match`` argument
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``match`` argument represents the currently found match. It is a
|
||||
`python Match object <https://docs.python.org/2.7/library/re.html#match-objects>`_.
|
||||
`Python Match object <https://docs.python.org/2.7/library/re.html#match-objects>`_.
|
||||
Its most useful method is ``group()`` which can be used to get the matched
|
||||
text corresponding to individual capture groups in the search regular
|
||||
expression.
|
||||
@ -278,7 +278,7 @@ for the current book's language.
|
||||
The ``data`` argument
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This a simple python ``dict``. When you run
|
||||
This a simple Python ``dict``. When you run
|
||||
:guilabel:`Replace All`, every successive match will cause ``replace()`` to be
|
||||
called with the same ``dict`` as data. You can thus use it to store arbitrary
|
||||
data between invocations of ``replace()`` during a :guilabel:`Replace All`
|
||||
@ -331,7 +331,7 @@ Debugging your functions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can debug the functions you create by using the standard ``print()``
|
||||
function from python. The output of print will be displayed in a popup window
|
||||
function from Python. The output of print will be displayed in a popup window
|
||||
after the Find/replace has completed. You saw an example of using ``print()``
|
||||
to output an entire table of contents above.
|
||||
|
||||
|
@ -91,19 +91,19 @@ Now in the :guilabel:`Advanced Mode` of the Custom news sources dialog, you sho
|
||||
.. image:: images/bbc_advanced.png
|
||||
:align: center
|
||||
|
||||
You can see that the fields from the :guilabel:`Basic mode` have been translated to python code in a straightforward manner. We need to add instructions to this recipe to use the print version of the articles. All that's needed is to add the following two lines:
|
||||
You can see that the fields from the :guilabel:`Basic mode` have been translated to Python code in a straightforward manner. We need to add instructions to this recipe to use the print version of the articles. All that's needed is to add the following two lines:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace('https://', 'https://newsvote.bbc.co.uk/mpapps/pagetools/print/')
|
||||
|
||||
This is python, so indentation is important. After you've added the lines, it should look like:
|
||||
This is Python, so indentation is important. After you've added the lines, it should look like:
|
||||
|
||||
.. image:: images/bbc_altered.png
|
||||
:align: center
|
||||
|
||||
In the above, ``def print_version(self, url)`` defines a *method* that is called by calibre for every article. ``url`` is the URL of the original article. What ``print_version`` does is take that url and replace it with the new URL that points to the print version of the article. To learn about `python <https://www.python.org>`_ see the `tutorial <https://docs.python.org/2/tutorial/>`_.
|
||||
In the above, ``def print_version(self, url)`` defines a *method* that is called by calibre for every article. ``url`` is the URL of the original article. What ``print_version`` does is take that url and replace it with the new URL that points to the print version of the article. To learn about `Python <https://www.python.org>`_ see the `tutorial <https://docs.python.org/2/tutorial/>`_.
|
||||
|
||||
Now, click the :guilabel:`Add/update recipe` button and your changes will be saved. Re-download the e-book. You should have a much improved e-book. One of the problems with the new version is that the fonts on the print version webpage are too small. This is automatically fixed when converting to an e-book, but even after the fixing process, the font size of the menus and navigation bar to become too large relative to the article text. To fix this, we will do some more customization, in the next section.
|
||||
|
||||
@ -287,7 +287,7 @@ The final new feature is the :meth:`calibre.web.feeds.news.BasicNewsRecipe.prepr
|
||||
Tips for developing new recipes
|
||||
---------------------------------
|
||||
|
||||
The best way to develop new recipes is to use the command line interface. Create the recipe using your favorite python editor and save it to a file say :file:`myrecipe.recipe`. The `.recipe` extension is required. You can download content using this recipe with the command::
|
||||
The best way to develop new recipes is to use the command line interface. Create the recipe using your favorite Python editor and save it to a file say :file:`myrecipe.recipe`. The `.recipe` extension is required. You can download content using this recipe with the command::
|
||||
|
||||
ebook-convert myrecipe.recipe .epub --test -vv --debug-pipeline debug
|
||||
|
||||
|
@ -84,7 +84,7 @@ If you want only the first two letters of the data, use::
|
||||
|
||||
{author_sort:.2} - Only the first two letter of the author sort name
|
||||
|
||||
The calibre template language comes from python and for more details on the syntax of these advanced formatting operations, look at the `Python documentation <https://docs.python.org/2/library/string.html#format-string-syntax>`_.
|
||||
The calibre template language comes from Python and for more details on the syntax of these advanced formatting operations, look at the `Python documentation <https://docs.python.org/2/library/string.html#format-string-syntax>`_.
|
||||
|
||||
Advanced features
|
||||
------------------
|
||||
@ -121,7 +121,7 @@ The functions available are listed below. Note that the definitive documentation
|
||||
* ``capitalize()`` -- return the value with the first letter upper case and the rest lower case.
|
||||
* ``contains(pattern, text if match, text if not match)`` -- checks if field contains matches for the regular expression `pattern`. Returns `text if match` if matches are found, otherwise it returns `text if no match`.
|
||||
* ``count(separator)`` -- interprets the value as a list of items separated by `separator`, returning the number of items in the list. Most lists use a comma as the separator, but authors uses an ampersand. Examples: `{tags:count(,)}`, `{authors:count(&)}`
|
||||
* ``format_number(template)`` -- interprets the value as a number and format that number using a python formatting template such as "{0:5.2f}" or "{0:,d}" or "${0:5,.2f}". The field_name part of the template must be a 0 (zero) (the "{0:" in the above examples). See the template language and python documentation for more examples. Returns the empty string if formatting fails.
|
||||
* ``format_number(template)`` -- interprets the value as a number and format that number using a Python formatting template such as "{0:5.2f}" or "{0:,d}" or "${0:5,.2f}". The field_name part of the template must be a 0 (zero) (the "{0:" in the above examples). See the template language and Python documentation for more examples. Returns the empty string if formatting fails.
|
||||
* ``human_readable()`` -- expects the value to be a number and returns a string representing that number in KB, MB, GB, etc.
|
||||
* ``ifempty(text)`` -- if the field is not empty, return the value of the field. Otherwise return `text`.
|
||||
* ``in_list(separator, pattern, found_val, not_found_val)`` -- interpret the field as a list of items separated by `separator`, comparing the `pattern` against each value in the list. If the pattern matches a value, return `found_val`, otherwise return `not_found_val`.
|
||||
@ -129,7 +129,7 @@ The functions available are listed below. Note that the definitive documentation
|
||||
* ``language_strings(lang_codes, localize)`` -- return the strings for the language codes passed in `lang_codes`. If `localize` is zero, return the strings in English. If localize is not zero, return the strings in the language of the current locale. `Lang_codes` is a comma-separated list.
|
||||
* ``list_item(index, separator)`` -- interpret the field as a list of items separated by `separator`, returning the `index`th item. The first item is number zero. The last item can be returned using `list_item(-1,separator)`. If the item is not in the list, then the empty value is returned. The separator has the same meaning as in the `count` function.
|
||||
* ``lookup(pattern, field, pattern, field, ..., else_field)`` -- like switch, except the arguments are field (metadata) names, not text. The value of the appropriate field will be fetched and used. Note that because composite columns are fields, you can use this function in one composite field to use the value of some other composite field. This is extremely useful when constructing variable save paths (more later).
|
||||
* ``re(pattern, replacement)`` -- return the field after applying the regular expression. All instances of `pattern` are replaced with `replacement`. As in all of calibre, these are python-compatible regular expressions.
|
||||
* ``re(pattern, replacement)`` -- return the field after applying the regular expression. All instances of `pattern` are replaced with `replacement`. As in all of calibre, these are Python-compatible regular expressions.
|
||||
* ``select(key)`` -- interpret the field as a comma-separated list of items, with the items being of the form "id:value". Find the pair with the id equal to key, and return the corresponding value. This function is particularly useful for extracting a value such as an isbn from the set of identifiers for a book.
|
||||
* ``shorten(left chars, middle text, right chars)`` -- Return a shortened version of the field, consisting of `left chars` characters from the beginning of the field, followed by `middle text`, followed by `right chars` characters from the end of the string. `Left chars` and `right chars` must be integers. For example, assume the title of the book is `Ancient English Laws in the Times of Ivanhoe`, and you want it to fit in a space of at most 15 characters. If you use ``{title:shorten(9,-,5)}``, the result will be `Ancient E-nhoe`. If the field's length is less than ``left chars`` + ``right chars`` + the length of ``middle text``, then the field will be used intact. For example, the title `The Dome` would not be changed.
|
||||
* ``str_in_list(val, separator, string, found_val, not_found_val)`` -- treat val as a list of items separated by separator, comparing the string against each value in the list. If the string matches a value, return found_val, otherwise return not_found_val. If the string contains separators, then it is also treated as a list and each value is checked.
|
||||
@ -441,7 +441,7 @@ It would be possible to do the above with no custom columns by putting the progr
|
||||
User-defined template functions
|
||||
-------------------------------
|
||||
|
||||
You can add your own functions to the template processor. Such functions are written in python, and 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.
|
||||
You can add your own functions to the template processor. Such functions are written in Python, and 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.
|
||||
|
||||
Special notes for save/send templates
|
||||
-------------------------------------
|
||||
|
@ -25,7 +25,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
|
||||
help_text = _('''
|
||||
<p>Here you can add and remove functions used in template processing. A
|
||||
template function is written in python. It takes information from the
|
||||
template function is written in Python. It takes information from the
|
||||
book, processes it in some way, then returns a string result. Functions
|
||||
defined here are usable in templates in the same way that builtin
|
||||
functions are usable. The function must be named <b>evaluate</b>, and
|
||||
@ -233,4 +233,3 @@ if __name__ == '__main__':
|
||||
from PyQt5.Qt import QApplication
|
||||
app = QApplication([])
|
||||
test_widget('Advanced', 'TemplateFunctions')
|
||||
|
||||
|
@ -141,6 +141,7 @@ def builtin_functions():
|
||||
if name.startswith('replace_') and callable(obj) and hasattr(obj, 'imports'):
|
||||
yield obj
|
||||
|
||||
|
||||
_functions = None
|
||||
|
||||
|
||||
@ -175,6 +176,7 @@ def remove_function(name, gui_parent=None):
|
||||
refresh_boxes()
|
||||
return True
|
||||
|
||||
|
||||
boxes = []
|
||||
|
||||
|
||||
@ -269,11 +271,11 @@ class FunctionEditor(Dialog):
|
||||
try:
|
||||
mod = compile_code(source, self.func_name)
|
||||
except Exception as err:
|
||||
return error_dialog(self, _('Invalid python code'), _(
|
||||
'The code you created is not valid python code, with error: %s') % err, show=True)
|
||||
return error_dialog(self, _('Invalid Python code'), _(
|
||||
'The code you created is not valid Python code, with error: %s') % err, show=True)
|
||||
if not callable(mod.get('replace')):
|
||||
return error_dialog(self, _('No replace function'), _(
|
||||
'You must create a python function named replace in your code'), show=True)
|
||||
'You must create a Python function named replace in your code'), show=True)
|
||||
user_functions[self.func_name] = source
|
||||
functions(refresh=True)
|
||||
refresh_boxes()
|
||||
@ -290,6 +292,7 @@ def builtin(name, *args):
|
||||
return func
|
||||
return f
|
||||
|
||||
|
||||
EMPTY_FUNC = '''\
|
||||
def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
|
||||
return ''
|
||||
@ -365,6 +368,7 @@ def replace_swapcase_ignore_tags(match, number, file_name, metadata, dictionarie
|
||||
'''Swap the case of the matched text, ignoring the text inside tag definitions.'''
|
||||
return apply_func_to_html_text(match, swapcase)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication([])
|
||||
FunctionEditor().exec_()
|
||||
|
Loading…
x
Reference in New Issue
Block a user