{********************************************}
{ TeeChart Pro VML Canvas and Exporting      }
{ VML : Vector Markup Language               }
{ Copyright (c) 2001-2020 by Steema Software }
{       All Rights Reserved                  }
{********************************************}
unit TeeVMLCanvas;
{$I TeeDefs.inc}

interface

uses {$IFNDEF LINUX}
     {$IFNDEF FMX}
     Windows,
     {$ENDIF}
     {$ENDIF}
     Classes,

     {$IFDEF FMX}
     System.UITypes, FMX.Types, System.Types,

     {$IFDEF D17}
     System.UIConsts,
     {$ENDIF}

     {$IFDEF D19}
     FMX.Graphics,
     {$ENDIF}

     {$ELSE}

     {$IFDEF D16}
     System.UITypes,
     {$ENDIF}

     Graphics, Forms,

     {$IFDEF D9}
     Types,
     {$ENDIF}

     {$ENDIF}

     {$IFDEF FMX}
     FMXTee.Canvas, FMXTee.Procs, FMXTee.Editor.Export
     {$ELSE}
     TeCanvas, TeeProcs, TeeExport
     {$ENDIF};

type
  TVMLCanvas = class(TTeeNeutralCanvas)
  private
    { Private declarations }
    FX           : TCoordinate;
    FY           : TCoordinate;
    FStrings     : TStrings;

    Procedure Add(Const S:String); {$IFDEF D9}inline;{$ENDIF}
    Procedure InternalRect(Const Rect:TRect; UsePen,IsRound:Boolean);
    Function PointToStr(const X,Y:TCoordinate):String;
    Function PrepareShape:String;
    Function TheBounds:String;
    function VMLOpacity:String;
  protected
    { Protected declarations }

    { 2d }
    procedure SetPixel(X, Y: Integer; Value: TColor); override;

    { 3d }
    procedure SetPixel3D(X,Y,Z:Integer; Value: TColor); override;

    Function VmlPen(const Element:String):String;
  public
    { Public declarations }
    Constructor CreateText(const AStrings:TStrings);

    Function InitWindow( const DestCanvas:TCanvas;
                         const A3DOptions:TView3DOptions;
                         ABackColor:TColor;
                         Is3D:Boolean;
                         Const UserRect:TRect):TRect; override;

    procedure Arc(const Left, Top, Right, Bottom, StartX, StartY, EndX, EndY: Integer); override;
    procedure Draw(X, Y: Integer; const Graphic: TGraphic); override;
    procedure FillRect(const Rect: TRect); override;
    procedure Ellipse(const X1, Y1, X2, Y2: TCoordinate); override;
    procedure LineTo(const X,Y:TCoordinate); override;
    procedure MoveTo(const X,Y:TCoordinate); override;
    procedure Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); override;
    procedure Rectangle(const X0,Y0,X1,Y1:TCoordinate); reintroduce; override;
    procedure RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer); override;
    procedure StretchDraw(const Rect: TRect; const Graphic: TGraphic); override;
    Procedure TextOut({$IFDEF FMX}const{$ENDIF} X,Y:TCoordinate; const Text:String); override;

    procedure ClipRectangle(Const Rect:TRect); override;
    procedure UnClipRectangle; override;

    Procedure GradientFill( Const Rect:TRect;
                            StartColor,EndColor:TColor;
                            Direction:TGradientDirection;
                            Balance:Integer=50;
                            RadialX:Integer=0;
                            RadialY:Integer=0); override;

    procedure RotateLabel(x,y:Integer; Const St:String; RotDegree:Double;
                          AllowHTML:Boolean=False); override;
    procedure RotateLabel3D(x,y,z:Integer;
                            const St:String; RotDegree:Double;
                            AllowHTML:Boolean=False); override;
    Procedure Polygon({$IFDEF D6}const{$ENDIF} Points:Array of TPoint); override;
    Procedure Polyline({$IFDEF D6}const{$ENDIF} Points:Array of TPoint); override;

    { 3d }
    Procedure ShowImage(const DestCanvas,DefaultCanvas:TCanvas; Const UserRect:TRect); override;
    procedure EllipseWithZ(const X1, Y1, X2, Y2:TCoordinate; const Z:Integer); override;
    Procedure TextOut3D(X,Y,Z:Integer; const Text:String; AllowHTML:Boolean=False); override;
  end;

  TVMLExportFormat=class(TTeeExportFormat)
  private
  protected
    Procedure DoCopyToClipboard; override;
  public
    function Description:String; override;
    function FileExtension:String; override;
    class function FileFilter:String; override;
    Function VML:TStringList;
    Procedure SaveToStream(Stream:TStream); override;
  end;

procedure TeeSaveToVMLFile( const APanel:TCustomTeePanel; const FileName: {$IFDEF D18}String{$ELSE}WideString{$ENDIF};
                            AWidth:Integer=0;
                            AHeight: Integer=0);

implementation

Uses {$IFDEF FMX}
     FMX.Platform,
     {$ELSE}
     Clipbrd,
     {$ENDIF}

     {$IFDEF FMX}
     FMXTee.Constants,
     {$ELSE}
     TeeConst,
     {$ENDIF}
     SysUtils;

{ TVMLCanvas }
Constructor TVMLCanvas.CreateText(const AStrings:TStrings);
begin
  Create;

  FSupportsAlpha:=True;
  
  FStrings:=AStrings;
  UseBuffer:=False;

  { start }
  Add('<xml:namespace prefix="v"></xml>');
end;

Procedure TVMLCanvas.ShowImage(const DestCanvas,DefaultCanvas:TCanvas; Const UserRect:TRect);
begin { finish }
  Add('</v:group>');
  Pen.OnChange:=nil;
end;

Procedure TVMLCanvas.Add(Const S:String);
begin
  FStrings.Add(S);
end;

Function VMLColorInternal(AColor:TColor):String;
begin
  //AColor:=ColorToRGB(AColor);

  // Remove Alpha, if existing:
  AColor:=TTeeCanvas.ColorFrom(AColor,0);

  case AColor of
    {$IFDEF FMX}claBlack{$ELSE}clBlack{$ENDIF}: result:='black';
    {$IFDEF FMX}claWhite{$ELSE}clWhite{$ENDIF}: result:='white';
    {$IFDEF FMX}claRed{$ELSE}clRed{$ENDIF}:   result:='red';
    {$IFDEF FMX}claGreen{$ELSE}clGreen{$ENDIF}: result:='green';
    {$IFDEF FMX}claBlue{$ELSE}clBlue{$ENDIF}:  result:='blue';
    {$IFDEF FMX}claYellow{$ELSE}clYellow{$ENDIF}: result:='yellow';
    {$IFDEF FMX}claGray{$ELSE}clGray{$ENDIF}:  result:='gray';
    {$IFDEF FMX}claNavy{$ELSE}clNavy{$ENDIF}:  result:='navy';
    {$IFDEF FMX}claOlive{$ELSE}clOlive{$ENDIF}: result:='olive';
    {$IFDEF FMX}claLime{$ELSE}clLime{$ENDIF}:  result:='lime';
    {$IFDEF FMX}claTeal{$ELSE}clTeal{$ENDIF}:  result:='teal';
    {$IFDEF FMX}claSilver{$ELSE}clSilver{$ENDIF}:  result:='silver';
    {$IFDEF FMX}claPurple{$ELSE}clPurple{$ENDIF}:  result:='purple';
    {$IFDEF FMX}claFuchsia{$ELSE}clFuchsia{$ENDIF}:  result:='fuchsia';
    {$IFDEF FMX}claMaroon{$ELSE}clMaroon{$ENDIF}:  result:='maroon';
  else
  begin
    result:='#'+IntToHex(GetRValue(AColor),2)+
                IntToHex(GetGValue(AColor),2)+
                IntToHex(GetBValue(AColor),2);

    if result='#000000' then result:='#0';
  end;
  end;
end;

Function VMLColor(AColor:TColor):String;
begin
  result:='"'+VMLColorInternal(AColor)+'"';
end;

procedure TVMLCanvas.Rectangle(const X0,Y0,X1,Y1:TCoordinate);
begin
  InternalRect(TeeRect(X0,Y0,X1,Y1),True,False);
end;

procedure TVMLCanvas.MoveTo(const X, Y: TCoordinate);
begin
  FX:=X;
  FY:=Y;
end;

function ToStr(const Value:TCoordinate):String;
begin
  {$IFDEF FMX}
  result:=IntToStr(Round(Value));
  {$ELSE}
  result:=TeeStr(Value);
  {$ENDIF}
end;

Function TVMLCanvas.VmlPen(const Element:String):String;

  Function PenStyle:String;
  begin
    if Pen.SmallDots then
       result:='dot'
    else
    Case Pen.Style of
      psSolid: ;
      psDash: result:='dash';
      psDot: result:='dot';
      psDashDot: result:='dashdot';
      psDashDotDot: result:='longdashdotdot';
    end;
  end;

  function PenWidth(const Element:String):String;
  begin
    if Pen.Width>1 then
       result:=' '+Element+'="'+ToStr(Pen.Width)+'" '
    else
       result:='';
  end;

  // IE9 and up, self-closing tags aren't valid anymore.
  function ClosingTag:String;
  begin
    result:='</v:'+Element+'>';
  end;

begin
  result:=' strokecolor='+VMLColor(Pen.Color);

  if (Pen.Style<>psSolid) or Pen.SmallDots then
     result:=result+'><v:stroke dashstyle="'+PenStyle+'"'+PenWidth('weight')+'></v:stroke>'+ClosingTag
  else
     result:=result+PenWidth('strokeweight')+'>'+ClosingTag;
end;

procedure TVMLCanvas.LineTo(const X, Y: TCoordinate);
begin
  Add('<v:line from="'+PointToStr(FX,FY)+'" to="'+PointToStr(X,Y)+'"'+VmlPen('line'));
  FX:=X;
  FY:=Y;
end;

procedure TVMLCanvas.ClipRectangle(Const Rect:TRect);
begin
end;

procedure TVMLCanvas.UnClipRectangle;
begin
end;

procedure TVMLCanvas.StretchDraw(const Rect: TRect; const Graphic: TGraphic);
begin
end;

procedure TVMLCanvas.Draw(X, Y: Integer; const Graphic: TGraphic);
begin
end;

Function TVMLCanvas.TheBounds:String;
begin
  result:='width:'+ToStr(Bounds.Right-Bounds.Left)+
          ';height:'+ToStr(Bounds.Bottom-Bounds.Top);
end;

Function TVMLCanvas.PointToStr(const X,Y:TCoordinate):String;
begin
  result:=ToStr(X)+' '+ToStr(Y);
end;

function TVMLCanvas.VMLOpacity:String;
begin
  if ITransp=1 then
     result:=''
  else
     result:=' opacity="'+FormatFloat('0.##',1-ITransp)+'"';
end;

Procedure TVMLCanvas.GradientFill( Const Rect:TRect;
                                   StartColor,EndColor:TColor;
                                   Direction:TGradientDirection;
                                   Balance:Integer=50;
                                   RadialX:Integer=0;
                                   RadialY:Integer=0);
var TheAngle : Integer;
begin
  Case Direction of
     gdTopBottom  : TheAngle:=180;
     gdBottomTop  : TheAngle:=0;
     gdLeftRight  : TheAngle:=270;
     gdRightLeft  : TheAngle:=90;
     gdFromCenter : TheAngle:=0; { to-do }
     gdFromTopLeft: TheAngle:=315;
  else
     TheAngle:=225; { gdFromBottomLeft }
  end;

  Add('<v:shape style="position:absolute;'+TheBounds+';" stroked="f">');

  Add(' <v:path v="M '+PointToStr(Rect.Left,Rect.Top)+' L '+
        PointToStr(Rect.Right,Rect.Top)+' '+
        PointToStr(Rect.Right,Rect.Bottom)+' '+
        PointToStr(Rect.Left,Rect.Bottom)+' X E"></v:path>');

  Add(' <v:fill type="gradient" color='+VMLColor(StartColor)+' color2='+VMLColor(EndColor)+VMLOpacity);

  Add(' method="sigma" angle="'+IntToStr(TheAngle)+'" focus="100%"></v:fill>');
  Add('</v:shape>');
end;

procedure TVMLCanvas.FillRect(const Rect: TRect);
begin
  InternalRect(Rect,False,False);
end;

Procedure TVMLCanvas.InternalRect(Const Rect:TRect; UsePen,IsRound:Boolean);
var tmp  : String;
    tmpElement : String;
    tmpR : TRect;
begin
  if (Brush.Style<>bsClear) or (UsePen and (Pen.Style<>psClear)) then
  begin
    tmp:='<v:';

    if IsRound then tmpElement:='roundrect'
               else tmpElement:='rect';

    tmp:=tmp+tmpElement;

    tmpR:=OrientRectangle(Rect);

    tmp:=tmp+' style="position:absolute;left:'+
         ToStr(tmpR.Left)+';top:'+ToStr(tmpR.Top)+';width:'+
         ToStr(tmpR.Right-tmpR.Left-1)+';height:'+
         ToStr(tmpR.Bottom-tmpR.Top-1)+'"';

    if Brush.Style<>bsClear then
       tmp:=tmp+' fillcolor='+VMLColor(Brush.Color)
    else
       tmp:=tmp+' filled="false"';

    if (not UsePen) or (Pen.Style=psClear) then
       tmp:=tmp+' stroked="f"';

    tmp:=tmp+VMLOpacity;

    Add(tmp+VmlPen(tmpElement));
  end;
end;

procedure TVMLCanvas.Ellipse(const X1, Y1, X2, Y2: TCoordinate);
begin
  EllipseWithZ(X1,Y1,X2,Y2,0);
end;

procedure TVMLCanvas.EllipseWithZ(const X1, Y1, X2, Y2:TCoordinate; const Z: Integer);
var P1, P2 : TPoint;
begin
  if (Brush.Style<>bsClear) or (Pen.Style<>psClear) then
  begin
    P1:=Calculate3DPosition(X1,Y1,Z);
    P2:=Calculate3DPosition(X2,Y2,Z);

    Add('<v:oval style="position:absolute;left:'+ToStr(P1.X)+';top:'+ToStr(P1.Y)+
         ';height:'+ToStr(P2.Y-P1.Y)+';width:'+ToStr(P2.X-P1.X)+'" fillcolor='+
         VMLColor(Brush.Color)+VmlPen('oval'));
  end;
end;

procedure TVMLCanvas.SetPixel3D(X,Y,Z:Integer; Value: TColor);
begin
  if Pen.Style<>psClear then
  begin
    Calc3DPos(x,y,z);
    Pen.Color:=Value;
    MoveTo(x,y);
    LineTo(x,y);
  end;
end;

procedure TVMLCanvas.SetPixel(X, Y: Integer; Value: TColor);
begin
  if Pen.Style<>psClear then
  begin
    Pen.Color:=Value;
    MoveTo(x,y);
    LineTo(x,y);
  end;
end;

procedure TVMLCanvas.Arc(const Left, Top, Right, Bottom, StartX, StartY, EndX, EndY: Integer);
var tmpSt : String;
begin
  if Pen.Style<>psClear then
  begin
    PrepareShape;

    tmpSt:=' <v:path v="ar ';
    tmpSt:=tmpSt+PointToStr(Left,Top)+' '+PointToStr(Right,Bottom)+' '+
           PointToStr(StartX,StartY)+' '+PointToStr(EndX,EndY);

    Add(tmpSt+' e"></v:path>');
    Add('</v:shape>');
  end;
end;

procedure TVMLCanvas.Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
var tmpSt : String;
begin
  if (Brush.Style<>bsClear) or (Pen.Style<>psClear) then
  begin
    PrepareShape;
    tmpSt:=' <v:path v="m '+PointToStr((X2+X1) div 2,(Y2+Y1) div 2)+' at ';
    tmpSt:=tmpSt+PointToStr(X1,Y1)+' '+PointToStr(X2,Y2)+' '+
      PointToStr(X3,Y3)+' '+PointToStr(X4,Y4);

    Add(tmpSt+' x e"></v:path>');
    Add('</v:shape>');
  end;
end;

procedure TVMLCanvas.RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer);
begin
  InternalRect(TeeRect(X1,Y1,X2,Y2),True,True);
end;

Procedure TVMLCanvas.TextOut3D(X,Y,Z:Integer; const Text:String; AllowHTML:Boolean=False);

  Function FontStyle:String;
  begin
    if Font.Style=[] then result:=''
    else
    begin
      result:='';

      if {$IFDEF FMX}TFontStyle.{$ENDIF}fsBold in Font.Style then
         result:=' font-weight=bold;';

      if {$IFDEF FMX}TFontStyle.{$ENDIF}fsItalic in Font.Style then
         result:=result+' font-style=italic;';

      if {$IFDEF FMX}TFontStyle.{$ENDIF}fsUnderline in Font.Style then
         result:=result+' text-decoration=underline;';

      if {$IFDEF FMX}TFontStyle.{$ENDIF}fsStrikeOut in Font.Style then
         result:=result+' text-decoration=line-through;';
    end;
  end;

  Procedure DoText(AX,AY:Integer; AColor:TColor);
  begin
    if (TextAlign and TA_BOTTOM)=TA_BOTTOM then
       Dec(AY,TextHeight(Text))
    else
    if (TextAlign and TA_CENTER)=TA_CENTER then
       Dec(AX,TextWidth(Text) div 2)
    else
    if (TextAlign and TA_RIGHT)=TA_RIGHT then
       Dec(AX,TextWidth(Text));

    Inc(AX);
    Inc(AY);

    Add('<v:textbox style="position:absolute; left:'+IntToStr(AX)+'; top:'+IntToStr(AY)+
        '; color:'+VMLColorInternal(AColor)+'; text-align:left; '+
        ' font-family:'+Font.{$IFDEF FMX}Family{$ELSE}Name{$ENDIF}+
        '; font-size:'+ToStr(Round(Font.Size*1.4))+'; '+FontStyle+'">');

    Add(Text);

    Add('</v:textbox>');
  end;

  procedure TryDrawShadow(const AShadow:TTeeShadow; Factor:Integer);
  var tmpX : Integer;
     tmpY : Integer;
  begin
    with AShadow do
    if Visible then
    begin
      if (HorizSize<>0) or (VertSize<>0) then
      begin
        if HorizSize<0 then
        begin
          tmpX:=X;
          X:=X-Factor*HorizSize;
        end
        else
          tmpX:=X+Factor*HorizSize;

        if VertSize<0 then
        begin
          tmpY:=Y;
          Y:=Y-Factor*VertSize;
        end
        else
          tmpY:=Y+Factor*VertSize;

        DoText(tmpX,tmpY,AShadow.Color);
      end;
    end;
  end;

begin
  Calc3DPos(x,y,z);

  TryDrawShadow(Font.Shadow,1);
  TryDrawShadow(Font.Emboss,-1);

  DoText(X,Y,Font.Color);
end;

Procedure TVMLCanvas.TextOut({$IFDEF FMX}const{$ENDIF} X,Y:TCoordinate;
                             const Text:String);
begin
  TextOut3D({$IFDEF FMX}Round{$ENDIF}(x),{$IFDEF FMX}Round{$ENDIF}(y),0,Text);
end;

procedure TVMLCanvas.RotateLabel3D(x,y,z:Integer; Const St:String; RotDegree:Double;
                                   AllowHTML:Boolean=False);
begin
//TODO: RotDegree rotation
  Calc3DPos(x,y,z);
  TextOut(X,Y,St);
end;

procedure TVMLCanvas.RotateLabel(x,y:Integer; Const St:String; RotDegree:Double;
                                 AllowHTML:Boolean=False);
begin
  RotateLabel3D(x,y,0,St,RotDegree,AllowHTML);
end;

Function TVMLCanvas.PrepareShape:String;
begin
  result:='<v:shape style="position:absolute;'+TheBounds+'"';
  if Brush.Style<>bsClear then
     result:=result+' fillcolor='+VMLColor(Brush.Color)
  else
     result:=result+' filled="f"';

  if Pen.Style=psClear then
     result:=result+' stroked="f"'
  else
     result:=result+' strokecolor='+VMLColor(Pen.Color);

  Add(result+'>');
end;

Procedure TVMLCanvas.Polygon({$IFDEF D6}const{$ENDIF} Points: Array of TPoint);
var tmpSt : String;
    t     : Integer;
begin
  if (Brush.Style<>bsClear) or (Pen.Style<>psClear) then
  begin
    PrepareShape;
    tmpSt:=' <v:path v="m '+PointToStr(Points[0].X,Points[0].Y)+' l ';
    for t:=1 to High(Points) do
       tmpSt:=tmpSt+PointToStr(Points[t].X,Points[t].Y)+' ';

    Add(tmpSt+' x e"></v:path>');
    Add('</v:shape>');
  end;
end;

Procedure TVMLCanvas.Polyline({$IFDEF D6}const{$ENDIF} Points:Array of TPoint);
var tmpSt : String;
    t     : Integer;
begin
  if Pen.Style<>psClear then
  begin
    PrepareShape;

    tmpSt:=' <v:path v="m '+PointToStr(Points[0].X,Points[0].Y)+' l ';

    for t:=1 to High(Points) do
    begin
      tmpSt:=tmpSt+PointToStr(Points[t].X,Points[t].Y)+' ';

      if Length(tmpSt)>80 then
      begin
        Add(tmpSt+#10#$A);
        tmpSt:='';
      end;
    end;

    Add(tmpSt+' e"></v:path>');
    Add('</v:shape>');
  end;
end;

function TVMLCanvas.InitWindow(const DestCanvas: TCanvas;
  const A3DOptions: TView3DOptions; ABackColor: TColor; Is3D: Boolean;
  const UserRect: TRect): TRect;
begin
  result:=inherited InitWindow(DestCanvas,A3DOptions,ABackColor,Is3D,UserRect);
  Add('<v:group style="'+TheBounds+
       '" coordsize="'+
       PointToStr(Bounds.Right-Bounds.Left,Bounds.Bottom-Bounds.Top)+'"'+
       ' coordorigin="'+PointToStr(Bounds.Top,Bounds.Left)+
       '">');
end;

{ TVMLExportFormat }

function TVMLExportFormat.Description: String;
begin
  result:=TeeMsg_AsVML;
end;

procedure TVMLExportFormat.DoCopyToClipboard;
begin
  with VML do
  try
    TeeCopyClipboard(Text);
  finally
    Free;
  end;
end;

function TVMLExportFormat.FileExtension: String;
begin
  result:='HTM';
end;

class function TVMLExportFormat.FileFilter: String;
begin
  result:=TeeMsg_VMLFilter;
end;

procedure TVMLExportFormat.SaveToStream(Stream: TStream);
begin
  with VML do
  try
    SaveToStream(Stream);
  finally
    Free;
  end;
end;

type
  TTeePanelAccess=class(TCustomTeePanel);

// Returns a panel or chart in VML format into a StringList
function TVMLExportFormat.VML: TStringList;
var tmp : TCanvas3D;
begin 
  CheckSize;
  result:=TStringList.Create;
  Panel.AutoRepaint:=False;

  try
    // Protected across assemblies
    tmp:=TTeePanelAccess(Panel).InternalCanvas;
    TTeePanelAccess(Panel).InternalCanvas:=nil;

    Panel.Canvas:=TVMLCanvas.CreateText(result);

    if not Assigned(Panel.Parent) then
      Panel.BufferedDisplay:=True;

    try
      Panel.Draw(Panel.Canvas.ReferenceCanvas,TeeRect(Width,Height));

      { insert html headings at top }
      result.Insert(0,'<!-- Generated by TeeChart Pro Version '+TeeChartVersion+' -->');
      result.Insert(0,'<body>');

      result.Insert(0,'</head>');
      result.Insert(0,'<style>v\:* {behavior: url(#default#VML);}</style>');
      result.Insert(0,'<head>');

      result.Insert(0,'<html xmlns:v="urn:schemas-microsoft-com:vml">');
      //result.Insert(0,'<!DOCTYPE html>');

      { add html headings at bottom }
      result.Add('</body>');
      result.Add('</html>');
    finally
      Panel.Canvas:=tmp;
    end;
  finally
    Panel.AutoRepaint:=True;
  end;
end;

procedure TeeSaveToVMLFile( const APanel:TCustomTeePanel; const FileName: {$IFDEF D18}String{$ELSE}WideString{$ENDIF};
                            AWidth:Integer=0;
                            AHeight: Integer=0);
begin { save panel or chart to filename in VML (html) format }
  with TVMLExportFormat.Create do
  try
    Panel:=APanel;
    Height:=AHeight;
    Width:=AWidth;
    SaveToFile(FileName);
  finally
    Free;
  end;
end;

initialization
  {$IFDEF FMX}
  TTeeExportFormats.RegisterFormat(TVMLExportFormat);
  {$ELSE}
  RegisterTeeExportFormat(TVMLExportFormat);
  {$ENDIF}
finalization
  {$IFDEF FMX}
  TTeeExportFormats.UnRegisterFormat(TVMLExportFormat);
  {$ELSE}
  UnRegisterTeeExportFormat(TVMLExportFormat);
  {$ENDIF}
end.

