libmsvg v0.02

Programmer's guide

Last update: December 7, 2010


Abstract

libmsvg is a minimal and generic C library to read and write SVG files.

SVG stand for Scalable Vector Graphics and is a standard defined by the World Wide Web Consortium (see http://www.w3.org/Graphics/SVG/).

libmsvg concentrates on a small subset of SVG to be useful. More specifically on a subset of the SVG Tiny 1.2 specification. The subset is described in Appendix A.

Contents

  • Starting with examples
  • The MsvgElement tree
  • The MsvgElement structure
  • Reading SVG files
  • Building a RAW MsvgElement tree by program
  • Building a COOKED MsvgElement tree by program
  • Manipulating a MsvgElement tree
  • Writing SVG files
  • Appendix A, the libmsvg SVG subset

  • Starting with examples

    I think the best way to start with a programmer's guide is to show examples, so here there are.

    Example 1

    This example read a SVG file in a MsvgElement tree

    #include <stdio.h>
    #include "msvg.h"
    
    int main(int argc, char **argv)
    {
      MsvgElement *root;
    
      if (argc <2)
        return 0;
    
      root = MsvgReadSvgFile(argv[1]);
    
      if (root == NULL) {
        printf("Error opening %s\n", argv[1]);
        return 0;
      }
    
      /* Now you can process the structure. By example: */
      MsvgPrintElementTree(stdout, root, 0);
    
      return 1;
    }
    

    Example 2

    This example builds a MsvgElement tree and writes it to a file

    #include <stdio.h>
    #include "msvg.h"
    
    #define TESTFILE "msvgt2.svg"
    
    int main(int argc, char **argv)
    {
      MsvgElement *root, *son;
    
      root = MsvgNewElement(EID_SVG, NULL);
      MsvgAddAttribute(root, "version", "1.2");
      MsvgAddAttribute(root, "baseProfile", "tiny");
      MsvgAddAttribute(root, "viewBox", "0 0 400 400");
    
      son = MsvgNewElement(EID_RECT, root);
      MsvgAddAttribute(son, "x", "1");
      MsvgAddAttribute(son, "y", "1");
      MsvgAddAttribute(son, "width", "398");
      MsvgAddAttribute(son, "height", "398");
      MsvgAddAttribute(son, "stroke", "#F00");
      MsvgAddAttribute(son, "fill", "#FFF");
    
      son = MsvgNewElement(EID_RECT, root);
      MsvgAddAttribute(son, "x", "11");
      MsvgAddAttribute(son, "y", "11");
      MsvgAddAttribute(son, "width", "380");
      MsvgAddAttribute(son, "height", "380");
      MsvgAddAttribute(son, "stroke", "#0F0");
      MsvgAddAttribute(son, "fill", "none");
      MsvgPrintElementTree(stdout, root, 0);
    
      if (!MsvgWriteSvgFile(root, TESTFILE)) {
        printf("Error writing %s\n", TESTFILE);
        return 0;
      }
    
      return 1;
    }
    
    

    How to compile the example programs

    We assume here that you have the libmsvg library previously installed. If not, please read the "readme" file for installation instructions.

    These are the command lines to compile the examples for the three supported platforms:


    The MsvgElement tree

    The central structure of libmsvg is the MsvgElement tree. Every MsvgElement has a defined element id (eid), a pointer to his father, pointers to his previous and next siblings and a pointer to his first son. The root element must be a EID_SVG. In this version of libmsvg only EID_SVG and EID_G can have son elements. The other supported elements are EID_RECT, EID_CIRCLE, EID_ELLIPSE, EID_LINE, EID_POLYLINE and EID_POLYGON.

    This graph represents an example of a MsvgElement tree:

    MsvgElement tree example

    Every MsvgElement can have attributes, they can be of two types: generic or specific. The type of MsvgElement attributes in a tree is determined by a variable in the root element: root->psvgattr->tree_type. If this variable is RAW_SVGTREE all attributes are generic. Generic attributes are simple key,value pairs. If it is COOKED_SVGTREE all attributes are specific. Specific attributes are typed variables specific to each element id type.

    The RAW_SVGTREE tree type

    After a SVG file is loaded to a MsvgElement tree by the MsvgReadSvgFile function it is marked as a RAW_SVGTREE. Only the supported elements are inserted in the tree, but all the read attributes are stored like generic attributes. This stage of the tree can suffice for some programs, elements and attributes can be added, deleted or reordered and finally be written to a file using the MsvgWriteSvgFile function.

    The COOKED_SVGTREE tree type

    Using the MsvgRaw2CookedTree funtion you can convert a MsvgElement tree to the COOKED_SVGTREE type. Only the supported attributes are processed and converted to specific attributes. In this state the tree is easier to be manipulated by a program.

    The FRIED_SVGTREE tree type

    A third tree type is planned, using another planned function ( to be added to the library) you can convert a tree from COOKED_SVGTREE to FRIED_SVGTREE. In this state, all the elements are serialized, resolving each posible EID_G element, and all coordinates are truncated to integer values. This will be the ideal state for drawing a MsvgElemet tree using a graphics package.


    The MsvgElement structure

    After reading the previous section it must be easy to undestand the MsvgElement structure from the msvg.h include file:

    typedef struct _MsvgElement *MsvgElementPtr;
    
    typedef struct _MsvgElement {
      enum EID eid;
      MsvgElementPtr father; /* pointer to father element */
      MsvgElementPtr psibling; /* pointer to previous sibling element */
      MsvgElementPtr nsibling; /* pointer to next sibling element */
      MsvgElementPtr fson; /* pointer to first son element */
      MsvgAttributePtr fattr; /* pointer to first generic attribute */
      union { /* specific attributes */
        MsvgSvgAttributes *psvgattr;
        MsvgGAttributes *pgattr;
        MsvgRectAttributes *prectattr;
        MsvgCircleAttributes *pcircleattr;
        MsvgEllipseAttributes *pellipseattr;
        MsvgLineAttributes *plineattr;
        MsvgPolylineAttributes *ppolylineattr;
        MsvgPolygonAttributes *ppolygonattr;
      };
    } MsvgElement;
    

    Generic attributes are stored in a simple linked list of MsvgAttribute variables:

    typedef struct _MsvgAttribute *MsvgAttributePtr;
    
    typedef struct _MsvgAttribute {
      char *key; /* key attribute */
      char *value; /* value attribute */
      MsvgAttributePtr nattr; /* pointer to next attribute */
    } MsvgAttribute;
    

    Specific attributes are different for each element id and stored in a union, you can inspect each one in the msvg.h include file.


    Reading SVG files

    Using the MsvgReadSvgFile function you load a SVG file in a MsvgElement tree.

      MsvgElement *root;
      root = MsvgReadSvgFile("filein.svg");
    

    If the file doesn't exists or it isn't a valid SVG file root will be NULL, so you must check it. Only the supported elements are stored in the tree, the not supported ones are silently ignored. The tree will be a RAW tree, with all the element attributes stored as generic attributes. If you want a COOKED tree you can use the MsvgRaw2CookedTree funtion:

      int result;
      result = MsvgRaw2CookedTree(root);
    

    result will be true if all was right, false otherwise


    Building a RAW MsvgElement tree by program

    Using only two function we can cosntruct a MsvgElement tree by program. The MsvgNewElement function takes two parameters: the element id and the father element, that can be NULL. It returns the pointer to the constructed element.

    So we begin constructing the SVG element passing NULL in the father parameter, because it is the root element. By default the tree will be RAW_SVGTREE.

      MsvgElement *root;
      root = MsvgNewElement(EID_SVG, NULL);
    

    Now, we add an attribute to set the drawing limits using the MsvgAddAttribute function.

      MsvgAddAttribute(root, "viewBox", "0 0 400 400");
    

    We continue adding two son elements, a RECT element and a G element.

      MsvgElement *son;
      son = MsvgNewElement(EID_RECT, root);
      MsvgAddAttribute(son, "x", "1");
      MsvgAddAttribute(son, "y", "1");
      MsvgAddAttribute(son, "width", "398");
      MsvgAddAttribute(son, "height", "398");
      MsvgAddAttribute(son, "stroke", "#F00");
      MsvgAddAttribute(son, "fill", "#FFF");
      son = MsvgNewElement(EID_G, root);
      MsvgAddAttribute(son, "stroke", "#0F0");
      MsvgAddAttribute(son, "fill", "none");
      MsvgPrintElementTree(stdout, root, 0);
    

    Finally we add two son CIRCLE elements to the G element. Note that they inherit the stroke and fill attributes.

      MsvgElement *soon2;
      son2 = MsvgNewElement(EID_CIRCLE, son);
      MsvgAddAttribute(son2, "cx", "100");
      MsvgAddAttribute(son2, "cy", "200");
      MsvgAddAttribute(son2, "r", "80");
      son2 = MsvgNewElement(EID_CIRCLE, son);
      MsvgAddAttribute(son2, "cx", "300");
      MsvgAddAttribute(son2, "cy", "200");
      MsvgAddAttribute(son2, "r", "80");
    

    We have now our MsvgElement tree and we can manipulate it or write it to a file.


    Building a COOKED MsvgElement tree by program

    Constructing a COOKED tree is the same like constructing a RAW one, except we don't use the MsvgAddAttribute function. Instead we set directly the element variables, there are no functions to hide the variables, because we are programmers and we know what are we doing, doesn't it.

      MsvgElement *root;
      root = MsvgNewElement(EID_SVG, NULL);
      root->psvgattr->vb_min_x = 0;
      root->psvgattr->vb_min_y = 0;
      root->psvgattr->vb_width = 400;
      root->psvgattr->vb_height = 400;
      root->psvgattr->tree_type = COOKED_SVGTREE;
    
      MsvgElement *son;
      son = MsvgNewElement(EID_RECT, root);
      son->prectattr->x = 1;
      son->prectattr->y = 1;
      son->prectattr->width = 398;
      son->prectattr->height = 398;
      son->prectattr->fill_color = 0XFFFFFF;
      son->prectattr->stroke_color = 0XFF0000;
      son = MsvgNewElement(EID_G, root);
      son->gattr->fill_color = NO_COLOR;
      son->gattr->stroke_color = 0X00FF00;
    
      MsvgElement *soon2;
      son2 = MsvgNewElement(EID_CIRCLE, son);
      son2->pcircleattr->cx = 100;
      son2->pcircleattr->cy = 200;
      son2->pcircleattr->r = 80;
      son2 = MsvgNewElement(EID_CIRCLE, son);
      son2->pcircleattr->cx = 300;
      son2->pcircleattr->cy = 200;
      son2->pcircleattr->r = 80;
    

    Manipulating a MsvgElement tree

    There are some functions to manipulate a MsvgElement tree.

    void MsvgPruneElement(MsvgElement *el);
    

    The MsvgPruneElement function prune an element from his tree and them we can insert it in another tree or in another point of the same tree. Note that if the pruned element has sons, it retains them after pruned.

    void MsvgDeleteElement(MsvgElement *el);
    

    The MsvgDeleteElement does two things, prunes the element from his tree and deletes it, freeing the allocated memory used. Note that if the deleted element has sons, they are deleted too.

    int MsvgInsertSonElement(MsvgElement *el, MsvgElement *father);
    int MsvgInsertPSiblingElement(MsvgElement *el, MsvgElement *sibling);
    int MsvgInsertNSiblingElement(MsvgElement *el, MsvgElement *sibling);
    

    This three functios insert an element (that can be a subtree if it has dons) in the desired point of a tree. The MsvgInsertSonElement fucntion inserts the element like the last son of the indicated father. The MsvgInsertPSiblingElement function inserts the element like a previous sibling to the indicated sibling. And the MsvgInsertNSiblingElement function inserts the element like a next sibling. The three functions return 1 if all was ok, 0 otherwise.


    Writing SVG files

    Using the MsvgWriteSvgFile function you can write a MsvgElement tree to a file.

    int MsvgWriteSvgFile(root, "fileout.svg")
    

    MsvgWriteSvgFile only know how to write RAW trees. If you want to write a COOKED tree you need to use the MsvgCooked2RawTree funtion (to be added to the library) first:

      int result;
      result = MsvgCooked2RawTree(root);
    

    Appendix A, the libmsvg SVG subset

    This table lists the SVG elementes and attributes that will be supported when libmsvg reaches version 0.1.

    Each row lists an element, the supported specific attributes and the possible elements that may be sons.

    Note that specific attributes only affects to COOKED trees, RAW trees can have any attribute.

    A status mark is added to each attribute: (ok) means it is already supported, (TO DO) means... you know: to do.

    Element

    Specific supported attributes (status)

    Possible son elements

    <svg>

    viewbox="min-x min-y width height" (ok)

    width="width" (ok)

    height="height" (ok)

    viewport-fill="color" (ok)

    viewport-fill-opacity="n" (ok)

    version="valor" (TO DO)

    baseprofile="valor" (TO DO)

    preserveaspectratio="valor" (TO DO)

    <g>, <rect>, <circle>, <ellipse>, <line>, <polyline>, <polygon>

    <g>

    id="valor" (ok)

    transform="translate(x,y)" (TO DO)

    transform="rotate(angulo)" (TO DO)

    transform="scale(factor)" (TO DO)

    fill="color" (ok)

    fill-opacity="n" (ok)

    stroke="color" (ok)

    stroke-width="n" (ok)

    stroke-opacity="n" (ok)

    <g>, <rect>, <circle>, <ellipse>, <line>, <polyline>, <polygon>

    <rect>

    x="n" (ok)

    y="n" (ok)

    width="n" (ok)

    height="n" (ok)

    rx="n" (ok)

    ry="n" (ok)

    id="valor" (ok)

    transform="translate(x,y)" (TO DO)

    transform="rotate(angulo)" (TO DO)

    transform="scale(factor)" (TO DO)

    fill="color" (ok)

    fill-opacity="n" (ok)

    stroke="color" (ok)

    stroke-width="n" (ok)

    stroke-opacity="n" (ok)


    <circle>

    cx="n" (ok)

    cy="n" (ok)

    r="n" (ok)

    id="valor" (ok)

    transform="translate(x,y)" (TO DO)

    transform="rotate(angulo)" (TO DO)

    transform="scale(factor)" (TO DO)

    fill="color" (ok)

    fill-opacity="n" (ok)

    stroke="color" (ok)

    stroke-width="n" (ok)

    stroke-opacity="n" (ok)


    <ellipse>

    cx="n" (ok)

    cy="n" (ok)

    rx="n" (ok)

    ry="n" (ok)

    id="valor" (ok)

    transform="translate(x,y)" (TO DO)

    transform="rotate(angulo)" (TO DO)

    transform="scale(factor)" (TO DO)

    fill="color" (ok)

    fill-opacity="n" (ok)

    stroke="color" (ok)

    stroke-width="n" (ok)

    stroke-opacity="n" (ok)


    <line>

    x1="n" (ok)

    y1="n" (ok)

    x2="n" (ok)

    y2="n" (ok)

    id="valor" (ok)

    transform="translate(x,y)" (TO DO)

    transform="rotate(angulo)" (TO DO)

    transform="scale(factor)" (TO DO)

    stroke="color" (ok)

    stroke-width="n" (ok)

    stroke-opacity="n" (ok)


    <polyline>

    points="data" (ok)

    id="valor" (ok)

    transform="translate(x,y)" (TO DO)

    transform="rotate(angulo)" (TO DO)

    transform="scale(factor)" (TO DO)

    stroke="color" (ok)

    stroke-width="n" (ok)

    stroke-opacity="n" (ok)


    <polygon>

    points="data" (ok)

    id="valor" (ok)

    transform="translate(x,y)" (TO DO)

    transform="rotate(angulo)" (TO DO)

    transform="scale(factor)" (TO DO)

    fill="color" (ok)

    fill-opacity="n" (ok)

    stroke="color" (ok)

    stroke-width="n" (ok)

    stroke-opacity="n" (ok)