Anyone who has looked at a Scribus preferences file (scribus.rc) will be aware that the sheer amount of settings is daunting. There’s a C++ file named prefsstructs.h that defines on 470 lines of code 29 data structures too hold various settings. And these are just dumb structs, without any code to read/write to XML (or even getters/setters or default values or scripter support). So how can we clean up this mess?

Take for example this struct and its corresponding section in the scribus.rc file:

//Document Setup
struct DocumentSetupPrefs
{
    QString pageSize; //! Default page size of a document
    int pageOrientation; //! Default orientation of the page
    double pageWidth; //! Width of a page
    double pageHeight; //! Height of a page
    MarginStruct margins; //! Margins for a page
    MarginStruct bleeds; //! Bleeds for a page
    int marginPreset; //! Use a preset ratio margin setup
    int pagePositioning; //! Show pages in 1,2,3,4 pages side by side on screen
    int docUnitIndex; //! The index of the default unit
    bool AutoSave;
    int AutoSaveTime;
    bool saveCompressed;
};

and

<DocumentSetup UnitIndex="0" MarginBottom="40.000" PageHeight="841.890"
    PageSize="A4" MarginLeft="40.000" PageOrientation="0"
    PageWidth="595.276" AutoSave="1" MarginTop="40.000"
    MarginPreset="0" BleedBottom="0.000" PagePositioning="0"
    BleedRight="0.000" AutoSaveTime="600000" BleedTop="0.000"
    MarginRight="40.000" SaveCompressed="0" BleedLeft="0.000"/>

Now wouldn’t it be nice if you’d just define the structure of the settings and the C++ class would be generated automatically?

Defining structure with RelaxNG

My favorite language to define XML structures is RelaxNG compact. A RelaxNG definition for the DocumentSetup element might look like this:

element DocumentSetup {
      attribute PageSize { xsd:string },
      attribute AutoSave { xsd:boolean },
      attribute AutoSaveTime { xsd:integer },
      attribute BleedBottom { xsd:decimal },
      attribute BleedLeft { xsd:decimal },
      attribute BleedRight { xsd:decimal },
      attribute BleedTop { xsd:decimal },
      attribute MarginBottom { xsd:decimal },
      attribute MarginLeft { xsd:decimal },
      attribute MarginPreset { xsd:integer },
      attribute MarginRight { xsd:decimal },
      attribute MarginTop { xsd:decimal },
      attribute PageHeight { xsd:decimal },
      attribute PageOrientation { xsd:integer },
      attribute PagePositioning { xsd:integer },
      attribute PageWidth { xsd:decimal },
      attribute SaveCompressed { xsd:boolean },
      attribute UnitIndex { xsd:integer }
    }

This already has a similar structure to our C++ struct. Unfortunately for each of the MarginStructs ‘bleeds’ and ‘margins’ we have four XML attributes. There are several ways to model this with RelaxNG. I choose to represent the MarginStructs with XML elements:

element DocumentSetup {
      attribute PageSize { xsd:string },
      attribute AutoSave { xsd:boolean },
      attribute AutoSaveTime { xsd:integer },
      attribute MarginPreset { xsd:integer },
      attribute PageHeight { xsd:decimal },
      attribute PageOrientation { xsd:integer },
      attribute PagePositioning { xsd:integer },
      attribute PageWidth { xsd:decimal },
      attribute SaveCompressed { xsd:boolean },
      attribute UnitIndex { xsd:integer },
      (element Bleed {
            attribute Bottom { xsd:decimal },
            attribute Left { xsd:decimal },
            attribute Right { xsd:decimal },
            attribute Top { xsd:decimal }
      }&
      element Margin {
            attribute Bottom { xsd:decimal },
            attribute Left { xsd:decimal },
            attribute Right { xsd:decimal },
            attribute Top { xsd:decimal }
      })
    }

In RelaxNG element definitions must follow any attribute definitions. By separating the elements with ‘&’ (instead of a comma) we allow any order of those elements.

We now use a named definition for the four MarginStruct attributes:

grammar {
      MarginStructDef = (attribute Bottom { xsd:decimal },
            attribute Left { xsd:decimal },
            attribute Right { xsd:decimal },
            attribute Top { xsd:decimal }),
      DocumentSetupDef = element DocumentSetup {
            attribute PageSize { xsd:string },
            attribute AutoSave { xsd:boolean },
            attribute AutoSaveTime { xsd:integer },
            attribute MarginPreset { xsd:integer },
            attribute PageHeight { xsd:decimal },
            attribute PageOrientation { xsd:integer },
            attribute PagePositioning { xsd:integer },
            attribute PageWidth { xsd:decimal },
            attribute SaveCompressed { xsd:boolean },
            attribute UnitIndex { xsd:integer },
            (element Bleed { MarginStructDef }&
            element Margin { MarginStructDef })
      }
    }

A corresponding XML fragment might look like this:

<DocumentSetup
    AutoSave="1" AutoSaveTime="600000" SaveCompressed="0"
    PageHeight="841.890" PageWidth="595.276" MarginPreset="0"
    PageSize="A4"  PageOrientation="0" PagePositioning="0"  UnitIndex="0" >
    <Margin Left="40.000" Right="40.000" Bottom="40.000" Top="40.000" />
    <Bleed Bottom="0.000" Right="0.000" Top="0.000" Left="0.000" />
</DocumentSetup>

Annotating for automatic code generation

Starting with the RelaxNG definition a simple approach would be to create a C++ class for every element definition and a C++ member for each attribute. But let’s say we want to have more control over the code generation process. RelaxNG allows to enhance the grammar with arbitrary attributes. We’ll take advantage of that in order to define names and data types:

namespace impl = "http://www.scribus.info/RelaxNG/implementation"

grammar {
      MarginStructDef = (attribute Bottom { xsd:decimal },
            attribute Left { xsd:decimal },
            attribute Right { xsd:decimal },
            attribute Top { xsd:decimal })
      DocumentSetupDef =
            [impl:name = "DocumentSetupPrefs" impl:template = "simple-settings"]
            element DocumentSetup {
                  [impl:datatype = "QString" impl:name = "pageSize"]
                  attribute PageSize { xsd:string },
                  attribute AutoSave { xsd:boolean },
                  attribute AutoSaveTime { xsd:integer },
                  [impl:name = "marginPreset"]
                  attribute MarginPreset { xsd:integer },
                  [impl:name = "pageHeight"]
                  attribute PageHeight { xsd:decimal },
                  [impl:name = "pageOrientation"]
                  attribute PageOrientation { xsd:integer },
                  [impl:name = "pagePositioning"]
                  attribute PagePositioning { xsd:integer },
                  [impl:name = "pageWidth"]
                  attribute PageWidth { xsd:decimal },
                  [impl:name = "saveCompressed"]
                  attribute SaveCompressed { xsd:boolean },
                  [impl:name = "docUnitIndex"]
                  attribute UnitIndex { xsd:integer },
                  (
                  [impl:datatype = "MarginStruct" impl:name = "bleeds"]
                  element Bleed { MarginStructDef } &
                  [impl:datatype = "MarginStruct" impl:name = "margins"]
                  element Margin { MarginStructDef })
      }
    }

Ok, now we have a RelaxNG definition that includes all information needed for defining the DocumentSetupPrefs class and how to write it to XML. So what happens next? A nice aspect of RelaxNG compact is that it has an equivalent RelaxNG XML form.

Preparing RelaxNG for XML processing

There is a pair of powerful tools for handling RelaxNG definitions: Jang and Trang. We will use Trang to convert the above RelaxNG compact grammar to RelaxNG XML:

<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns:impl="http://www.scribus.info/RelaxNG/implementation" xmlns="ht
tp://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XML
Schema-datatypes">
   <define name="MarginStructDef">
   ...
   </define>
   <define name="DocumentSetupDef">
      <element name="DocumentSetup" impl:name="DocumentSetupPrefs"
               impl:template="simple-settings">
         <attribute name="PageSize" impl:datatype="QString"
                    impl:name="pageSize">
            <data type="string"/>
         </attribute>
         <attribute name="AutoSave">
            <data type="boolean"/>
         </attribute>
         ...
         <attribute name="UnitIndex" impl:name="docUnitIndex">
            <data type="integer"/>
         </attribute>
         <interleave>
            <element name="Bleed" impl:datatype="MarginStruct"
                     impl:name="bleeds">
               <ref name="MarginStructDef"/>
            </element>
            <element name="Margin" impl:datatype="MarginStruct"
                     impl:name="margins">
               <ref name="MarginStructDef"/>
            </element>
         </interleave>
      </element>
   </define>
</grammar>

Ok, that looks like something that can be processed easily. We just have to expand <ref> elements with their definitions and then iterate over the grammar. At first I thought about using XQuery to do this, since XQuery is cool and is ideal to parse XML. But it’s not so good in producing plain text, so I’ll look into an alternative approach using Python. Stay tuned for part 2!