ExportFilter

Webs: Faemalia -:- Greatprawn -:- Playground -:- Technical -:- Tweak
Technical Web Sections: Register -:- Users -:- Changes -:- Index -:- Search -:- Statistics

An export filter is most easily implemented as a renderer. To make a new renderer, make a 'subclass' of the Renderer and supply your own render functions. Like the Object subclasses, Renderer subclass structures must start with an instance of Renderer. After that can follow extra object values, typically for export renderers including a file being written:

struct _MyRenderer {
  Renderer renderer; /* Superclass */
  FILE *file; /* File to write to */
...

After that, you need to define the RenderOps implementations for your renderer. Some of the more complex functions have defaults that use the lower-level functions, for instance draw_rounded_rectangle uses lines and arcs. To allow these to be used, you should initialize the vtable in a function rather than by a structure definition:

static void
init_my_renderops()
{
  myRenderOps = create_renderops_table();

  myRenderOps->begin_render = (BeginRenderFunc) my_begin_render;
  myRenderOps->end_render = (EndRenderFunc) my_end_render;
...

The create_renderops_table call inserts the default implementation, and you can then override those that you want. If for some reason you want to call the default implementation from yours, this is where you should grab a pointer to it.

To avoid creating a new table every time you export, you can make the table a static top-level variable and only call init_my_renderops if it's NULL.

When the vtable is initialized, you need to define an export function that creates and uses the renderer. This is the format of the function that Dia will call to do the exporting:

static void
export_func(DiagramData *data, const gchar *filename, 
            const gchar *diafilename, void* user_data)

data is the diagram itself. filename is the actual filename to write to, the name you'll want to call fopen on. diafilename is the name of this diagram, for comments and suchlike. userdata is just a pointer to data that belongs to the export filter.

The typical export function does the following:

  1. Open the file.
  2. Initialize vtable, if needed.
  3. Create and initialize the renderer.
  4. Call begin_render.
  5. Iterate over layers and render each.
  6. Call end_render.
  7. Free the renderer.
  8. Close the file.

This example is an abbreviated version of the XFig export filter main function:

static void
export_fig(DiagramData *data, const gchar *filename,
           const gchar *diafilename, void* user_data)
{
  FILE *file;
  Renderer *renderer;
  int i;
  Layer *layer;

  file = fopen(filename, "w");
  if (file == NULL) {
    message_error(_("Couldn't open: '%s' for writing.\n"), filename);
    return;
  }

  if (figRenderOps == NULL)
     init_fig_renderops();
  renderer = (Renderer *)g_new(Rendererfig, 1);
  renderer->renderer.is_interactive = 0;
  renderer->renderer.interactive_ops = NULL;
  renderer->file = file;

  (renderer->ops->begin_render)(renderer);

  for (i=0; ilayers->len; i++) {
    layer = (Layer *) g_ptr_array_index(data->layers, i);
    layer_render(layer, renderer, NULL, NULL, data, 0);
  }

  (renderer->ops->end_render)(renderer);

  g_free(renderer);
  fclose(file);
}

For some file formats, you need to do multiple passes of rendering. For instance, the XFig format requires all colors to be defined at the start of the file, so we must scan the diagram for non-predefined colors. This is most easily done by having more than one RenderOps table, one for each type of pass. You can reuse the Renderer structure by just replacing the ops pointer. Just be sure to call the begin_render and end_render functions around each pass.

-- LarsClausen - 05 Sep 2002


Edit -:- Attach -:- Ref-By -:- Printable -:- More