diff --git a/docs/api.rst b/docs/api.rst index c38bb400..b724b116 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -105,8 +105,8 @@ Reference :members: :undoc-members: -.. autoclass:: ocrmypdf.ExitCode +.. autofunction:: ocrmypdf.configure_logging + +.. automodule:: ocrmypdf.exceptions :members: :undoc-members: - -.. autofunction:: ocrmypdf.configure_logging diff --git a/docs/index.rst b/docs/index.rst index e28f3234..8e9cd991 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,6 +34,7 @@ image processing and OCR to existing PDFs. :maxdepth: 2 api + plugins contributing Indices and tables diff --git a/docs/plugins.rst b/docs/plugins.rst index e22cea36..f2ac0b94 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -2,23 +2,79 @@ Plugins ======= -You can use plugins to customize the behavior of OCRmyPDF at certain -points of interest. +You can use plugins to customize the behavior of OCRmyPDF at certain points of +interest. -Currently, it is possible to: - override the decision for whether or not -to perform OCR on a particular file - modify the image is about to be -sent for OCR +Currently, it is possible to: + +- add new command line arguments +- override the decision for whether or not to perform OCR on a particular file +- modify the image is about to be sent for OCR +- modify the page image before it is converted to PDF + +OCRmyPDF plugins are based on the Python ``pluggy`` package and conform to its +conventions. Note that: plugins installed with as setuptools entrypoints are +not checked currently, because OCRmyPDF assumes you may not want to enable +plugins for all files. Also, plugins must be functions, not classes. How plugins are imported ======================== -Plugins are imported on demand, by the OCRmyPDF worker process that -needs to use them. As such, plugins cannot share state with each other, -and will be imported many times, once for each worker process. +Plugins are imported on demand, by the OCRmyPDF worker process that needs to use +them. As such, plugins cannot share state with other plugins, cannot rely on +their module's or the interpreter's global state, and should expect asynchronous +copies of themselves to be running. Plugins can write intermediate files to the +folder specified in ``options.work_folder``. -Plugins currently cannot override the same hook. +Plugins should work whether executed in threads or processes. -How plugins are invoked -======================= +Script plugins +============== -Plugins may be called from the command line: +Script plugins may be called from the command line, by specifying the name of a file. + +.. code-block:: bash + + ocrmypdf --plugin example_plugin.py input.pdf output.pdf + +Multiple plugins may be called by issuing the ``--plugin`` argument multiple times. + +Packaged plugins +================ + +Installed plugins may be installed into the same virtual environment as OCRmyPDF +is installed into. They may be invoked using Python standard module naming. + +.. code-block:: bash + + ocrmypdf --plugin ocrmypdf_fancypants.pockets.contents input.pdf output.pdf + +OCRmyPDF does not automatically import plugins, because the assumption is that +plugins affect different files differently and you may not want them activated +all the time. The command line or ``ocrmypdf.ocr(plugin='...')`` must call +for them. + +Third parties that wish to distribute packages for ocrmypdf should package them +as packaged plugins, and these modules should begin with the name ``ocrmypdf_`` +similar to ``pytest`` packages such as ``pytest-cov`` (the package) and +``pytest_cov`` (the module). + +Plugin hooks +============ + +A plugin may provide the following hooks. Hooks should be decorated with +``ocrmypdf.hookimpl``, for example: + +.. code-block:: python + + from ocrmpydf import hookimpl + + @hookimpl + def prepare(options): + pass + +The following is a complete list of hooks that may be installed and when +they are called. + +.. automodule:: ocrmypdf.pluginspec + :members: diff --git a/src/ocrmypdf/pluginspec.py b/src/ocrmypdf/pluginspec.py index 3a9620c4..6063ce54 100644 --- a/src/ocrmypdf/pluginspec.py +++ b/src/ocrmypdf/pluginspec.py @@ -40,26 +40,27 @@ def add_options(parser: ArgumentParser) -> None: def prepare(options: Namespace) -> None: """Called to notify a plugin that a file will be processed. - The plugin may modify the options. All objects that are in options must + The plugin may modify the *options*. All objects that are in options must be picklable so they can be marshalled to child worker processes. """ @hookspec def validate(pdfinfo: 'PdfInfo', options: Namespace) -> None: - """Called to give a plugin an opportunity to review options and pdfinfo. + """Called to give a plugin an opportunity to review *options* and *pdfinfo*. - options contains the "work order" to process a particular file. pdfinfo + *options* contains the "work order" to process a particular file. *pdfinfo* contains information about the input file obtained after loading and - parsing. The plugin may modify the options. For example, you could decide + parsing. The plugin may modify the *options*. For example, you could decide that a certain type of file should be treated with ``options.force_ocr = True`` - based on information in its pdfinfo. + based on information in its *pdfinfo*. - The plugin may raise InputFileError or any ExitCodeException to request + The plugin may raise :class:`ocrmypdf.exceptions.InputFileError` or any + :class:`ocrmypdf.exceptions.ExitCodeException` to request normal termination. ocrmypdf will hold the plugin responsible for raising exceptions of any other type. - The return value is ignored. To abort processing, raise an ExitCodeException. + The return value is ignored. To abort processing, raise an ``ExitCodeException``. """