Inkscape is an application used to design vector graphics. Its development started in 2003 as a fork of Sodipodi1, which it quickly replaced. It has evolved a lot over the years, and its (architectural) documentation2 has become outdated. The source code3 has grown to almost 600,000 lines of C/C++ code spread over more than 2000 files.4 The goal of this essay is to provide an architectural overview and insight into the design of Inkscape in its current state. We used tools such as Doxygen5 to analyse the source code and gprof6 to view the interaction between the software components.
The upcoming section will provide a high-level overview of Inkscape and in particular its core application. The sections thereafter will explain the components of the application in detail and discuss the quality attributes of the architecture.
The figure below shows the containers and part of the context of Inkscape. Inkscape is mostly a monolithic application. However, it does depend on the extension gallery7, which is a separately developed application, and the file system, which is provided by the operating system. The extension gallery will be discussed later. The file system allows the application to read and write files to persistent storage. This enables users to store, continue, and share their work.
The components of the application container are shown in the figure below. The Inkscape application follows the Model-View-Controller8 (MVC) design pattern. Here, the document would be the model, the extensions and tools would be the controllers, and the display would be the view. The Command Line Interface (CLI) and Graphical User Interface (GUI) together form a layer on top of this MVC structure and handle the interaction with the user. This allows the different controllers and views to be accessed through the user’s interface of choice.
The communication between components is handled via direct manipulation (e.g., method invocation), the Observer9 design pattern, and, at a lower level, signals10. Direct manipulation is used whenever an action requires a direct response or cannot be delayed. For example, tools directly manipulate the document. Observers and signals are used when two components need to be more loosely coupled, such as the extensions, or when the processing of an event that occurred may be delayed, such as when the display needs to be redrawn after a tool altered the document.
As not all components are equally important, we have chosen to focus our analysis on the tools, document, GUI, and extension components. However, for the sake of completeness we will now quickly describe the remaining components as well.
The CLI is quite self-explanatory. It allows users to manipulate documents, in bulk, from the command line.
The Display component performs the rendering of the live preview, presented using the GUI, via the Cairo graphics library. Cairo in turn uses the OpenGL API to perform the rendering operations.
The document provides a structural representation of the vector graphics being worked on and implements the undo/redo system that Inkscape uses.
In the above figure a high-level overview of the document is given. The document contains 2 main subsystems: the XML tree, and the object tree.
The XML tree in the document represents an SVG document, realised as a tree-like structure. Everything that will be stored in an SVG document will have to exist in this tree.11 Additional functionality of the XML tree is the ability to create events. Changing the XML tree by for example adding a child node, creates an event, which is saved by the document in the undo/redo stack and is propagated to any observers of the affected nodes.
The object tree provides a mapping between the XML subsystem and the display subsystem 12 and forms the structure that the tools modify. There is a one-to-one mapping between Object node and XML node instances, and the two trees are structurally identical. Upon construction of an object node, observers are set up to its corresponding XML node, which results in the object node being notified in case of an XML node event. This does not hold the other way around, and an update function must be explicitly called to update the XML tree in case the object tree is modified.
This dual representation provides a form of layering, where the XML tree deals with low-level functionality like exporting and importing, while the object tree is closer to the user; where it can be modified by the tools and update the display.
To facilitate the artist to interact and manipulate the underlying nodes, the tools component provides the mechanism for the user to interact with the document. The UI events that the user can trigger include mouse movements, keypresses, and actions (like creating or modifying objects) which activate the tools defined inside the tools component to manipulate the document. A diagram illustrating the interactions of the tools is shown below:
The way this is realised is that the tools use a set of observers to recognize a user event for a particular tool. The chosen tool can use 2Geom13, a library specifically developed for Inkscape, or other libraries to perform operations on the user selected object. There is a close coupling between 2Geom, the tools and another library called shape editor. The 2Geom library functions are used on the data members defined and exposed by the shape editor module.
Some of the tools are listed below.
The GUI is the interface most commonly used by users. It provides easy access to the different tools and extensions, shows a live preview of the document being worked on, and allows the user to interact directly with the document using their keyboard, mouse, and other peripherals (e.g., drawing tablet).
Many of Inkscape’s GUI elements are deeply intertwined with the drawing tools described in the above section. In future architecture, these tools and elements are planned to be properly separated.14
The dialog elements interact with both the GTK library and widget elements to create dialog boxes. The view tools are used to render the SVG using the Inkscape canvas object. These tools provide the base classes with minimum functionality and use signals to perform actions like redrawing or resizing with the display subsystem.15 The widgets tools provide basic user interface for user entry and often use the GTK library.16
Some of the elements are listed below.
|Units selector menu
The extension17 component provides an abstract and unified layer to add and manage extensions. The presence of extensions enables developers to add new features, such as a PDF generator, which do not fall under the core feature set of Inkscape. The process of adding new functionality through an extension allows the developer to integrate with the system without making changes to it.
The working of this component involves providing the developer a framework to easily add functionality. It uses the document as the representation entity which is used by all the installed extensions to implement their functionality. Two important subcomponents of the extension subsystem are:
- Extension execution environment18: This is the environment in which the extension runs. It creates an environment that allows an effect to execute, and it is mapped to the display component.
- Extension Implementation19: A framework for all the implementation details for extensions.
Extension base instances compose a set of generic functionality such as input, output, effect, and print, with a specific implementation providing functionality through the 2Geom library and direct manipulation of objects.
The interaction of the above-mentioned entities can be visualized as shown in the figure below.
Portability ensures that Inkscape is accessible to artists on their desired platform. Inkscape is available on GNU/Linux, Windows and macOS.20 Inkscape is written in a mixture of C and C++ and to build Inkscape across these multiple platforms CMake is used. CMake is an extensible, open-source system that has powerful features to support complex build environments.21 Using the libraries of GTK and Cairo for rendering improve portability as these are abstraction layers over the OS provided display manager. To meet specific requirements of certain platforms, some compile-time conditional definitions are made in the source code ensuring compatibility. However, platform specific issues do arise.22 To prevent this, test environments have been developed for each platform individually.23
Extensibility determines the ease with which a software can be extended with additional features.24 The primary consideration lies in providing enhancements to the software while minimizing impact to existing system functions. Inkscape implements this using the extension subsystem. For a developer, it is highly modularized to add new features without affecting the system, as described in Extension section. To a user, Inkscape provides an extension gallery7 where they can download new extensions. This environment provides a nice coupling between extension developers and the users, where developers can work on features that are actually demanded and used.
Inkscape’s architecture has changed a lot since it was forked from Sodipodi in 2003, and a large amount of refactoring has been done.25 The current system has been divided into the containers and components described, and it does well to achieve its key quality attributes of portability and extensibility. However, remnants of old architectural patterns still remain, and Inkscape incurs its fair share of technical debt, of which a more in-depth analysis will be given in the next essay.
Sodipodi announcement - https://wiki.inkscape.org/wiki/index.php?title=Announcement_to_Sodipodi ↩︎
Inkscape architectural overview - https://wiki.inkscape.org/wiki/index.php/Architectural_overview ↩︎
Model-View-Controller wiki - https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller ↩︎
Signals design pattern - https://signalslot.readthedocs.io/en/latest/pattern.html ↩︎
XML node source - https://gitlab.com/inkscape/inkscape/-/blob/master/src/xml/node.h#L54 ↩︎
Object tree wiki - https://wiki.inkscape.org/wiki/index.php?title=Object_tree ↩︎
Future Architecture - https://wiki.inkscape.org/wiki/index.php/Future_Architecture ↩︎
Extension subsystem - https://inkscape.gitlab.io/inkscape/doxygen/classInkscape_1_1Extension_1_1Extension.html ↩︎
Execution environment for extensions - https://inkscape.gitlab.io/inkscape/doxygen/classInkscape_1_1Extension_1_1ExecutionEnv.html ↩︎
Implementation details for any extension - https://inkscape.gitlab.io/inkscape/doxygen/classInkscape_1_1Extension_1_1Implementation_1_1Implementation.html ↩︎
Examples of platform specific issues - https://wiki.inkscape.org/wiki/index.php?title=Release_notes/1.0#Known_Issues ↩︎
Test sets for different platforms - https://inkscape.org/contribute/testing/#running-unit-tests ↩︎
Metrics for Sustainable Software Architecture, An Industry Perspective - https://resources.sei.cmu.edu/asset_files/Presentation/2014_017_001_88189.pdf ↩︎
Old Roadmap - https://wiki.inkscape.org/wiki/index.php?title=OldRoadmap ↩︎