Howto:Extend the Canvas SVG module

From FlightGear wiki
Revision as of 17:04, 19 April 2016 by Hooray (talk | contribs) (→‎tag)
Jump to navigation Jump to search
This article is a stub. You can help the wiki by expanding it.

Objective

Document and demonstrate how the Canvas/Nasal SVG module can be extended to suport additional SVG features, by mapping SVG/XML markup to the corresponding Canvas primitives, such as mapping the <image> tag to a Canvas.Image.

Background

Test Code

Note  The following code is sufficiently self-contained, so that it can be put into a separate Nasal module, but also executed as a standalone snippet using the Nasal Console
var (width,height) = (320,160);
var svg_filename = "/Nasal/img.svg";
var title = 'SVGTest: '~svg_filename;


# create a new window, dimensions are WIDTH x HEIGHT, using the dialog decoration (i.e. titlebar)
var window = canvas.Window.new([width,height],"dialog")
 .set('title',title);

# adding a canvas to the new window and setting up background colors/transparency
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));

# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();

# change the background color 
myCanvas.set("background", "#ffaac0");

# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();

var svg_symbol = root.createChild('group');
canvas.parsesvg(svg_symbol, svg_filename);

svg_symbol.setTranslation(width/2,height/2);

#svg_symbol.setScale(0.2);
#svg_symbol.setRotation(radians)

Examples

<image> tag

Let's assume, we'd like to support raster images, e.g. SVG markup such as the following:

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">
  <image x="160" y="0" width="160" height="160"
     xlink:href="https://forum.flightgear.org/styles/flightgear/theme/images/logoF.png" />
</svg>

By default, the svg parser will bail out with a message saying Skipping unknown element image. Thus, we need to open $FG_ROOT/Nasal/canvas/svg.nas and navigate to the line where that error is triggered:

else
    {
      printlog("info", "Skipping unknown element '" ~ name ~ "'");
      skip = level;
      return;
    }

So, the next step is prepending a new conditional block that looks specifically for the new tag that we'd like to see supported, e.g.:

else if (name == "image")
    {
        printlog("alert", "image tag encountered (not yet implemented)");
    }

Or as a diff/patch:

diff --git a/Nasal/canvas/svg.nas b/Nasal/canvas/svg.nas
index d7fe570..c1b4a90 100644
--- a/Nasal/canvas/svg.nas
+++ b/Nasal/canvas/svg.nas
@@ -578,6 +578,10 @@ var parsesvg = func(group, path, options = nil)
       append(defs_stack, "defs");
       return;
     }
+    else if (name == "image") 
+    {
+       printlog("alert", "image tag encountered (not yet implemented)");
+    }
     else
     {
       printlog("info", "Skipping unknown element '" ~ name ~ "'");

Whenever the parser now sees an image tag, it will tell us so saying: parsesvg: image tag encountered (not yet implemented)

The next step is actually mapping the SVG markup to the corresponding Nasal/Canvas code.

However, for starters, we will keep things simple and add a fixed image from $FG_ROOT/Textures whenever an image tag is encountered, e.g. a splash screen texture:

For that, we need to take into account that, as per the SVG spec, an image tag may not only refer to raster images, but also other vector graphics (SVG files), which means that we need to look at the file extension - i.e. only add a raster image primitive (Canvas.Image) if the extension is not .svg: