.. imprint: a program for creating documents from data and content templates

.. Copyright (C) 2019  Joseph R. Fox-Rabinovitz <jfoxrabinovitz at gmail dot com>

.. This program is free software: you can redistribute it and/or modify
.. it under the terms of the GNU Affero General Public License as
.. published by the Free Software Foundation, either version 3 of the
.. License, or (at your option) any later version.

.. This program is distributed in the hope that it will be useful,
.. but WITHOUT ANY WARRANTY; without even the implied warranty of
.. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
.. GNU Affero General Public License for more details.

.. Author: Joseph Fox-Rabinovitz <jfoxrabinovitz at gmail dot com>
.. Version: 13 Apr 2019: Initial Coding


.. _plugins:

==========
Plugin API
==========

All complex custom content in Imprint is generated by the
:ref:`plugins <introduction-layers-plugins>`. Plugins are implemented by
special configurable :py:func:`callable` objects called **handlers** that
follow a specific interface, which allows them to be referenced by the
appropriate tags in the :ref:`configuration-xml`.

Three types of content are supported out of the box: :ref:`plugins-figures`,
:ref:`plugins-tables` and :ref:`plugins-strings`. Each type of plugin accepts a
mapping of keywords from the :ref:`configuration-ipc` (and the
:ref:`xml-spec-tags-expr` tags in the :ref:`configuration-xml`), and a
dictionary of data configuration values from the :ref:`configuration-idc` that
defines the behavior of the plugin. Beyond that, each type of handler has a
different interface.

In fact, any custom :py:class:`~imprint.core.tags.TagDescriptor` may define its
own plugin interface. What makes a tag pluggable is its reliance on a function
that accepts a data configuration. This technically makes the plugin API an
implementation of a very distinct part of the :doc:`tag_api`.


.. _plugins-toc:

.. contents:: Contents
   :depth: 2
   :local:


.. _plugins-data-configuration:

------------------
Data Configuration
------------------

The data configuration is the second argument to every handler. The data
configuration is a mapping set for every plugin in the
:ref:`configuration-idc`. The name of the configuration dictionary is in the
:ref:`id attribute <configuration-idc-names>` of the corresponding
:ref:`xml-spec-tags-figure`, :ref:`xml-spec-tags-table` or
:ref:`xml-spec-tags-string` placeholder tag in the :ref:`configuration-xml`.
Custom tags may be registered as configurable plugins by setting the
:py:attr:`~imprint.core.tags.TagDescriptor.data_config` attribute of their
:py:class:`~imprint.core.tags.TagDescriptor`.

Data configuration values can contain any type of values, as long as they are
meaningful to the plugin. Plugins may require some keys to be present in the
configuration, and should raise a :py:exc:`~imprint.core.KnownError`, optionally
caused by a :py:exc:`KeyError` in response to missing keys. Most plugins will
require some sort of data source, such as a file name, but again, this is not
required.

Some values are special, in that they can override XML attibutes used by the
:py:class:`~imprint.core.tags.TagDescriptor`. In particular, the ``handler``
attribute can be overridden by a key with the similar name in the
:ref:`configuration-idc`. Overridable values are noted for each
:ref:`builtin tag <xml-spec-tags>` in the :doc:`xml_spec`.


.. _plugins-handlers:

--------
Handlers
--------

Handlers are named by the ``handler`` attribute of the corresponding
:ref:`xml-spec-tags-figure`, :ref:`xml-spec-tags-table` or
:ref:`xml-spec-tags-string` placeholder tag in the :ref:`configuration-xml`.
The exact class name (including package) is searched for the handler. If not
found, a prefix of :mod:`imprint.handlers` is prepended to the nominal package
name.

The handler can be overridden in the :ref:`plugins-data-configuration`
dictionary. Normally, all configuration keys are interpreted directly by the
handler. However, a special ``handler`` key will processed before a handler is
found, and can override the setting in the XML. This mechanism is provided by
each of the :ref:`tag-api-descriptors` for :ref:`plugins-figures`,
:ref:`plugins-tables` and :ref:`plugins-strings`. It allows for more flexible
debugging, and modification of existing templates. New
:ref:`tag-api-descriptors` can use the :py:func:`~imprint.core.tags.get_handler`
function to implement the same functionality, although it is not stritcly
required.


.. _plugins-figures:

-------
Figures
-------

Some built-in figure handler examples can be found in
:mod:`imprint.handlers.figure`.


.. _plugins-figures-tag:

XML Tag
=======

:ref:`xml-spec-tags-figure`


.. _plugins-figures-signature:

Handler Signature
=================

.. py:function:: handler(config, kwds[, output])

   Generate an image based on the :ref:`plugins-data-configuration`. If an
   output is specified, it will be a string or file-like. A string indicates an
   output file name, which the handler may modify and return. A file-like can
   be assumed to be open for binary writing, with random access enabled. It
   should be rewound before being returned.

   :param dict config: The :ref:`plugins-data-configuration` for the figure.
   :param dict kwds: The :ref:`keyword <keywords>` dictionary for the figure.
   :param output: The name of the output file, or the output file to save the
       figure to. If omitted, the output must go to an in-memory file-like
       object like :py:class:`io.BytesIO`. The handler may determine the output
       format based on the file extension, but this is not required. Each
       handler should have a default format for extensionless files and omitted
       output.
   :type output: str or file-like or None
   :return: Either the actual output file name, or an in-memory file-like
       object, rewound to the beginning, containing the image. A string output
       will not necessarily be the input file name. It may, for example, have
       an extension appended to it. A :py:obj:`None` return value indicates an
       internal non-fatal error.
   :rtype: str or file-like


.. _plugins-tables:

------
Tables
------

Some built-in table handler examples can be found in
:mod:`imprint.handlers.table`.


.. _plugins-tables-tag:

XML Tag
=======

:ref:`xml-spec-tags-table`


.. _plugins-tables-signature:

Handler Signature
=================

.. py:function:: handler(config, kwds, doc, style, *, image_log_name=None)

   Generate a table based on the :ref:`plugins-data-configuration`. The handler
   is responsible for generating a table of the correct size and styling it
   properly based on the ``style`` parameter.

   This type of plugin is expected to have no return value.

   :param dict config: The :ref:`plugins-data-configuration` for the table.
   :param dict kwds: The :ref:`keyword <keywords>` dictionary for the table.
   :param docx.document.Document doc: The document to insert the table into.
       The handler is responsible for invoking the
       :py:meth:`~docx.document.Document.add_table` method.
   :param str style:
       The name of the style to apply to the generated object.
   :param image_log_name:
       The name of the image log to use if table data is to be logged. If
       :ref:`keywords-system-log_images` is off, this will be :py:obj:`None`.
       May be completely ignored by the handler if impractical or inappropriate
       to implement. The file name, if supplied, is provided without any
       extension.
   :type image_log_name: str, Path-like or None


.. _plugins-strings:

-------
Strings
-------

Some built-in string handler examples can be found in
:mod:`imprint.handlers.string`.


.. _plugins-strings-tag:

XML Tag
=======

:ref:`xml-spec-tags-string`


.. _plugins-strings-signature:

Handler Signature
=================

.. py:function:: handler(config, kwds)

   Generate a string based on :ref:`plugins-data-configuration`.

   :param dict config: The :ref:`plugins-data-configuration` for the string.
   :param dict kwds: The :ref:`keyword <keywords>` dictionary for the string.
   :return: The newly created string. A :py:obj:`None` return value indicates
       an internal non-fatal error.
   :rtype: :py:class:`str`


.. _plugins-errors:

------
Errors
------

Since plugins implement a subset of the tag-processing functionality, the same
rules apply to plugin errors at to generic tag errors. See
:ref:`tag-api-descriptor-errors` in the :doc:`tag_api` section.


.. _plugins-builtin:

---------------
Builtin Plugins
---------------

.. automodule:: imprint.handlers


.. _plugins-builtin-figures:

Figures
=======

.. automodule:: imprint.handlers.figure

.. autofunction:: ImageFile


.. _plugins-builtin-figures-mods:

Submodules
----------

.. automodule:: imprint.handlers.figure.images


.. _plugins-builtin-tables:

Tables
======

.. automodule:: imprint.handlers.table

.. autofunction:: CSVFile

.. autofunction:: DataFrame


.. _plugins-builtin-tables-mods:

Submodules
----------

.. automodule:: imprint.handlers.table.tables


.. _plugins-builtin-strings:

Strings
=======

.. automodule:: imprint.handlers.string

.. autofunction:: TextFile


.. _plugins-builtin-string.strings:

Submodules
----------

.. automodule:: imprint.handlers.string.strings


.. _plugins-builtin-utilities:

Utilities
=========

.. automodule:: imprint.handlers.utilities
   :members:
