Drawing outlines around SVG elements

 

The SVG control package offers a number of controls to display SVG graphics. But if you want more control over how SVG graphics are rendered or if you want to post process graphics, you can also use the lower level classes, interfaces and functions of the package.

 

Two of these interfaces are

ISVGRenderContext

and

ISVGRoot

 

ISVGRenderContext is a high quality canvas, very much like the Delphi Firemonkey canvas, but it has the advantage that you can use it in Delphi VCL also. It always draws to a bitmap which you have to supply.

It has most of the drawing functions that the Firemonkey canvas has. Functions like DrawCircle, DrawRect, FillRect, FillPath, ApplyFill, ApplyStroke, MultiplyMatrix BeginScene, EndScene and so on.

You can use the render context (RC) for example to add drawings before or after you render an SVG graphic, or you could just use it for high quality drawings, also supporting transparency, something Delphi VCL does not excel in.

ISVGRoot is the root of the tree of SVG elements of the SVG graphic, equivalent to the Document Object Model (DOM)  in an internet browser. This is created after parsing the SVG xml text and allows you to manipulate elements and parameters before actually rendering the graphic. You can also use it to measure element dimensions.

I’ll give an example here how you can use both to render an SVG graphic an then to post process the graphic to draw some outlines around some of the elements in the SVG graphic.

We use the following simple SVG graphic and we will draw outlines around the text, star group an rotated ellipse.

 

This SVG graphic looks like this

 

Now for the code.

In a new Delphi VCL application, using one form with a standard TButton and TImage control, on the OnClick of the button we do the following:

  1. First we will create an SVGRoot object and parse the SVG text
  2. Then we will calculate the size of the entire SVG and set the bitmap size accordingly
  3. Then we will render the SVG to the bitmap
  4. Then we will draw rectangles around the three elements
  5. Last we assign the bitmap to the TImage control

 

Step 1, create SVGRoot object and parse the SVG text.

 

Step 2, calculate the size of the entire SVG and set the bitmap size accordingly

Next we want to know the dimensions of the SVG graphic. In this case it is not very difficult because the svg element has a with (480) and a height(320) defined. But for this example I’ll show how to calculate the size of an arbitrary SVG graphic.

The ISVGRoot interface has a method “CalcIntrinsicSize”  for calculating the outer dimensions of an SVG graphic.

This method is defined as follows:

So it needs a render context and the bounds of a parent object, this is the outer container. The function returns a rectangle with the dimensions of the SVG graphic.

Why does it need a render context? Because the SVG graphic may contain text and it needs a render context to have access to the font system.

The aParentBounds is used in cases where the SVG size is a percentage of it’s parent or container object.

So to use this we need to create a render context first. Since we don’t have to actually render the SVG yet, we can create a render context based on an arbitrary sized bitmap, so we take a bitmap of size 1 by 1.

 

Step 3, render the SVG to the bitmap

Now we are going to render the SVG to the bitmap using ISVGRoot and ISVGRenderContext.

So just as with drawing on a Firemonkey canvas, we need to enclose any drawing with BeginScene and EndScene commands. We could also modify the matrix of the render context to scale or rotate or translate the SVG graphic.

The global procedure “SVGRenderToRenderContext” is used to draw the SVG contained in “SVGRoot” to the render context “RC”.

This procedure is defined as follows:

The parameter “aRenderOptions” can have a combination of the following settings:

  • sroFilters: SVG filters will be rendered if these are defined in the SVG graphic
  • sroClippath: clippaths will be rendered if these are defined in the SVG graphic
  • sroEvents: mouse pointer events will be enabled for the SVG graphic, for this a Object state tree will be generated

Because filters, clippaths en events need extra, in some cases a lot of resources, they are made optional.

The last option “sroEvents” is interesting if we want to have measurements of individual elements in the SVG graphic. If we want to enable mouse pointer events, we need to know the dimensions of each visible element. The renderer will in that case create a so called “ObjectState” tree , while rendering the SVG graphic.

The “ObjectState” tree will contain a ScreenBBox (bounding box in screen dimensions) of every element visible on the screen. Note that sometimes an element can be drawn multiple times on the screen, if it is referenced by a “use” element. In that case it will be present more than once in the Object State tree.

So we will use the “ObjectState” tree to draw outlines around elements on the rendered SVG.

Step 4, draw rectangles around the three elements and step 5, assign bitmap to TImage control

This produces the following output on the VCL form.

 

 

You can download the sources here, to compile it, you need the demo or the full version of the SVG control package v2.2

SVGDrawing

SVG package v2.20 update 13 for Delphi Rio

In update 13 of the SVG control package, functionality is added to support DPI aware applications.

Scaling, without loss of quality, is of course one of the major benefits of SVG graphics, so they lend themselves well for this kind of application

 

Delphi VCL

The VCL controls in the SVG Package: TSVG2Control, TSVG2Image and TSVG2LinkedImage will now scale the containing SVG graphic if your application is DPI aware en the DPI settings change.

Here is a bit of technical background how scaling in TSVG2Control is implemented, this is more or less the same for TSVGImage and TSVGLinkedImage:

  • A “Scale” property is added to the control
  • The “ChangeScale” method from TControl is overridden and will set a new value for “Scale”
  • Following a change in size of the control, the embedded “render context” of the control is also resized (this was already the case in previous versions).
  • If the SVG renderer draws on the render context, the coordinates are multiplied by “Scale”
  • If the SVG renderer needs to find the element on a particular coordinate of the render context, the coordinates are divided by “Scale”

 

What about the SVG image list?

The TSVG2ImageList, TSVG2LinkedImageList and TSVG2Graphic are not derived from TControl but are just components. These have no parent control and will not scale automatically when DPI settings change (this is the same as with Delphi’s standard TImageList).

To respond to DPI changes, you could write a procedure that updates the size of the images in the SVG image list when this is required. This is something you would have to put into your application yourself, since I can’t implement it in the TSVG2ImageList component.

This procedure could look like this:

This procedure must be called, one time when the application starts…

… and every time the monitor dpi setting changes.

If you do this with a normal TImageList, the bitmaps will be stretched, witch degrades the quality of the image. The difference with the SVG image list is that a change in bitmap size will trigger a re-render of the image, which will keep the quality unchanged.

There is of course a performance penalty for the re-render, this will depend on the amount of images in the image list and the complexity of the svg graphics.

An alternative solution is to use multiple image lists with pre-rendered images and then switch between these image lists by updating the image list property of the linked controls.

I updated the VCL version of the SVG viewer demo application so it is DPI aware. So the images in the SVG image list and any SVG graphics that are loaded or added will be scaled according to the monitor settings.

 

Delphi FMX

The controls in FMX already support scaling, so no extra functionality is added to the FMX SVG controls to support DPI awareness.

The image list in FMX also supports scaling, it isn’t based on fixed sized bitmaps as in VCL but allows for different sized bitmaps, here also nothing had to be changed.

So on the control/component level, there seems to be nothing extra that has to be done.

The only thing we can do, is just check how the application behaves if we declare it DPI aware.

So I checked the “Per Monitor V2” setting in the manifest of the FMX viewer application and gave it a run.

What didn’t work:

  • Any controls that have the “ControlType” property on “Platform” won’t scale right, these I had to change to “Styled”
  • The application did not respond to change in monitor DPI settings while running.

What did work:

  • Starting the application, after changing the monitor scale settings, seems to work o.k.

(Image is from OpenClipart.org “blue-butterfly”)

 

Conclusion

  • The VCL components in the SVG control package will scale automatically in response to any scaling of there parent control
  • The VCL components, TSVG2ImageList, TSVG2LinkedImageList, TSVG2Graphic will not scale automatically, you have to deal with that in your application.
  • The FMX controls in the SVG control package already support scaling, but although you can declare an FMX application DPI aware, it seems that change in DPI settings while running is not yet supported.

Apart from these changes in update 13, a number of bugs are fixed and the performance for SVG mouse events is much improved, see the change log for a full list.

 

v2.20 update 11

Here are a couple of things that are new or improved in update 11. For a full list, see the change log.

Added “AspectRatioAlign” and “AspectRatioMeetOrSlice” to SVG controls

It was already possible to automatically add a viebox to SVG graphics that don’t have one defined with the property “AutoViewbox”.

I have now added the properties “AspectRatioAlign” and “AspectRatioMeetOrSlice”. These properties give you further control how the SVG graphic is aligned within the control bounds.

Below you see the effect of setting the “AutoViewbox” property.

If “AutoViewbox” = FALSE, the SVG graphic will be rendered exactly as defined in the SVG graphic itself, if it is bigger than the SVG control, it will be clipped.

If “AutoViewbox” = TRUE, the SVG graphic will be aligned within the control according to the “AspectRatioAlign’ and “AsectRatioMeetOrSlice” settings, the default setting is “xMid yMid Meet”.

 

Here you see different settings for “AspectRatioAlign” and “AspectRatioMeetOrSlice”, these are equivalent to the settings of the SVG “preserveAspectRatio” attribute.

 

Improved rendering quality for rotated SVG graphics

On FMX you can rotate and scale controls. The SVG renderer in update 11 contains a new algorithm for calculating internal bitmap buffers, for things like filters and masks. This gives much better results for transformed controls.

Below you see de difference between the graphic rendered with update 10 left, and update 11 right.

This also works on VCL, although in Delphi, you don’t have properties on the VCL controls to scale or rotate, you might have SVG graphics that have a transformation defined on the outer SVG element.

 

Support for “fill-rule” and “clip-rule” attributes

These attributes give you control over how an path element is filled.

This attribute is not supported for all platforms, see table below.

SVG Render context VCL FMX
Direct2D Yes n.a.
GDI+ No n.a.
Aggpas Yes Yes
FMX canvas n.a. No

The standard SVG render context for VCL is “Direct2D”, for FMX the standard is “FMX canvas”. You can overrule the stanard render context in the SVG control package by setting compiler directives in the “CompilerSettings.inc” file.

 

Improved parsing speed for pathdata

For testing the parsing speed I use an SVG graphic with a very large amount of path elements: the Rose-Breasted-Groesbeck.svg. This SVG graphic has 36832 path elements.

Update 10 Update 11
Parsing time in ms 8703 5766

 

Version 2.2 of SVG control package released

The SVG control package offers a set of controls and tools to use Scalable Vector Graphics (SVG) in your Delphi projects.

It is a commercial package but there is also a demo package available and a number of demo applications.

There are many improvements made in rendering quality and speed, also new features are added:

The image shows examples of SVG filters. The SVG graphic was made with Inkscape using the filter templates “Enamel Jewelry”, “Pressed Steel” and “Rough and Dilate” on the upper image. This was then rendered with the SVG control package on a VCL form. The filters in the package are all hand coded.

Inkscape has many wonderful filter templates to experiment with. These will often need quite a lot of resources to render, because SVG filters operate on pixel level.

With SVG pointer events you can build interactivity in SVG graphics, these will then be available in your Delphi application. An example of this is the “About” form in de demo viewer application, which is just a TSVG2Image control, but with pointer events build in for the “W3C SVG” link and the “www.bverhue.nl/delphisvg/” link.

The VCL demo viewer is also upgraded. SVG content can be copied and pasted using the clipboard and selections of rendered images can be exported as png files.

Source code of the demo applications is also included in the package.

Save bitmap with transparency as .png in VCL

Here is an example how to save a bitmap containing an alpha channel as a .png image using Delphi VCL.

In Delphi VCL you can use the TPngImage component to work with .png images. The only problem is that if you assign a bitmap to a TPngImage using PngImage.Assign(Bitmap) the alpha channel is lost.

I found the following solution thanks to this discussion on a German forum. So in stead of assigning the bitmap to the TPngImage object, you create a new blank TPngImage with alpha channel enabled and draw the bitmap on the TPngImage canvas.

After that you copy the alpha channel of the bitmap to the TPngImage in a separate step.

So in this example I render an SVG image to a bitmap and save the bitmap as a .png image (thanks to Thomas Wassermann).

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation
uses
  BVE.SVG2SaxParser,
  BVE.SVG2Intf,
  BVE.SVG2Types,
  BVE.SVG2ELements.VCL,
  Vcl.Imaging.pngimage;

{$R *.dfm}

// Source: http://www.entwickler-ecke.de/topic_Bitmap+pf32bit+mit+Alpha+afPremultipied+zu+PNG+speichern_103159,0.html

type
  TRGB = packed record B, G, R: byte end;
  TRGBA = packed record B, G, R, A: byte end;
  TRGBAArray = array[0..0] of TRGBA;

function PNG4TransparentBitMap(aBitmap:TBitmap): TPNGImage;
var
  X, Y: integer;
  BmpRGBA: ^TRGBAArray;
  PngRGB: ^TRGB;
begin
  //201011 Thomas Wassermann

  Result := TPNGImage.CreateBlank(COLOR_RGBALPHA, 8, aBitmap.Width , aBitmap.Height);

  Result.CreateAlpha;
  Result.Canvas.CopyMode:= cmSrcCopy;
  Result.Canvas.Draw(0, 0, aBitmap);

  for Y := 0 to Pred(aBitmap.Height) do
  begin
    BmpRGBA := aBitmap.ScanLine[Y];
    PngRGB:= Result.Scanline[Y];

    for X := 0 to Pred(aBitmap.width) do
    begin
      Result.AlphaScanline[Y][X] :=  BmpRGBA[X].A;
      if aBitmap.AlphaFormat in [afDefined, afPremultiplied] then
      begin
        if BmpRGBA[X].A <> 0 then
        begin
          PngRGB^.B := Round(BmpRGBA[X].B / BmpRGBA[X].A * 255);
          PngRGB^.R := Round(BmpRGBA[X].R / BmpRGBA[X].A * 255);
          PngRGB^.G := Round(BmpRGBA[X].G / BmpRGBA[X].A * 255);
        end else begin
          PngRGB^.B := Round(BmpRGBA[X].B * 255);
          PngRGB^.R := Round(BmpRGBA[X].R * 255);
          PngRGB^.G := Round(BmpRGBA[X].G * 255);
        end;
      end;
      Inc(PngRGB);
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  FileName: string;
  SVGParser: TSVGSaxParser;
  SVGRoot: ISVGRoot;
  Bitmap: TBitmap;
  Png : TPngImage;
begin
  if OpenDialog1.Execute then
  begin
    FileName := OpenDialog1.FileName;

    // Create a root to store the SVG rendering tree

    SVGRoot := TSVGRootVCL.Create;

    // Create a SAX parser instance

    SVGParser := TSVGSaxParser.Create(nil);
    try

      // Parse SVG document and build the rendering tree

      SVGParser.Parse('', FileName, SVGRoot);

      // Create a bitmap

      Bitmap := TBitmap.Create;
      try
        Bitmap.PixelFormat := TPixelFormat.pf32bit;   // 32bit bitmap
        Bitmap.AlphaFormat := TAlphaFormat.afDefined; // Enable alpha channel

        Bitmap.SetSize(480, 320);            // Set desired size
        Bitmap.Canvas.Brush.Color := clNone; // Fill background with transparent
        Bitmap.Canvas.FillRect(Rect(0, 0, 480, 320));

        // Render the SVG onto the bitmap

        SVGRenderToBitmap(
          SVGRoot, // The root containing the rendering tree
          Bitmap   // The destination bitmap
          );

        // Create the png component

        Png := PNG4TransparentBitMap(Bitmap);
        try
          FileName := ChangeFileExt(FileName, '.png');
          Png.SaveToFile(FileName);
        finally
          Png.Free;
        end;

      finally
        Bitmap.Free;
      end;

    finally
      SVGParser.Free;
    end;
  end;
end;

end.

 

 

 

Random SVG star

Here is small application that morphs between star shapes.

The VCL project looks like this: The form color is set to “black” and it contains an TSVG2Image aligned “alClient” and a TTimer.

Also, the “DoubleBuffered” property of the form is set to “True”, otherwise we’ll end up with a lot of unwanted flicker.

RandomStar1

Then the following code in the unit:


unit Unit1;

// -----------------------------------------------------------------------------
//
// Random SVG star
//
// B.J.H. Verhue
//
// -----------------------------------------------------------------------------

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.ExtCtrls,
  BVE.SVG2Image.VCL;

type
  TForm1 = class(TForm)
    SVG2Image1: TSVG2Image;
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure SVG2Image1Click(Sender: TObject);
  private
    FTime: integer;
    function LFO(const f: single): single;
    function LFO2(const s, f: single): single;
  public
    function StarPath(const aEdgeCount: integer; const aRIn, aROut, aOuterEdge: single): string;
    procedure Update;
  end;

var
  Form1: TForm1;

implementation
uses
  Vcl.Clipbrd,
  System.Math;

{$R *.dfm}

{ TForm1 }

function TForm1.LFO(const f: single): single;
begin
  // Low frequency oscilator

  Result := Sin(2* PI * f * FTime / 1000);
end;

function TForm1.LFO2(const s, f: single): single;
begin
  // Coupled LFO's to simulate randomness

  Result := LFO(s * LFO(f));
end;

function SVGColor(const aR, aG, aB: single): string;
begin
  Result := '#'
     + IntToHex(Round(255 * Abs(aR)), 2)
     + IntToHex(Round(255 * Abs(aG)), 2)
     + IntToHex(Round(255 * Abs(aB)), 2);
end;

function TForm1.StarPath(const aEdgeCount: integer; const aRIn, aROut,
  aOuterEdge: single): string;
var
  i: integer;
  InnerAngle, OuterAngle, X, Y: single;
begin
  // Create starshaped pathdata
  // aEdgeCount : number of edges or points
  // aRIn       : radius of star core
  // aROuter    : outer radius of star
  // aOuterEdge : width of star point

  Result := '';

  InnerAngle := 2 * PI / aEdgeCount;
  OuterAngle := arctan2(aOuterEdge, aROut);

  for i := 0 to aEdgeCount - 1 do
  begin
    X := aRIn * Sin(i * InnerAngle);
    Y := aRIn * Cos(i * InnerAngle);

    if i = 0 then
      Result := Result + Format('M%0:.2f,%1:.2f', [X, Y])
    else
      Result := Result + Format('L%0:.2f,%1:.2f', [X, Y]);

    X := aROut * Sin((i + 0.5 - OuterAngle) * InnerAngle);
    Y := aROut * Cos((i + 0.5 - OuterAngle) * InnerAngle);
    Result := Result + Format('L%0:.2f,%1:.2f', [X, Y]);

    X := aROut * Sin((i + 0.5 + OuterAngle) * InnerAngle);
    Y := aROut * Cos((i + 0.5 + OuterAngle) * InnerAngle);
    Result := Result + Format('L%0:.2f,%1:.2f', [X, Y]);

  end;
  Result := Result + 'Z"/>';
end;

procedure TForm1.SVG2Image1Click(Sender: TObject);
begin
  Timer1.Enabled := not Timer1.Enabled;

  // Put SVG on clipboard if paused

  if not Timer1.Enabled then
    Clipboard.AsText := SVG2Image1.SVG.Text;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  // Timer interval is set to 25ms, FPS=40

  Update;
  Inc(FTime, Timer1.Interval);
end;

procedure TForm1.Update;
begin
  // Create SVG content and assign to SVG2Image

  SVG2Image1.SVG.Text :=
      '<svg version="1.1" id="star"' + ' viewBox="-100 -100 200 200"' + ' xmlns="http://www.w3.org/2000/svg">'

  // Defs section with a random radial gradient

    + ' <defs>'
      + ' <radialGradient id="radGrad1">'
      + ' <stop offset="0%" stop-color="' + SVGColor(LFO2(0.01, 0.02), LFO2(0.015, 0.03), LFO2(0.008, 0.015)) + '" />'
      + ' <stop offset="100%" stop-color="' + SVGColor(LFO2(0.02, 0.01), LFO2(0.025, 0.015), LFO2(0.03, 0.008)) + '" />'
      + ' </radialGradient>'
    + '</defs>'

  // Path element with random starshape

    + '<path stroke="pink" fill="url(#radGrad1)" stroke-width="3" d="' + StarPath( Round(19 + 16 * LFO2(0.01, 0.01)), 50 + 30 * LFO2(0.03, 0.005), 80 + 10 * LFO2(0.008, 0.01), 16 + 16 * LFO2(0.01, 0.002)) + '"/>'

    + '</svg>';

  SVG2Image1.Repaint;
end;

initialization
  // Set decimal seperator for "Format" function
  FormatSettings.DecimalSeparator := '.';

end.

Here is the source, you will have to set the search path to the directory containing the “dcu” files if you use it with the demo package.

RandomStar

 

Enable transparency with TSVG2ImageList on VCL

There was a little bug in the package which prevented the proper rendering of images with transparency on the VCL TButton, so I uploaded a new fix which corrects this. After download of the fixed package you should rebuild the packages. The demo packages are update also.

In this post I would like to show how you can set properties on the TSVG2ImageList to enable transparency.

First there is the question what type of button you should select from the Tool Palette in the Delphi IDE. There is a TButton, a TBitBtn and a TSpeedButton.

It turns out that the TBitBtn and TSpeedButton are legacy controls and you should avoid using these in new applications. The illustration below shows why:

TSVG2ImageList1

There is  a Fuchsia color border on the TBitBtn and TSpeedButton control, that is because it uses the old windows method of simulating transparency by using the left (or right, not sure) bottom pixel as a transparency color. Before it copies the image from the image list, it clears the target area with color Fuchsia. On the pixels that should be semi transparent this Fuchsia color seeps through.

So TBitBtn and TSpeedButton should be avoided, the TButton, shows the correct image.

In this test application linked the image list to the ActionManager1 and added a couple of actions. On each action I set the ImageIndex of the corresponding image in the SVG2ImageList. Next linked one of the Actions to TButton.

On the TButton you also have to set the “Images” property to link to an image list. Then you have the option to set a number of image index properties on the TButton to show different images depending on the state of the button.

Finally here are the properties of the TSVG2ImageList to enable transparency:

TSVG2ImageList2

All color settings, especially “BkColor” should be set to “clNone” and “ColorDepth” should be set to “cd32Bit”.

SVG Font example for SVG package

The new version 2.1 of the SVG package supports SVG Fonts.

The previous versions of the package used only system fonts and converted these to glyphs, but this would not always give accurate results. Could be that the specified font is not installed and also, in Delphi it is not easy to find out wat the exact glyph metrics are.

With SVG fonts you have more control over the final result.

Here is an example how SVG fonts can be used with the package. I have created a test application that looks like this:

FontExample1

The memo control contains the SVG content and with the button “Apply”, I can apply the SVG content to the SVG2Image control.

There are two ways to apply SVG content to SVG controls, I could set the “SVG” property of the TSVG2Image controls like so:

procedure TForm1.bApplyClick(Sender: TObject);
begin
  SVG2Image1.SVG.Assign(Memo1.Lines);
  SVG2Image1.Repaint;
end;

The advantage of this method is dat the SVG content is saved in the form file and in the excutable file so you don’t have to supply the SVG as a seperate file to the excutable.

The other method is to specify a reference to a file in the “Filename” property of the TSVG2Image control. In that case we first have to save the content of the Memo control to file.

procedure TForm1.bApplyClick(Sender: TObject);
begin
  Memo1.Lines.SaveToFile(Edit1.Text);
  SVG2Image1.Filename := Edit1.Text;
  SVG2Image1.NeedsParse := True;
  SVG2Image1.Repaint;
end;

The advantage of the second method is that, if a reference to an external file is used from within the SVG content, the parser will know where to find it.

So in this example we want to specify an external SVG file containing the SVG font, so we use the second method.

The statement:  “SVG2Image1.NeedsParse := True” will force the control to reparse the SVG content, even though the filename stays the same.

For this example I have put this SVG content in the Memo:

<?xml version="1.0" standalone="no"?>
<svg width="10cm" height="3cm" viewBox="0 0 1000 300" xmlns="http://www.w3.org/2000/svg" version="1.1">

  <g font-family="Roboto" font-size="45" >
    <text x="200" y="150" fill="blue" >You are <tspan font-weight="bold" fill="red" >not </tspan>a banana.</text>
  </g>

  <rect x="1" y="1" width="998" height="298" fill="none" stroke="blue" stroke-width="2" />
</svg>

Now when I run the application it will save the SVG content to the location I have specified in the Edit control and render the SVG in the TSVG2Image control.

FontExample2

Now we need an SVG file containg an SVG font. You can use the Batik SVG Font converter to create such a file out of a any “True type font”. So as an example I have converted the Great Vibes  font to “GreatVibes.svg” and I have saved this file relative to the referencing SVG in folder “\Fonts”.

Now I have to change the SVG content to make it use the new font, there are several ways to do that.

You can use the SVG FontFace functionality like so:


<?xml version="1.0" standalone="no"?>
<svg
version="1.1"
width="10cm" height="3cm"
viewBox="0 0 1000 300"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">

<defs>
<font-face font-family="Great Vibes" unicode-range="U+0-7F">
<font-face-src>
<font-face-uri xlink:href="Fonts/GreatVibes.svg#Font"/>
</font-face-src>
</font-face>
</defs>

<g font-family="Great Vibes" font-size="80" >
<text x="200" y="150" fill="blue" >You are <tspan font-weight="bold" fill="red" >not </tspan>a banana.</text>
</g>

<rect x="1" y="1" width="998" height="298"  fill="none" stroke="blue" stroke-width="2" />
</svg>

The “font-face-uri” element specifies an external SVG document containing a font element with id=”Font”. Note that I had to add the namespace declaration xmlns:xlink=”http://www.w3.org/1999/xlink” in the SVG element because the font-face-uri uses the xlink:href attribute!

Another method would be using CSS like so:


<?xml version="1.0" standalone="no"?>
<svg
version="1.1"
width="10cm" height="3cm"
viewBox="0 0 1000 300"
xmlns="http://www.w3.org/2000/svg">

<defs>
<style type="text/css">
@font-face {
font-family: 'Great Vibes';
font-weight: normal;
font-style: normal;
src: url("Fonts/GreatVibes.svg#Font") format("svg")
}
</style>
</defs>

<g font-family="Great Vibes" font-size="80" >
<text x="200" y="150" fill="blue" >You are <tspan font-weight="bold" fill="red" >not </tspan>a banana.</text>
</g>

<rect x="1" y="1" width="998" height="298"  fill="none" stroke="blue" stroke-width="2" />
</svg>

The end result should look like this:

FontExample3