How to write a Scribus Plugin

How to write a Scribus Plugin

Preface

This article shows you how to write plugins for Scribus. Plugins are dynamic loaded libraries in "so" format.

Where do I find Scribus Plugins?

Your Scribus, if manually installed in the following manner:

#./configure [--prefix=/your/selected/path]
#make
#make install

Substituting /your/selected/path with the one appropriate for your system, eg. /usr, change directory to /your/selected/path/lib/Scribus/plugins and list its files. You should see a lot of *.la and *.so* files and some *.qm too. Qm files contain language translations and localisation, la and so are loadable libraries which are topic of this article.

Before you start coding

Let's suppose you've got a great idea to improve Scribus and you cannot wait to start writing some excellent C++ constructs.

Important

Wait here for a while! Maybe you are at the edge of "reinventing the wheel" - so join the mailing-list or #scribus channel at irc.freenode.net. Your topic will be discussed and you'll get support too. We cannot stop you to write anything you want - sure. :)))

Example Plugin

myplugin.h

#ifndef MYPLUGIN_H
#define MYPLUGIN_H

#include <scribus.h>

/** Calls the Plugin with the main Application window as parent
  * and the main Application Class as parameter */
extern "C" void Run(QWidget *d, ScribusApp *plug);


/** Returns the Name of the Plugin.
  * This name appears in the relevant Menu-Entries */
extern "C" QString Name();


/** Returns the Type of the Plugin.
  * 1 = the Plugin is a normal Plugin, which appears in the Extras Menu
  * 2 = the Plugin is a Import Plugin, which appears in the Import Menu
  * 3 = the Plugin is a Export Plugin, which appears in the Export Menu
  * 4 = the Plugin is a resident Plugin   */
extern "C" int Type();

#endif

Now let's describe the source code. MYPLUGIN_H declaration is the standard way to ensure only one insertion of the header file. Scribus.h is required to access Scribus objects. The function Run() is called when the plugin is loaded, ie. called from Scribus menu - depending on Type() return value.

Name() returns the string to display in menu.

In this example we're working on normal plugin (type 1). Import and Export plugins (types 2, 3) you can study by reading the code of e.g. SVG import/export plugin that is distributed in Scribus tarball and resident plugin (type 4) is e.g. Python scripting plugin aka. Scripter from the distribution.

You can use a very special type of plugin when you use number not listed in the example above - e.g. 5. In this case you have to handle the plugin integration into Scribus menus yourself. The newfromtemplate addon with its:

plug->fileMenu->insertItem(QObject::tr("New &from Template..."));

is a good example.

myplugin.cpp

#include "myplugin.h"
#include <qstring.h>

QString Name()
{
    return QObject::tr("Do Nothing Plugin");
}

int Type()
{
    return 1;
}

void Run(QWidget *d, ScribusApp *plug)
{
}

To explain the source code again. Qt qtstring.h is required as the function Name() returns a QString instance - text for the user interface. It should be prepared for translations with QObject::tr().

As said above - Type() of this plugin is 1.

Run(QWidget*, ScribusApp*) is empty while we write a plugin that does nothing. In a real plugin there has to be the code that is the plugin logic, custom class initializations, dialog calling etc.

Compile it!

Warning

Beware - a qmake project easy to use but isn't necessarily the most standard way to distribute software on the Linux platform. This process is an example only for development. When you create your bug-free functional package then save the time to prepare full featured automagic (autoconf, automake) distributon as described in the next section.

Let's compile it, but it isn't so easy as typing in gcc myplugin.cpp ;). One easy way to build it - Qt qmake (because some people really really hate autoconf and automake in their complexity). Note: you will need to create an empty config.h file before running these steps

#qmake -project

Now the project file is created and we'll make just a few changes into it.

######################################################################
# Automatically generated by qmake (1.06c) Sun Dec 14 13:32:11 2003
######################################################################

#change TEMPLATE = app. We aren't working on application just plugin
TEMPLATE = lib
INCLUDEPATH += .
#As working with Scribus, we need Scribus includes too.
INCLUDEPATH += /home/subzero/devel/Scribus/include/Scribus/
#And Scribus have to use freetype2.
#So we should link it too. Use paths returned from
##freetype-config --cflags and --libs
INCLUDEPATH += /usr/include/freetype2
LIBS += -lfreetype -lz

# Input
#create empty config.h file
HEADERS += myplugin.h config.h
SOURCES += myplugin.cpp

After these changes you're ready to compile

#qmake
#make

Running Qmake creates the Makefile and by running make you compile your plugin.

Then just copy *so* files into Scribus plugin directory and run Scribus. You'll see "Do Nothing Plugin" in the Extras menu.

It is clear that you have to use some other way to distribute your source code to others - some use autogenerated qmake pro files, other use the autoconf/automake combination.

Distribute it! (a.k.a. Compile it! 2nd edition)

Qmake is user frendly and useful for rapid development, but there is one standard way to compile/distribute software for Linux, *BSD etc. - autoconf and automake. Lets call these two programs by the automagic acronym in the following text.

To use automagic successfully you'll need chalk drawn to the north oriented pentagram on the floor (Carrefour, 2€), red-black daemonic dress (Hugo Boss, 2000€) and a sacrificed penguin on the home altar (one hour of the fear in your local Zoo). Err, only joking.. dont harm any nice little penguins, you wont need to.

Back to business...

Download the donothingplugin-1.0.tar.gz example from http://docs.scribus.net, unpack it and browse it.

When you enter the main directory you can see a lot of files and directories. Warning, do not change anything in the admin directory. It's content is TABOO for you!

As you read the automagic docs (surely) you know that there is an important file in every directory of your project called Makefile.am. Here is a short commented example:

# set where will be plugin installed
pluginsdir = $(prefix)/lib/scribus/plugins
# specify additional includes for compilation
AM_CPPFLAGS = -I$(prefix)/include/scribus
# specify directories to dive in
SUBDIRS = translation doc
# name of the plugin = library
plugins_LTLIBRARIES = libscribusvlna.la
# join all includes
INCLUDES = $(LIBFREETYPE_CFLAGS) $(all_includes)
# library related trash - symlinks etc.
libscribusvlna_la_LDFLAGS = -version-info 0:0:0
libscribusvlna_la_METASOURCES = AUTO
#
# list of the source files of your project
libscribusvlna_la_SOURCES = frame.cpp selection.cpp vlnadialog.cpp vlnapage.cpp svlna.cpp
EXTRA_DIST = frame.cpp selection.cpp svlna.cpp vlnadialog.cpp vlnapage.cpp frame.h /
		selection.h svlna.h vlnadialog.h vlnapage$
# how to compile
KDE_OPTIONS = qtonly
AM_LDFLAGS =    -s $(LIBFREETYPE_LIBS)

Shortly - if you take the donothingplugin-1.0.tar.gz file and parse the content (and rename it of course :)) of Makefile.am files you'll get a functional package.

There is one more thing to do. You have to specify directory structure in configure.in in the project root directory too:

AC_CONFIG_FILES([Makefile])
AC_CONFIG_FILES([svlna/Makefile])
AC_CONFIG_FILES([svlna/translation/Makefile])
AC_CONFIG_FILES([svlna/doc/Makefile])

And now to how it works...

First run make -f Makefile.dist in the root directory. It creates Makefile.in templates (some kind of the black magic).

And then obligatory ./configure ; make ; make install should work for you. Enjoy.

Scribus Object Model

Read doxygen documentation for Scribus-API and Scribus source code in the same time.

Some (useful) code examples

Some pieces of the code fromn the source code follows.

How to show what are you doing to the user in Scribus's statusbar:

plug->FMess->setText(QString(aText));

Item is a text frame:

PageItem *aFrame;
if (aFrame->PType==4) {}

Navigation through the pages:

for (uint i=0; iview->Pages.count(); i++) {
    plug->FMess->setText(QObject::tr("Page: ")
            + i
            + plug->view->Pages.at(i)->PageNam);
    // do something with active page
    parse(plug->view->Pages.at(i));
}

Storing your plugin's preferences:

Scribus provides a preferences api for plugin writers to store data between Scribus launches. There are two types of storage formats available: key-value pairs and tables.

First you will need to get the PrefsContext object for your plugin. Then you can query PrefsContext to get the value for a specific key or you can ask for a PrefsTable by it's name. Here is a short example using key-value pairs.

#include <prefsfile.h>
#include <prefscontext.h>

extern PrefsFile* prefsFile;

PrefsContext *myPluginPrefs = prefsFile->getPluginContext("MyPlugin");

// default value -1 will be used if "i" doesn't already exist
int i = myPluginPrefs->getInt("i");

// default value "dog" will be used if "s" doesn't already exist
QString s = myPluginPrefs->get("s", "dog");

myPluginPrefs->set("i", 221);
myPluginPrefs->set("s", "cat");

Plugin related links

Qt documentation from Trolltech
QMake documentation from Trolltech
Automake/autoconf example