diff --git a/doc/api/index.rst b/doc/api/index.rst index 6bab589..fa2a66a 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -1,9 +1,20 @@ API and Development =================== +This guide should give a short introduction in the way zathura's plugin +system works and how you can write your own plugin and let zathura use +it. + +zathura's plugin system is quite simple. At startup zathura searches +through a specified directory for shared objects and tries to load them +as plugins. Each plugin has to register itself by a name, its version, a +special function as well as its supported mimetypes to zathura. After +the registration of the plugin zathura will automatically use it to open +files with the previous defined mimetypes. That's it. + .. toctree:: - :maxdepth: 1 - - plugin-development + :maxdepth: 2 + :hidden: + plugin diff --git a/doc/api/plugin-development.rst b/doc/api/plugin-development.rst new file mode 100644 index 0000000..e771332 --- /dev/null +++ b/doc/api/plugin-development.rst @@ -0,0 +1,245 @@ +Example - A minimalistic PDF plugin +=================================== + +In this section we are going to develop a simplified version of the +`zathura-pdf-poppler <../zathura-pdf-poppler>`_ plugin. For the sake of +simplicity we are not discussing the build process of the plugin because +we would recommend you to adapt our Makefiles from existing plugins. In +addition we avoid most of the error handling that should be implemented. + +Prerequisites +~~~~~~~~~~~~~ + +In order to use the following described functions and macros you have to +include the *plugin-api.h* header file: + +:: + + #include + +This automatically loads other header files for the +*zathura\_document\_t*, *zathura\_page\_t* as well as all the other +types that are necessary automatically. + +Register the plugin +~~~~~~~~~~~~~~~~~~~ + +As previously described each plugin has to register itself to zathura so +that it can be used properly. Therefore we have introduced a macro +called *ZATHURA\_PLUGIN\_REGISTER* which expects several parameters: + +- Plugin name *The name of the plugin* +- Major version *The plugins major version* +- Minor version *The plugins minor version* +- Revision *The plugins revision* +- Open function *The open function* +- Mimetypes *A character array of supported mime types* + +In our case we are going to register our plugin "my plugin" with its +version 1.0.1, the register function *register\_functions* and the list +of supported mimetypes. + +:: + + ZATHURA_PLUGIN_REGISTER( + "plugin-tutorial", + 0, 1, 0, + register_functions, + ZATHURA_PLUGIN_MIMETYPES({ + "application/pdf" + }) + ) + +This macro will automatically generate among others a function called +*plugin\_register* which is used to register the plugin to zathura when +it has been loaded. + +Register the plugin functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In our macro we have defined that the function *register\_functions* is +used to install our functions which will implement a certain +functionality in the struct: + +:: + + void + register_functions(zathura_plugin_functions_t* functions) + { + functions->document_open = plugin_document_open; + functions->document_free = plugin_document_free; + functions->page_init = plugin_page_init; + functions->page_clear = plugin_page_clear; + functions->page_render_cairo = plugin_page_render_cairo; + } + +We are now going to give a short overview about the used functions in +the above code snippet. For a complete documentation you should checkout +the documentation of `zathura\_document\_functions\_t <../../doxygen>`_. +A document instance consists out of a *zathura\_document\_t* document +object that contains information about the document itself and a defined +number of *zathura\_page\_t* page objects. There are several functions +defined for those two types and they have to be implemented by the +plugin. For our simple plugin which will only be capable of rendering a +page we will need one function that is capable of opening the PDF +document and setting up all necessary objects for further usage and one +function which will clean up all the allocated objects afterwards. In +addition we need two of those functions for page objects as well and one +function that will actually implement the rendering process. + +Open and closing a document +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The first thing we have to do when opening a document is to initialize +all necessary objects and values that we are going to need for the +future use of the plugin. Therefore we have to implement our +*pdf\_document\_open* function: + +:: + + zathura_error_t + plugin_document_open(zathura_document_t* document) + { + /* get path and password */ + const char* path = zathura_document_get_path(document); + const char* password = zathura_document_get_password(document); + + /* create document data */ + char* uri = g_filename_to_uri(path, NULL, NULL); + PopplerDocument* poppler_document = poppler_document_new_from_file(uri, password, NULL); + g_free(uri); + + if (poppler_document == NULL) { + return ZATHURA_ERROR_UNKNOWN; + } + + /* save poppler document for further usage */ + zathura_document_set_data(document, poppler_document); + + /* get number of pages */ + unsigned int number_of_pages = poppler_document_get_n_pages(poppler_document); + zathura_document_set_number_of_pages(document, number_of_pages); + + return ZATHURA_ERROR_OK; + } + +To open the document we retrieve the *path* and the optional *password* +of the document to create an instance of *PopplerDocument* which +represents a document in the poppler library. If this fails for any +reason (e.g.: the path does not exist, the user provided the incorrect +password) we tell zathura that this function failed for an unknown +reason. If we are lucky we continue and save the created +*poppler\_document* object in the custom data field of the document so +that we can access it later on. After that we determine the number of +pages that the document contains so that zathura can initialize every +single page. + +Since we have allocated the *poppler\_document* object we have to make +sure that its resources will be freed when it is no longer needed. This +happens in our *pdf\_document\_free* function: + +:: + + zathura_error_t + plugin_document_free(zathura_document_t* document, PopplerDocument* poppler_document) + { + g_object_unref(poppler_document); + + return ZATHURA_ERROR_OK; + } + +Page initialization +~~~~~~~~~~~~~~~~~~~ + +Each page has to be initialized so that zathura knows about its +dimension. In addition this stage is used to store additional data in +the page that will be used for further use with it. Therefore we are +implementing *pdf\_page\_init* which will save the width and the height +of the page in the given structure: + +:: + + zathura_error_t + plugin_page_init(zathura_page_t* page) + { + unsigned int page_index = zathura_page_get_index(page); + zathura_document_t* document = zathura_page_get_document(page); + PopplerDocument* poppler_document = zathura_document_get_data(document); + + /* create poppler page */ + PopplerPage* poppler_page = poppler_document_get_page(poppler_document, page_index); + zathura_page_set_data(page, poppler_page); + + /* get page dimensions */ + double width, height; + poppler_page_get_size(poppler_page, &width, &height); + + zathura_page_set_width(page, width); + zathura_page_set_height(page, height); + + return ZATHURA_ERROR_OK; + } + +And we have to make sure that all requested resources are freed in the +end: + +:: + + zathura_error_t + plugin_page_clear(zathura_page_t* page, PopplerPage* poppler_page) + { + g_object_unref(poppler_page); + + return ZATHURA_ERROR_OK; + } + +Render a page +~~~~~~~~~~~~~ + +After we have setup the document and the page objects we are ready to +implement the render function which finally will be able to draw our +page on a widget so that it can be viewed with zathura. This function +has two additional parameters to the already known *zathura\_page\_t* +object: One of them is a *cairo\_t* object which will be used to render +the page, the other one is a flag called *printing* which determines if +the rendered page should be rendered for the print process of zathura. +For instance if this flag is set to true you should not render any +rectangles around links in the document because they are totally +worthless on paper: + +:: + + zathura_error_t + pdf_page_render_cairo(zathura_page_t* page, cairo_t* cairo, bool printing) + { + if (printing == false) { + poppler_page_render(poppler_page, cairo); + } else { + poppler_page_render_for_printing(poppler_page, cairo); + } + + return ZATHURA_ERROR_OK; + } + +In this case the *pdf\_page\_render\_cairo* function is very simplistic +since all the work is done by the *poppler* library. In your case you +might have to do some magic here to draw the page to the cairo object. +Make sure to check out the source code of our plugins. + +Installation of the plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As we suggested earlier the easiest way to build and install the plugin +is to duplicate the *Makefile* (as long with its *common.mk* and +*config.mk* files of one of our plugins. It already contains all +necessary targets for building, installing and debugging the plugin. + +Otherwise you could build the above plugin with the following command: + +:: + + $ gcc -std=c99 -shared -fPIC -pedantic -Wall `pkg-config --cflags --libs poppler-glib zathura` \ + -o pdf.so pdf.c + +After that you have to copy the *pdf.so* file into the directory where +zathura looks for plugins (this is by default: */usr/lib/zathura*). diff --git a/doc/api/plugin.rst b/doc/api/plugin.rst new file mode 100644 index 0000000..d854a18 --- /dev/null +++ b/doc/api/plugin.rst @@ -0,0 +1,26 @@ +Plugin system +============= + +zathura's plugin system is quite simple. At startup zathura searches +through a specified directory for shared objects and tries to load them +as plugins. Each plugin has to register itself by a name, its version, a +special function as well as its supported mimetypes to zathura. After +the registration of the plugin zathura will automatically use it to open +files with the previous defined mimetypes. That's it. + +Each plugin has to implement a basic set of functionality so that it can +be used in a meaningful way with zathura. For instance it would not make +any sense if the plugin was not able to render any page at all. On the +contrary the export of images out of the document might not be +considered as that important. + +We have predefined a certain set of functionality that a plugin can have +and that can be used by zathura if it has been implemented by the +plugin. When a plugin is loaded, zathura calls a certain function that +the plugin **must implemented** in order to work correctly. This +function gets a data structure which has to be filled with function +pointers by the plugin, which are then used by the main application. + +.. toctree:: + + plugin-development