pvbrowser manual
Back Content Forward

SVG graphics

A Scalable Vector Graphic is an XML specification and file format for describing two-dimensional vector graphics, both static and animated. It is an open standard created by the W3C's SVG Working Group.

<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
   "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
 <svg xmlns="http://www.w3.org/2000/svg"
      width="1000"
      height="600"
      viewBox="0 0 5 5">
    <rect id="black_stripe"
          fill="#000"
          width="5"
          height="1"/>
    <rect id="gray_i"
          fill="#444"
          width="5"
          height="1" y="1"/>
    <rect id="gray_ii"
          fill="#888"
          width="5"
          height="1" y="2"/>
    <rect id="gray_iii"
          fill="#ccc"
          width="5"
          height="1" y="3"/>
    <rect id="white"
          fill="#fff"
          width="5"
          height="1" y="4"/>
 </svg>
SVG example code

You can draw SVG graphics with specialized drawing programs like Inkscape or use a converter to convert other formats like DXF from CAD programs to SVG.

You can use these graphics within pvbrowser. Simply insert a Draw/SVG widget and use rlSvgAnimator for drawing and animating the graphic. Using this class the graphic will be send from your pvserver to the pvbrowser client. Once the graphic is loaded within the pvbrowser client rlSvgAnimator can be used to change (animate) the graphic. Your pvserver will get events when you click objects within the SVG graphic. And also you will get some mouse events.

The following code shows howto load an SVG graphic into the pvbrowser client and draw it.

//
// this is extracted from a maskX_slot.h file
//

typedef struct // (todo: define your data structure here)
{
  rlSvgAnimator svgAnimator;
  // further members ...
}
DATA;

static int drawSVG(PARAM *p, int id, DATA *d)
{
  if(d == NULL) return -1;
  gBeginDraw(p,id);
  d->svgAnimator.writeSocket();
  gEndDraw(p);
  return 0;
}

static int slotInit(PARAM *p, DATA *d)
{
  if(p == NULL || d == NULL) return -1;

  // load svg into pvbrowser client
  d->svgAnimator.setSocket(&p->s);           // register the socket
  d->svgAnimator.setId(svgExample);          // set the id of the widget
  d->svgAnimator.read("img/myExample.svg");  // load a svg file into pvbrowser client

  // set the zoom factor (negative values -> keep aspect ratio)
  pvSetZoomX(p, svgExample, -1.0f);
  pvSetZoomY(p, svgExample, -1.0f);

  // draw svg
  drawSVG(p,svgExample,d);

  return 0;
}

The following methods from rlSvgAnimator are used for animating and modifying the loaded SVG graphics.

int svgPrintf(const char *objectname, const char *tag, const char *format, ...);
int svgTextPrintf(const char *objectname, const char *format, ...);
int svgRecursivePrintf(const char *objectname, const char *tag, const char *format, ...);
int svgSearchAndReplace(const char *objectname, const char *tag, const char *before, const char *after);
                                                                 // before may be a wildcard
int svgRecursiveSearchAndReplace(const char *objectname, const char *tag, const char *before, const char *after);
                                                                 // before may be a wildcard
int show(const char *objectname, int state); // state := 0|1
int setMatrix(const char *objectname, float sx, float alpha, float x0, float y0, float cx, float cy);
int setMatrix(const char *objectname, rlSvgPosition &pos);

The objectname is used to address a graphical object in SVG ( id="objectname" ) within the graphic. Using a SVG drawing program the user can set this id as wanted.

A text can now be changed like that.

d->svgAnimator.svgTextPrintf("HelloObject", "Hello World");

The object can be hidden or shown.

d->svgAnimator.show("HelloObject", 0); // hide HelloObject
d->svgAnimator.show("HelloObject", 1); // show HelloObject

Any property of the object can be modified.

d->svgAnimator.svgPrintf("HelloObject", "fill=", "#000"); // set a property

You can set the transformation matrix of the object. (move/rotate/scale)

// sx    := scale factor
// alpha := rotation angle in radiant
// x0,y0 := position
// cx,cy := position around which to rotate
d->svgAnimator.setMatrix(const char *objectname, float sx, float alpha, float x0, float y0, float cx, float cy);
// or using
d->svgAnimator.setMatrix(const char *objectname, rlSvgPosition &pos);

rlSvgPosition is defined as follows

class rlSvgPosition
{
  public:
    rlSvgPosition();
    rlSvgPosition(float sx_init, float a_init, float x0_init, float y0_init, float cx_init, float cy_init);
    virtual ~rlSvgPosition();
    float sx, alpha, x0, y0, cx, cy;
    struct rlPositionInit {float sx, alpha, x0, y0, w, h;} init;
    void setInit(float x0_init, float y0_init, float w_init, float h_init);
    void move(float x, float y);
    void moveRelative(float dx, float dy);
    void scale(float s);
    void scaleRelative(float ds);
    void rotate(float alpha, float cx, float cy);
};

Initially rlSvgPosition is set to the identity matrix (no move, no rotation, scaling factor 1.0).

After changing all the objects within the SVG graphic as wanted you must redraw the graphic "drawSVG(p,svgExample,d);" as shown above.

When using the mouse above a SVG graphic you will get events for the following slot functions.

static int slotMouseMovedEvent(PARAM *p, int id, DATA *d, float x, float y)
{
  if(p == NULL || id == 0 || d == NULL || x < -1000 || y < -1000) return -1;
  return 0;
}

static int slotMousePressedEvent(PARAM *p, int id, DATA *d, float x, float y)
{
  if(p == NULL || id == 0 || d == NULL || x < -1000 || y < -1000) return -1;
  return 0;
}

static int slotMouseReleasedEvent(PARAM *p, int id, DATA *d, float x, float y)
{
  if(p == NULL || id == 0 || d == NULL || x < -1000 || y < -1000) return -1;
  return 0;
}

If you want to get an event when the user clicks a graphical object the id must start with "@".

Example: id="@testclick"

Then "slotTextEvent(PARAM *p, int id, DATA *d, const char *text)" will be called.

Further more you can use a name like id="@@testclick" which will instruct pvbrowser to give a visaul feedback when the user moves the mouse over that object. The mouse will change it's shape to Qt::PointingHandCursor if the user moves over that object. Thus you can include "Buttons" etc. within your SVG drawing that have a visual feedback.

slotTextEvent is not only used for these click events. There are more functions that result in such a slotTextEvent. Thus we provide some utility functions for parsing the text within slotTextEvent.

static int slotTextEvent(PARAM *p, int id, DATA *d, const char *text)
{
  float x,y,w,h;

  if(p == NULL || id == 0 || d == NULL || text == NULL) return -1;
  switch(textEventType(text))
  {
    case PLAIN_TEXT_EVENT:
      printf("plain\n");
      break;
    case SVG_LEFT_BUTTON_PRESSED:
      printf("left pressed %s\n", svgObjectName(text));
      break;
    case SVG_MIDDLE_BUTTON_PRESSED:
      printf("middle pressed %s\n", svgObjectName(text));
      break;
    case SVG_RIGHT_BUTTON_PRESSED:
      printf("right pressed %s\n", svgObjectName(text));
      break;
    case SVG_LEFT_BUTTON_RELEASED:
      printf("left released %s\n", svgObjectName(text));
      break;
    case SVG_MIDDLE_BUTTON_RELEASED:
      printf("middle released %s\n", svgObjectName(text));
      break;
    case SVG_RIGHT_BUTTON_RELEASED:
      printf("right released %s\n", svgObjectName(text));
      break;
    case SVG_BOUNDS_ON_ELEMENT:
      getSvgBoundsOnElement(text, &x, &y, &w, &h);
      printf("bounds object=%s xywh=%f,%f,%f,%f\n",svgObjectName(text),x,y,w,h);
      break;
    default:
      printf("default\n");
      break;
  }
  return 0;
}

"switch(textEventType(text))" will detect all possible events.

The "case SVG_BOUNDS_ON_ELEMENT:" is a response to a call of

pvRequestSvgBoundsOnElement(p, svgExample, "testobject");

with which you can request the bounding geometry of a graphical object within the SVG graphic.

"svgObjectName(text)" will extract the SVG objectname from the text event.

Using the method

int svgSearchAndReplace(const char *objectname, const char *tag, const char *before, const char *after);

from rlSvgAnimator you replace components of properties. This is usefull for example if you want to change parts of a style property. The "before" parameter may include wildcards like * (any characters) ? (1 unknown character).

Additionally there are methods for recursively working on a grouped graphical object.

int svgRecursivePrintf(const char *objectname, const char *tag, const char *format,...);
int svgRecursiveSearchAndReplace(const char *objectname, const char *tag, const char *before, const char *after);

The method

int read(const char *infile, rlIniFile *inifile=NULL);

which sends the SVG graphic to the browser has an optional parameter inifile. If you supply it, it will be filled with the properties from the SVG. The id's within SVG result in section names within inifile. Suggestion: You can insert your own properties like your:tag="any value" into the SVG, that are not interpreted by the Renderer. These properties can be used freely by your own application.

For zooming and panning the whole SVG graphic use these methods:

int setMainObject(const char *main_object); // set get MainObject name
const char *mainObject();
int setXY0(float x0, float y0);             // set/get origin
float x0();
float y0();
int setMouseXY0(float x0, float y0);        // set/get mouse position 0 for the MainObject
float mouseX0();
float mouseY0();
int setMouseXY1(float x1, float y1);        // set/get mouse position 1 for the MainObject
float mouseX1();
float mouseY1();
int setScale(float scale);                  // set/get the scaling factor for the MainObject
float scale();
int zoomCenter(float newScale);             // zooms the whole SVG graphic keeping it centered to the viewport
int zoomRect();                             // zooms the whole SVG graphic so that the visible section is from x0,x0 to x1,y1
int setMainObjectMatrix();                  // sets the MainObject matrix according to scale,x0,y0
int setWindowSize(int width, int height);   // call this method when the widget is resized
float windowWidth();
float windowHeight();
int moveMainObject(float x, float y);       // move MainObject to position x,y

There is an example howto use these methods within pvbaddon.tar.gz directory pvbaddon/templates/weblayout/

Here are some examples.

A Simple SVG
A Simple SVG
A mechanical Drawing
A mechanical Drawing
A SVG of a process
A SVG of a process
A SVG with moveing parcels and sliders implemented as SVG
A SVG with moveing parcels and sliders implemented as SVG

The first 2 examples are from pvsexample included in pvbrowser. The last example can be downloaded here .


Back Content Forward