Internals of Styles in Calligra
This page is about how styles are handled internally in Calligra. As you can read in the parent page, there are two ways that styles are handled in Calligra now:
- The old way where everything is kept inside each shape and no styling information is saved in style classes. This applies also to named styles, which is the big problem. In other words, the named styles are not kept.
- The new way where styling information is kept in style classes and can be saved back later. This is only applicable to named styles, of course, since the automatic styles are always generated while saving using the KoGenstyle class.
I also want to propose an even more improved system for styles that builds on and extends the new way. See below for details about this.
Most of the style handling are done in 3 libraries under libs/ :
/libs/odf contains classes that are relevant for handling ODF files. Fundamental classes for loading and saving are here: KoXmlReader and KoXmlWriter, KoGenStyle and KoGenStyles, KoOdfReadStore and KoOdfWriteStore.
There are also a number of pure data store classes that store specific types of medium level data like KoBorder (border information for paragraphs, pages, cells, etc), KoStroke (information about pen strokes), KoElementReference (unique identifiers to elements in a document) and so on. All these classes know how to load from an odf style or style stack and how to save its content into a KoGenStyle.
There also seems to be a number of classes that duplicate classes under libs/kotext: KoCell, KoCellStyle, KoColumn, KoColumnStyle, etc, which seem to have to do with tables. I have not yet investigated where these are used and if they in fact do duplicate the classes in libs/kotext.
The old way
In the old way there are no special classes that store the style information. Most of the style information is read into and kept inside the classes that use them. The most prominent example of this is the KoShape class in libs/flake which is the base class for all shape implementations.
KoShape has a method called loadOdfProperties() which load all or a subset of the shape's graphic properties from the style stack. This method examines the graphic-properties of the styles that are on the style stack including the one whose name is in the style-name property of the shape itself. It does this by calling a method loadStyle() which loads the style. If you want to load properties that are only relevant for a specific type of shape, then reimplement loadStyle() in that shape and call KoShape::loadStyle() at the beginning of it.
Loading of shape information is done using the loadOdf() method, which normally calls loadOdfProperties() somewhere near the top. A parameter to this method is the so called KoShapeLoadingContext which contains a number of relevant data that is used during the loading. One of them is the KoOdfStylesReader which originally comes from the KoOdfReadStore which represents the zip store of the ODF file.
The styles reader gives you the KoXmlElement which contains the XML tree for a certain style. It is then up to you to examine the properties of the style or push it on the style stack and examine that instead. This means that for every object that uses a style, the XML text of the style is reparsed. Also if the datatypes of the properties are complex, like vectors, matrixes, paths or other advanced data types, then they have to be reparsed from the string value of the property in question.
This is very inefficient but it is how most of the styling works right now.
The new way
The new way of handling styles is represented by the classes in libs/kotext/styles. These are classes that contain style information for a specific item, such as KoCharacterStyle, KoParagraphStyle, KoSectionStyle, KoTable*Style, etc. At this time (2.4) only styles that affect text are implemented in this way.
All of these classes use the StylePrivate class as their storage (Styles_p.h and .cpp). StylePrivate has a QMap<int, QVariant> as its central data structure, which maps a property ID to a QVariant. The ID corresponds to the property name and the QVariant stores a binary representation of the property value. All known property names for a specific property set of a style (like character-properties) are assigned an integer ID in the style class that represents the style in question. ID's for different types of properties overlap in values so for instance ID's for character properties are partly the same as those for paragraph properties.
There is also a KoStyleManager that stores and manages these styles during the runtime of the application. The style manager also affects style changes to the document contents when they are changed.
A drawback of the current implementation is that it is tightly tied to QTextDocument. This means that it's not at this time applicable to other types of text like rich text in spreadsheet cells or text-on-shapes or similar uses of text in Calligra.
Another drawback is that the style manager have hardcoded categories that cannot be extended by simply adding a number to an enum. For every new property set the manager should support, a number of new methods have to be implemented.