{********************************************************************}
{ written by TMS Software                                            }
{            copyright © 2013                                        }
{            Email : info@tmssoftware.com                            }
{            Web : http://www.tmssoftware.com                        }
{                                                                    }
{ The source code is given as is. The author is not responsible      }
{ for any possible damage done due to the use of this code.          }
{ The complete source code remains property of the author and may    }
{ not be distributed, published, given or sold in any form as such.  }
{ No parts of the source code can be included in any other component }
{ or application without written authorization of the author.        }
{********************************************************************}

unit FMX.TMSHTMLEngine;

interface

{$I TMSDEFS.INC}

uses
  Classes, Types, SysUtils, UITypes, FMX.Objects,
  FMX.Dialogs, FMX.Types, FMX.TMSBitmapContainer
  , UIConsts, FMX.Styles
  {$IFDEF DELPHIXE5_LVL}
  , FMX.Graphics
  {$ENDIF}
  ;

var
  FColorLookup: TStringList;

function HTMLDrawEx(Canvas: TCanvas; s:string; fr:TRectF;
                    XPos,YPos,FocusLink,HoverLink,ShadowOffset: Integer;
                    CheckHotSpot,CheckHeight,Print,Selected,Blink,HoverStyle,WordWrap: Boolean;
                    ResFactor:Double;
                    URLColor,HoverColor,HoverFontColor,ShadowColor:TAlphaColor;
                    var AnchorVal,StripVal,FocusAnchor: string;
                    var XSize,YSize: single; var HyperLinks,MouseLink: Integer;
                    var HoverRect:TRectF;LineSpacing: Integer;
                    BitmapContainer: TTMSFMXBitmapContainer; Opacity: Single): Boolean;

function HiLight(s,h,tag:string;DoCase:boolean):string;
function UnHiLight(s,tag:string):string;

function ColorLookup: TStringList;

implementation

function ColorLookup: TStringList;
begin
  Result := FColorLookup;
end;

function VarPos(su,s:string;var Res:Integer):Integer;
begin
  Res := Pos(su,s);
  Result := Res;
end;

function VarIPos(su,s:string;var Res:Integer):Integer;
begin
  Res := Pos(su,Uppercase(s));
  Result := Res;
end;



function HTMLStripAll(s:string):string;
var
  TagPos, PTP: integer;
begin
  Result := '';

  // remove all tags
  while (VarPos('<',s,TagPos)>0) do
  begin
    PTP := TagPos;
    if (VarPos('>',s,TagPos)>PTP) then
    begin
      Result := Result + Copy(s,1,TagPos - 1);
      Delete(s,1,TagPos)
    end
    else
      Break;
  end;
  Result := Result + s;
end;

function StripPos2HTMLPos(s:string; i: Integer): Integer;
var
  j,k: Integer;
  Skip: Boolean;
begin
  Result := 0;
  k := 1;
  Skip := False;

  {$IFDEF DELPHI_LLVM}
  for j := 0 to Length(s) - 1 do
  {$ELSE}
  for j := 1 to Length(s) do
  {$ENDIF}
  begin
    if s[j] = '<' then
      Skip := True;

    if k = i then
    begin
      Result := j;
      Exit;
    end;

    if not Skip then
      Inc(k);

    if s[j] = '>' then
      Skip := False;

  end;

  if k = i then
  begin
    Result := Length(s) + 1;
  end;
end;


function PosFrom(su,s:string; h: Integer;DoCase: boolean; var Res: Integer): Integer;
var
  r: Integer;
begin
  Result := 0;
  Res := 0;

  if h > 0 then
    Delete(s,1,h);

  if DoCase then
    r := Pos(su,s)
  else
    r := Pos(AnsiUpperCase(su),AnsiUpperCase(s));

  if r > 0 then
  begin
    Res := h + r;
    Result := Res;
  end;
end;


function HiLight(s,h,tag:string;DoCase:boolean):string;
var
  hs: string;
  l,k: Integer;
begin
  hs := HTMLStripAll(s);

  l := 0;

  if (pos(h,s) > 0) and ((pos('<',hs) >0) or (pos('>',hs) > 0)) then
  begin
    hs := StringReplace(hs,'<','&lt;',[rfReplaceAll]);
    hs := StringReplace(hs,'>','&gt;',[rfReplaceAll]);
    s := hs;
    h := StringReplace(h,'<','&lt;',[rfReplaceAll]);
    h := StringReplace(h,'>','&gt;',[rfReplaceAll]);
  end;

  while PosFrom(h,hs,l,DoCase,k) > 0 do
  begin
    l := k + Length(h);
    Insert('<'+tag+'>',s,StripPos2HTMLPos(s,k));
    Insert('</'+tag+'>',s,StripPos2HTMLPos(s,l));
  end;

  Result := s;
end;

function UnHiLight(s,tag:string):string;
begin
  Result := '';
  // replace line breaks by linefeeds
  tag := Uppercase(tag);
  while Pos('<'+tag+'>',Uppercase(s)) > 0 do s := StringReplace(s,'<'+tag+'>','',[rfIgnoreCase]);
  while Pos('</'+tag+'>',Uppercase(s)) > 0 do s := StringReplace(s,'</'+tag+'>','',[rfIgnoreCase]);

  s := StringReplace(s,'&lt;','<',[rfReplaceAll]);
  s := StringReplace(s,'&gt;','>',[rfReplaceAll]);

  Result := s;
end;


function Text2Color(s:string): TAlphaColor;
var
  i: integer;
begin
  Result := claBlack;
  if Assigned(FColorLookup) then
    if FColorLookup.Find(LowerCase(s), i) then
      Result := TAlphaColor(FColorLookup.Objects[i]);
end;

function HexVal(s:string): Integer;
var
  i,j: Integer;
  i1, i2: Integer;
begin
  if Length(s) < 2 then
  begin
    Result := 0;
    Exit;
  end;

  {$IFDEF DELPHI_LLVM}
  i1 := 0;
  i2 := 1;
  {$ELSE}
  i1 := 1;
  i2 := 2;
  {$ENDIF}

  if s[i1] >= 'A' then
    i := ord(s[i1]) - ord('A') + 10
  else
    i := ord(s[i1]) - ord('0');

  if s[i2] >= 'A' then
    j := ord(s[i2]) - ord('A') + 10
  else
    j := ord(s[i2]) - ord('0');

  Result := i shl 4 + j;
end;

function Hex2Color(s:string): TAlphaColor;
const
  Alpha = TAlphaColor($FF000000);
var
  r,g,b: Integer;
begin
  r := Hexval(Copy(s,2,2)) shl 16;
  g := Hexval(Copy(s,4,2)) shl 8;
  b := Hexval(Copy(s,6,2));
  Result := Alpha or TAlphaColor(b + g + r);
end;

function IPos(su,s:string):Integer;
begin
  Result := Pos(UpperCase(su),UpperCase(s));
end;

function IStrToInt(s:string):Integer;
var
  Err,Res: Integer;
begin
  Val(s,Res,Err);
  Result := Res;
end;

function DBTagStrip(s:string):string;
var
  i,j: Integer;
begin
  i := Pos('<#',s);
  if i > 0 then
  begin
    Result := Copy(s,1,i - 1);
    Delete(s,1,i);
    j := Pos('>',s);
    if j > 0 then
      Delete(s,j,1);
    Result := Result + s;
  end
  else
    Result := s;
end;

function CRLFStrip(s:string;break:boolean):string;
var
  i: Integer;
begin
  Result := '';
  {$IFDEF DELPHI_LLVM}
  for i := 0 to Length(s) - 1 do
  {$ELSE}
  for i := 1 to Length(s) do
  {$ENDIF}
  begin
    if not ( (s[i] = #13) or (s[i] = #10)) then
      Result := Result + s[i]
    else
      if (s[i] = #13) and break then
        Result := Result + '<BR>';
  end;
end;


function TagReplaceString(const Srch,Repl:string;var Dest:string):Boolean;
var
  i: Integer;
begin
  i := IPos(srch,dest);
  if i > 0 then
  begin
    Result := True;
    Delete(Dest,i,Length(Srch));
    Dest := Copy(Dest,1,i-1) + Repl + Copy(Dest,i,Length(Dest));
  end
  else
    Result := False;
end;

function HTMLDrawEx(Canvas:TCanvas; s:string; fr:TRectF;
                    XPos,YPos,FocusLink,HoverLink,ShadowOffset: Integer;
                    CheckHotSpot,CheckHeight,Print,Selected,Blink,HoverStyle,WordWrap: Boolean;
                    ResFactor:Double;
                    URLColor,HoverColor,HoverFontColor,ShadowColor:TAlphaColor;
                    var AnchorVal,StripVal,FocusAnchor: string;
                    var XSize,YSize: single; var HyperLinks,MouseLink: Integer;
                    var HoverRect:TRectF;LineSpacing: Integer;
                    BitmapContainer: TTMSFMXBitmapContainer; Opacity: Single): Boolean;
var
  su: string;
  r,dr: TRectF;
  hr,rr: TRectF;
  er: TRectf;
  htmlwidth,htmlheight,txtheight: Single;
  Align: TAlignment;
  PIndent: Integer;
  OldFont: TFont;
  OldFontColor: TAlphaColor;
  CalcFont: TFont;
  CalcFontColor: TAlphaColor;
  DrawFont: TFont;
  DrawFontCOlor: TAlphaColor;
  FontColor: TAlphaColor;
  OldPenColor: TAlphaColor;
  OldCalcFont: TFont;
  OldCalcFontColor: TAlphaColor;
  OldBkColor: TAlphaColor;
  BkColor,BGColor: TAlphaColor;
  OldDrawFont: TFont;
  OldDrawFontColor: TAlphaColor;
  Hotspot, ImageHotspot: Boolean;
  Anchor,OldAnchor,MouseInAnchor,Error: Boolean;
  paracolor,hvrcolor,hvrfntcolor,pencolor,blnkcolor,hifcol,hibcol: TAlphaColor;
  LastAnchor,OldAnchorVal,LType: string;
  IMGSize: TPoint;
  isSup,isSub,isPara,isShad: Boolean;
  hlcount,licount: Integer;
  imgali: single;
  subh, suph: Single;
  ListIndex: Integer;
  Invisible: Boolean;
  FoundTag: Boolean;
  nnFit: Integer;
  inspoint: Integer;
  AltImg,ImgIdx,OldImgIdx: Integer;
  ColL,ColB: TColor;
  ofsx,newofsx: integer;

  function HTMLDrawLine(Canvas: TCanvas;var s:string;r: TRectF;Calc:Boolean;
                        var w,h,subh,suph,imgali:Single;var Align:TAlignment; var PIndent: Integer;
                        XPos,YPos:Integer;var Hotspot,ImageHotSpot:Boolean;OffsetX: integer; var NewOffsetX: integer):string;
  var
    su,Res,TagProp,Prop,Tagp,LineText,dsu:string;
    cr: TRectF;
    linebreak,imgbreak,linkbreak: Boolean;
    indent,bmpx,bmpy,imgw,imgh: Integer;
    oldh: single;
    err: integer;
    sw, th, errs: Single;
    TagPos,SpacePos,o,l: Integer;
    bmp: TBitmap;
    NewColor: TAlphaColor;
    TagWidth,TagHeight,WordLen: Integer;
    WordWidth: Single;
    TagChar: Char;
    LengthFits: Boolean;
    ptf1,ptf2: TPointF;

  begin
    Result := '';
    LineText := '';
    WordWidth := 0;
    r.Bottom := r.Bottom - Subh;

    w := 0;
    sw := 0;

    LineBreak := False;
    ImgBreak := False;
    LinkBreak := False;
    HotSpot := False;
    ImageHotSpot := False;

//    r.Left := r.Left + offsetX;

    cr := r;
    res := '';

    if not Calc then
      cr.Left := cr.Left + OffsetX;

    if isPara and not Calc then
    begin
      Pencolor := Canvas.Stroke.Color;
      Canvas.Stroke.Color := Canvas.Fill.Color;
      {$IFDEF DELPHIXE6_LVL}
      Canvas.FillRect(Rectf(fr.Left,r.Top,fr.Right,r.Top + h),0,0,[TCorner.TopLeft, TCorner.TopRight, TCorner.BottomLeft, TCorner.BottomRight],Opacity);
      {$ELSE}
      Canvas.FillRect(Rectf(fr.Left,r.Top,fr.Right,r.Top + h),0,0,[TCorner.crTopLeft, TCorner.crTopRight, TCorner.crBottomLeft, TCorner.crBottomRight],Opacity);
      {$ENDIF}
    end;

    while (Length(s) > 0) and not LineBreak and not ImgBreak do
    begin
      // get next word or till next HTML tag
      TagPos := Pos('<',s);

      if WordWrap then
        SpacePos := Pos(' ',s)
      else
        SpacePos := 0;

      if (Tagpos > 0) and ((SpacePos > TagPos) or (SpacePos = 0)) then
        su := Copy(s,1,TagPos - 1)
      else
      begin
        if SpacePos > 0 then
          su := Copy(s,1,SpacePos)
        else
          su := s;
      end;

      WordLen := Length(su);

      while Pos('&nbsp;',su) > 0 do
      begin
        TagReplacestring('&nbsp;',' ',su);
      end;

      while Pos('&lt;',su) > 0 do
      begin
        TagReplacestring('&lt;','<',su);
      end;

      while Pos('&gt;',su) > 0 do
      begin
        TagReplacestring('&gt;','>',su);
      end;

      //WordLenEx := Length(su);

      if WordLen > 0 then
      begin
        th := Canvas.TextHeight(su);

        if isSub and (subh < (th / 4)) then subh := th / 4;
        if isSup and (suph < (th / 4)) then suph := th / 4;

        if th > h then
          h := th;

        StripVal := StripVal + su;

        if Invisible then
          Delete(s,1,WordLen);

        if not Invisible then
        begin
          // draw mode
          if not Calc then
          begin
            if isSup then
              cr.Bottom := cr.Bottom - suph;

            if isSub then
              cr.Bottom := cr.Bottom + subh;

            cr.Bottom := cr.Bottom - imgali;

            errs := Canvas.TextWidth(su);

            if BkColor <> claNull then
            begin
              Canvas.Fill.Color := BkColor;
              Canvas.Stroke.Color := BkColor;
              Canvas.FillRect(RectF(cr.Left, cr.Top, cr.Left + errs, cr.Top + Canvas.TextHeight('gh')),0,0,[],Opacity);
            end;

            if isShad then
            begin
              OffsetRect(cr,ShadowOffset,ShadowOffset);
              NewColor := Canvas.Fill.Color;
              Canvas.Fill.Color := ShadowColor;
              {$IFDEF DELPHIXE6_LVL}
              Canvas.FillText(cr,su,true,Opacity,[],TTextAlign.Leading, TTextAlign.Center);
              {$ELSE}
              Canvas.FillText(cr,su,true,Opacity,[],TTextAlign.taLeading, TTextAlign.taCenter);
              {$ENDIf}
              Offsetrect(cr,-ShadowOffset,-ShadowOffset);
              FontColor := NewColor;
            end;

            //errs := cr.Right;

            {$IFDEF DELPHI_LLVM}
            if (su <> '') and (su[length(su)-1] = ' ') and Anchor then
            {$ELSE}
            if (su <> '') and (su[length(su)] = ' ') and Anchor then
            {$ENDIF}
              dsu := su + #0
            else
              dsu := su;

            Canvas.Fill.Color := FontColor;
            {$IFDEF DELPHIXE6_LVL}
            Canvas.FillText(cr,dsu,wordwrap,Opacity,[],TTextAlign.Leading, TTextAlign.Center);
            {$ELSE}
            Canvas.FillText(cr,dsu,wordwrap,Opacity,[],TTextAlign.taLeading, TTextAlign.taCenter);
            {$ENDIF}


            cr.Right := cr.Left + errs;

            if Anchor and (Hyperlinks - 1 = FocusLink) then
              FocusAnchor := LastAnchor;

            if Error then
            begin
              Canvas.Stroke.Color := claRed;
              {$IFDEF DELPHIXE6_LVL}
              Canvas.Stroke.Kind := TBrushKind.Solid;
              {$ELSE}
              Canvas.Stroke.Kind := TBrushKind.bkSolid;
              {$ENDIF}
              Canvas.StrokeThickness := 1;

              l := Round((cr.Left / 2) * 2);
              if (l mod 4)=0 then o := 2 else o := 0;

              while l < cr.Right do
              begin
                ptf1 := PointF(l,r.Bottom + o - 1);
                if o = 2 then o := 0 else o := 2;
                ptf2 := PointF(l + 2, r.Bottom + o - 1);
                Canvas.DrawLine(ptf1, ptf2,Opacity);
                Inc(l,2);
              end;
            end;

            cr.Left := cr.Right;
            cr.Right := r.Right;
            cr.Bottom := r.Bottom;
            cr.Top := r.Top;
          end
        else
          begin
            cr := r; //reinitialized each time !

            cr.Right := cr.Left + Canvas.TextWidth(su);

            // preparations for editing purposes
            if (ypos > cr.Top) and (ypos < cr.bottom) and (xpos > w) then {scan charpos here}
            begin
              er := rectf(w,cr.top,xpos,cr.bottom);

              //this will get the character pos of the insertion point
              if nnfit = WordLen then
                InsPoint := InsPoint + WordLen
              else
                InsPoint := InsPoint + nnfit;
            end;

            //end of preparations for editing purposes

            // Calculated text width
            WordWidth := cr.Right - cr.Left;
            w := w + WordWidth;

            //errs := XPos - cr.Left;
            //errs := r.Right - XPos - cr.Left;

            if (XPos - cr.Left  >= w - WordWidth) and (XPos - cr.Left <= w) and Anchor then
            begin
              HotSpot := True;
              if (YPos > cr.Top){ and (YPos < cr.Bottom)} then
              begin
                Anchorval := LastAnchor;
                MouseInAnchor := True;
              end;
            end;
          end;

          LengthFits := (w < r.Right - r.Left - OfsX) or (r.Right - r.Left - OfsX <= WordWidth);

//          if not LengthFits and
//            ((Length(LineText) > 0) and (LineText[Length(LineText)] <> ' ')) then
//            LengthFits := True;

          LineText := LineText + su;

//          if not LengthFits and not Wordwrap then
//          begin
//            LineBreak := true;
//          end;

          if LengthFits or not WordWrap then
          begin
            Res := Res + Copy(s,1,WordLen);

            //if not LengthFits and Calc and (LineText <> su) then
            //  s := '';

            Delete(s,1,WordLen);

            if Length(su) >= WordLen then
            begin

              if Copy(su, WordLen, 1) = ' ' then
                sw := Canvas.TextWidth(' ')
              else
                sw := 0;
            end
            else
              sw := 0;
          end
          else
          begin
            LineBreak := True;
            w := w - WordWidth;
          end;
        end;
      end;

      TagPos := Pos('<',s);

      if (TagPos = 1) and (Length(s) <= 2) then
        s := '';

      if not LineBreak and (TagPos = 1) and (Length(s) > 2) then
      begin
        {$IFDEF DELPHI_LLVM}
        if (s[1] = '/') and (Length(s) > 3) then
        {$ELSE}
        if (s[2] = '/') and (Length(s) > 3) then
        {$ENDIF}
        begin
          {$IFDEF DELPHI_LLVM}
          case UpCase(s[2]) of
          {$ELSE}
          case UpCase(s[3]) of
          {$ENDIF}
          'A':begin
                if (not HoverStyle or (Hoverlink = Hyperlinks)) and not Calc then
                begin
                  Canvas.Font.Style := Canvas.Font.Style - [TFontStyle.fsUnderline];
                  if Hovercolor <> claNull then
                    BkColor := HoverColor;
                  if HoverFontColor <> claNull then
                    FontColor := HoverFontColor;
                end;

                if not Selected then
                  FontColor := OldFontColor;

                Anchor := False;

                if MouseInAnchor then
                begin
                  hr.Bottom := r.Bottom;
                  hr.Right := r.Left + w;
                  if r.Top <> hr.Top then
                  begin
                    hr.Left := r.Left;
                    hr.Top := r.Top;
                  end;

                  HoverRect := hr;
                  MouseLink := HyperLinks;
                  {$IFDEF TMSDEBUG}
                  DbgRect('hotspot anchor '+lastanchor,hr);
                  {$ENDIF}
                  MouseInAnchor := False;
                end;

                if Focuslink = Hyperlinks - 1 then
                begin
                  rr.Right := cr.Left;
                  rr.Bottom := cr.Bottom - ImgAli;
                  rr.Top := rr.Bottom - Canvas.TextHeight('gh');
                  InflateRect(rr,1,0);
                  //if not Calc then Canvas.DrawFocusRect(rr);
                end;
              end;
          'E':begin
                if not Calc then
                  Error := False;
              end;
          'B':begin
                {$IFDEF DELPHI_LLVM}
                if s[3] <> '>' then
                {$ELSE}
                if s[4] <> '>' then
                {$ENDIF}
                  FontColor := OldFontColor
                else
                  Canvas.Font.Style := Canvas.Font.Style - [TFontStyle.fsBold];
              end;
          'S':begin
                {$IFDEF DELPHI_LLVM}
                TagChar := UpCase(s[3]);
                {$ELSE}
                TagChar := UpCase(s[4]);
                {$ENDIF}

                if (TagChar = 'U') then
                begin
                  isSup := False;
                  isSub := False;
                end
                else
                 if (TagChar = 'H') then
                  isShad := False
                 else
                  Canvas.Font.Style := Canvas.Font.Style - [TFontStyle.fsStrikeOut];
              end;
          'F':begin
                Canvas.Font.Family := OldFont.Family;
                Canvas.Font.Size := OldFont.Size;
                if not Calc and not Selected then
                begin
                  FontColor := OldFontColor;
                  Canvas.Fill.Color := BGColor;
                end;
              end;
          'H':begin
                if not Calc then
                begin
                  FontColor := hifCol;
                  OldBkColor := BkColor;
                  BkColor := hibCol;
                  Canvas.Fill.Color := hibCol;
                end;
              end;
          'I':begin
                Canvas.Font.Style := Canvas.Font.Style - [TFontStyle.fsItalic];
              end;
          'L':begin
                LineBreak := True;
              end;
          'O':begin
                NewOffsetX := 0;
              end;
          'P':begin
                LineBreak := True;
                if not Calc then
                begin
                  Canvas.Fill.Color := ParaColor;
                  isPara := false;
                end;
              end;
          'U':begin
                {$IFDEF DELPHI_LLVM}
                if (s[3] <> '>') and (ListIndex > 0) then
                {$ELSE}
                if (s[4] <> '>') and (ListIndex > 0) then
                {$ENDIF}
                  Dec(Listindex)
                else
                  Canvas.Font.Style := Canvas.Font.Style - [TFontStyle.fsUnderline];
              end;
          'R':begin
                //EndRotated(Canvas);
              end;
          'Z':Invisible := False;
          end;
        end
        else
        begin
          {$IFDEF DELPHI_LLVM}
          case Upcase(s[1]) of
          {$ELSE}
          case Upcase(s[2]) of
          {$ENDIF}
          'A':begin
                // only do this when at hover position in xpos,ypos
                if (FocusLink = HyperLinks) and not Calc then
                begin
                  rr.Left := cr.Left;
                  rr.Top := cr.Top;
                end;

                Inc(HyperLinks);
                if (not HoverStyle or (Hoverlink = HyperLinks)) and not Calc then
                begin
                  Canvas.Font.Style := Canvas.Font.Style + [TFontStyle.fsUnderline];
                  if (Hovercolor <> claNull) and not Calc then
                  begin
                    HvrColor := Canvas.Fill.Color;
                    BkColor := HoverColor;
                  end;

                  if HoverFontColor <> claNull then
                  begin
                    hvrfntcolor := FontColor;
                    FontColor := HoverFontColor;
                  end;
                end;

                if not Selected and ((HoverFontColor = claNull) or (HoverLink <> HyperLinks) or not HoverStyle) then
                  FontColor := URLColor;

                TagProp := Copy(s,3,Pos('>',s) - 1);  // <A href="anchor">
                Prop := Copy(TagProp,Pos('"',TagProp) + 1,Length(TagProp));
                Prop := Copy(Prop,1,Pos('"',Prop) - 1);
                LastAnchor := Prop;
                Anchor := True;

                hr.Left := w;
                hr.Top := r.Top;
              end;
          'B':begin
                {$IFDEF DELPHI_LLVM}
                TagChar := Upcase(s[2]);
                {$ELSE}
                TagChar := Upcase(s[3]);
                {$ENDIF}
                case TagChar of
                '>': Canvas.Font.Style := Canvas.Font.Style + [TFontStyle.fsBold]; // <B> tag
                'R': // <BR> tag
                   begin
                    LineBreak := true;
                    StripVal := StripVal + #13;
                   end;
                'L': if not Blink then
                   FontColor := BlnkColor; // <BLINK> tag
                'O':
                  begin

                    bmpy := 0;
                    Res := Res + Copy(s,1,pos('>',s));
                    if not Calc and not Selected then
                    begin
                      TagProp := Uppercase(Copy(s,6,pos('>',s)-1));

                      if (Pos('BACKGROUND',TagProp) > 0) then
                      begin
                        Prop := Copy(TagProp,Pos('BACKGROUND',TagProp)+10,Length(TagProp));
                        Prop := Copy(Prop,Pos('"',Prop)+1,Length(prop));
                        Prop := Copy(Prop,1,Pos('"',Prop)-1);

                        if Pos('IDX:', UpperCase(Prop)) > 0 then
                          begin
                            Delete(Prop, 1, 4);
                            (*
                            if Assigned(FImages) and (IStrToInt(Prop) < FImages.Count) then
                              begin
                                IMGSize.X := MulDiv(FImages.Width, GetDeviceCaps(Canvas.Handle, LOGPIXELSX), 96);
                                IMGSize.Y := MulDiv(FImages.Height, GetDeviceCaps(Canvas.Handle, LOGPIXELSY), 96);

                                if not Calc and not Print then
                                  FImages.Draw(Canvas, CR.Left, CR.Top, IStrToInt(Prop), True);

                                if not Calc and Print then
                                  begin
                                    CR.Right := CR.Left + Round(resfactor * FImages.Width);
                                    CR.Bottom := CR.Top + Round(resfactor * FImages.Height);

                                    ABitmap := TBitmap.Create;
                                    FImages.GetBitmap(IStrToInt(Prop), ABitmap);
                                    PrintBitmap(Canvas, CR, ABitmap);
                                    ABitmap.Free;
                                    CR := r;
                                  end;
                              end;
                            *)
                          end;

                        if Pos('SSYS:', UpperCase(Prop)) > 0 then
                          begin
                            Delete(Prop, 1, 5);
                            (*
                            IMGSize := SysImage(Canvas, CR.Left, CR.Top, Prop, False, not Calc, Print, resfactor);

                            IMGSize.X := MulDiv(IMGSize.X, GetDeviceCaps(Canvas.Handle, LOGPIXELSX), 96);
                            IMGSize.Y := MulDiv(IMGSize.Y, GetDeviceCaps(Canvas.Handle, LOGPIXELSY), 96);
                            *)
                          end;

                        if Pos('LSYS:', UpperCase(Prop)) > 0 then
                          begin
                            Delete(Prop, 1, 5);
                            (*
                            IMGSize := SysImage(Canvas, CR.Left, CR.Top, Prop, True, not Calc, Print, resfactor);

                            IMGSize.X := MulDiv(IMGSize.X, GetDeviceCaps(Canvas.Handle, LOGPIXELSX), 96);
                            IMGSize.Y := MulDiv(IMGSize.Y, GetDeviceCaps(Canvas.Handle, LOGPIXELSY), 96);
                            *)
                          end;

                        bmp := nil;

                        if (Pos(':',Prop) = 0) and Assigned(BitmapContainer) then
                        begin
                          bmp := BitmapContainer.FindBitmap(Prop);
                        end;


                        if Assigned(bmp) then
                        begin
                          if not bmp.IsEmpty and (bmp.Width > 0) and (bmp.Height > 0) then
                          begin
                            // do the tiling here
                            //hrgn := CreateRectRgn(fr.left, fr.top, fr.right,fr.bottom);
                            //SelectClipRgn(Canvas.Handle, hrgn);
                            while (bmpy < fr.bottom-fr.top) do
                            begin
                              bmpx := 0;
                              while (bmpx < fr.right - fr.left) do
                              begin
                                Canvas.DrawBitmap(bmp, RectF(0,0,bmp.Width, bmp.Height), RectF(fr.left+bmpx,fr.top+bmpy, fr.Left + bmpx + bmp.Width,fr.top+bmpy + bmp.Height),Opacity);
                                //Canvas.Draw(fr.left+bmpx,fr.top+bmpy,bmp);
                                bmpx := bmpx + bmp.width;
                              end;
                              bmpy := bmpy + bmp.height;
                            end;
                            //SelectClipRgn(Canvas.handle, 0);
                            //DeleteObject(hrgn);
                          end;
                        end; //end of bmp <> nil
                      end; //end of background

                      if (Pos('BGTOPLEFT', TagProp) > 0) then
                        begin
                          Prop := Copy(TagProp, Pos('BGTOPLEFT', TagProp) + 10, Length(TagProp));
                          Prop := Copy(Prop, Pos('"', Prop) + 1, Length(Prop));
                          Prop := Copy(Prop, 1, Pos('"', Prop) - 1);

                          bmp := nil;

                          if (Pos(':', Prop) = 0) and Assigned(BitmapContainer) then
                            begin
                              bmp := BitmapContainer.FindBitmap(Prop);
                            end;

                          if Assigned(bmp) then
                          begin
                            if not bmp.IsEmpty and (bmp.Width > 0) and (bmp.Height > 0) then
                            begin
                              Canvas.DrawBitmap(bmp, RectF(0,0,bmp.Width, bmp.Height), RectF(fr.Left, fr.Top + bmpy,fr.Left + bmp.Width ,fr.Top + bmp.Height),Opacity);
                            end;
                          end;
                        end; //end of bgtopleft

                      if (Pos('BGTOPRIGHT', TagProp) > 0) then
                        begin
                          Prop := Copy(TagProp, Pos('BGTOPRIGHT', TagProp) + 10, Length(TagProp));
                          Prop := Copy(Prop, Pos('"', Prop) + 1, Length(Prop));
                          Prop := Copy(Prop, 1, Pos('"', Prop) - 1);

                          bmp := nil;

                          if (Pos(':', Prop) = 0) and Assigned(BitmapContainer) then
                            begin
                              bmp := BitmapContainer.FindBitmap(Prop);
                            end;


                          if Assigned(bmp) then
                          begin
                            if not bmp.IsEmpty and (bmp.Width > 0) and (bmp.Height > 0) then
                            begin
                              Canvas.DrawBitmap(bmp, RectF(0,0,bmp.Width, bmp.Height), RectF(fr.Right - bmp.Width, fr.Top + bmpy, fr.Right, fr.Top + bmp.Height ),Opacity);
                            end;
                          end;
                        end; //end of bgtopright

                      if VarPos('BGCOLOR',TagProp,TagPos) > 0 then
                      begin
                        Prop := Copy(TagProp,TagPos + 5,Length(TagProp));
                        Prop := Copy(Prop,Pos('"',Prop) + 1,Length(Prop));
                        Prop := Copy(Prop,1,Pos('"',Prop) - 1);

                        NewColor := claNull;

                        if Length(Prop) > 0 then
                        begin
                          {$IFDEF DELPHI_LLVM}
                          if Prop[0] = '#' then
                          {$ELSE}
                          if Prop[1] = '#' then
                          {$ENDIF}
                            NewColor := Hex2Color(Prop)
                          else
                            NewColor := Text2Color(AnsiLowerCase(prop));
                        end;

                        if VarPos('BGCOLORTO',TagProp,TagPos) > 0 then
                        begin
                          Prop := Copy(TagProp,TagPos + 5,Length(TagProp));
                          Prop := Copy(Prop,Pos('"',Prop) + 1,Length(Prop));
                          Prop := Copy(Prop,1,Pos('"',Prop) - 1);

                          Prop := 'H';
                          if VarPos('DIR',TagProp,TagPos) > 0 then
                          begin
                            Prop := Copy(TagProp,TagPos + 3,Length(TagProp));
                            Prop := Copy(Prop,Pos('"',Prop) + 1,Length(Prop));
                            Prop := Copy(Prop,1,Pos('"',Prop) - 1);
                          end;

                          Canvas.Stroke.Color := Newcolor;
                          {$IFDEF DELPHIXE6_LVL}
                          Canvas.Fill.Gradient.Style := TGradientStyle.Linear;
                          {$ELSE}
                          Canvas.Fill.Gradient.Style := TGradientStyle.gsLinear;
                          {$ENDIF}
                          //DrawHTMLGradient(Canvas,NewColor,NewColorTo,clNone,64,Rect(fr.left,fr.top,fr.right,fr.bottom),Prop = 'H');
                          //Canvas.Brush.Style := bsClear
                        end
                        else
                        begin
                          BGColor := Canvas.Fill.Color;
                          Canvas.Fill.Color := NewColor;
                          PenColor:=Canvas.Stroke.Color;
                          Canvas.Stroke.Color := Newcolor;
                          Canvas.FillRect(Rectf(fr.left - 2,fr.top,fr.right,fr.bottom),0,0,[],Opacity);
                          Canvas.Stroke.Color := PenColor;
                        end;
                      end;
                    end;
                  end;
                end;
              end;
          'E':begin
                if not Calc then
                  Error := True;
              end;
          'H':begin
                {$IFDEF DELPHI_LLVM}
                case Upcase(s[2]) of
                {$ELSE}
                case Upcase(s[3]) of
                {$ENDIF}
                'R':
                begin
                  LineBreak := True;
                  if not Calc then
                  begin
                    TagProp := Copy(s,4,Pos('>',s) - 1);

                    NewColor := claBlack;

                    if VarPos('COLOR',Uppercase(TagProp),TagPos) > 0 then
                    begin
                      Prop := Copy(TagProp,TagPos+5,Length(TagProp));
                      Prop := Copy(Prop,Pos('"',prop)+1,Length(Prop));
                      Prop := Copy(Prop,1,Pos('"',prop)-1);

                      if Length(Prop) > 0 then
                      begin
                        {$IFDEF DELPHI_LLVM}
                        if Prop[0] = '#' then
                        {$ELSE}
                        if Prop[1] = '#' then
                        {$ENDIF}
                          NewColor := Hex2Color(Prop)
                        else
                          NewColor := Text2Color(AnsiLowerCase(prop));
                      end;
                    end;

                    Pencolor := Canvas.Stroke.color;
                    Canvas.Stroke.Color := Newcolor;
                    ptf1 := PointF(r.Left, cr.Bottom  + 1);
                    ptf2 := PointF(r.Right, cr.Bottom + 1);
                    Canvas.DrawLine(ptf1,ptf2,Opacity);
                    Canvas.Stroke.Color := Pencolor;
                  end;
                end;
                'I':
                begin
                  if not Calc then
                  begin
                    hifCol := FontColor;
                    hibCol := BkColor;
                    BkColor := claBlue;
                    FontColor := claWhite;
                  end;
                end;
                end;
              end;
          'I':begin
                {$IFDEF DELPHI_LLVM}
                TagChar := Upcase(s[2]);
                {$ELSE}
                TagChar := Upcase(s[3]);
                {$ENDIF}

                if TagChar = '>' then // <I> tag
                  Canvas.Font.Style := Canvas.Font.Style + [TFontStyle.fsItalic]
                else
                if TagChar = 'N' then  // <IND> tag
                begin
                  TagProp := Copy(s,3,pos('>',s) - 1);

                  Prop := Copy(TagProp,ipos('x',TagProp) + 2,Length(TagProp));
                  Prop := Copy(Prop,Pos('"',Prop) + 1,Length(prop));
                  Prop := Copy(Prop,1,Pos('"',Prop) - 1);

                  val(Prop,indent,err);
                  if err = 0 then
                  begin
                    if indent > w then
                     begin
                       w := Indent;
                       cr.left := fr.left + Indent;
                     end;
                  end;
                end
                else
                  if TagChar = 'M' then
                  begin
                    inc(ImgIdx);

                    TagProp := Copy(s,3,Pos('>',s) - 1);
                    Prop := Copy(TagProp,Pos('SRC',Uppercase(TagProp)) + 4,Length(TagProp));
                    Prop := Copy(Prop,Pos('"',Prop) + 1,Length(Prop));
                    Prop := Copy(Prop,1,Pos('"',Prop) - 1);

                    TagProp := Uppercase(TagProp);

                    if (Pos('ALT',TagProp) > 0) and (AltImg = ImgIdx) then
                    begin
                      Prop := Copy(TagProp,Pos('ALT',TagProp) + 4,Length(TagProp));
                      Prop := Copy(Prop,Pos('"',Prop) + 1,Length(Prop));
                      Prop := Copy(Prop,1,Pos('"',Prop) - 1);
                    end;

                    TagWidth := 0;
                    TagHeight := 0;

                    if Pos('WIDTH',TagProp) > 0 then
                    begin
                      Tagp := Copy(TagProp,Pos('WIDTH',TagProp) + 6,Length(TagProp));
                      Tagp := Copy(Tagp,Pos('"',tagp) + 1,Length(Tagp));
                      Tagp := Copy(Tagp,1,Pos('"',tagp) - 1);
                      Val(Tagp,TagWidth,Err);
                    end;

                    if Pos('HEIGHT',TagProp) > 0 then
                    begin
                      Tagp := Copy(TagProp,ipos('HEIGHT',TagProp) + 7,Length(TagProp));
                      Tagp := Copy(Tagp,pos('"',Tagp) + 1,Length(Tagp));
                      Tagp := Copy(Tagp,1,pos('"',Tagp) - 1);
                      Val(Tagp,TagHeight,Err);
                    end;

                    IMGSize.x := 0;
                    IMGSize.y := 0;

                    bmp := nil;

                    if (Pos(':',Prop) = 0) and Assigned(BitmapContainer) then
                    begin
                      bmp := BitmapContainer.FindBitmap(Prop);
                    end;

                    imgw := 0;
                    imgh := 0;


                      if Assigned(bmp) then
                      begin
                        if not bmp.IsEmpty then
                        begin
                          if (TagWidth > 0) and (TagHeight > 0) then
                          begin
                            imgw := TagWidth;
                            imgh := TagHeight;
                          end;
                          if (TagWidth > 0) and (TagHeight = 0) then
                          begin
                            imgw := TagWidth;
                            imgh := Round(TagWidth/bmp.Width * bmp.Height);
                          end;
                          if (TagWidth = 0) and (TagHeight > 0) then
                          begin
                            imgw := Round(TagHeight/bmp.Height * bmp.Width);
                            imgh := TagHeight;
                          end;
                          if (TagWidth = 0) and (TagHeight = 0) then
                          begin
                            imgw := bmp.Width;
                            imgh := bmp.Height;
                          end;

                          if not Calc {and not Print} then
                          begin
                            if (TagWidth > 0) or (TagHeight > 0) then
                            begin
                              Canvas.DrawBitmap(bmp,RectF(0,0,bmp.Width,bmp.Height),RectF(cr.Left,cr.Top,cr.Left + imgw,cr.Top + imgh),Opacity);
                            end
                            else
                            if (TagWidth > 0) and (TagHeight = 0) then
                            begin
                              Canvas.DrawBitmap(bmp,RectF(0,0,bmp.Width,bmp.Height),RectF(cr.Left,cr.Top,cr.Left + TagWidth,cr.Top + TagHeight),Opacity);
                            end
                            else
                            begin
                              Canvas.DrawBitmap(bmp,RectF(0,0,bmp.Width,bmp.Height),RectF(cr.Left,cr.Top,cr.Left + bmp.Width, cr.Top + bmp.Height),Opacity);
                            end;
                          end;

                          if (TagWidth > 0) or (TagHeight > 0) then
                          begin
                            IMGSize.x := imgw;
                            IMGSize.y := imgh;
                          end
                          else
                          begin
                            IMGSize.x := bmp.Width;
                            IMGSize.y := bmp.Height;
                          end;
                        end;
                      end;

                    if (XPos - r.Left > w) and (XPos - r.Left < w + IMGSize.x) and
                       (YPos > cr.Top) and (YPos < cr.Top + IMGSize.Y) and Anchor then
                    begin
                      ImageHotSpot := True;
                      AnchorVal := LastAnchor;
                      AltImg := ImgIdx;
                    end;

                    oldh := h;

                    if (w + IMGSize.x > r.Right-r.Left) and
                       (IMGSize.x < r.Right - r.Left) then
                    begin
                      ImgBreak := True;
                    end
                    else
                      begin
                        w := w + IMGSize.x;
                        cr.left := cr.left + IMGSize.x;
                        if IMGSize.y > h then
                          h := IMGSize.y;
                      end;

                    if Pos('ALIGN',TagProp) > 0 then
                    begin
                      if Pos('"TOP',TagProp) > 0 then
                      begin
                        ImgAli := h - Canvas.TextHeight('gh');
                      end
                      else
                      begin
                        if Pos('"MIDDLE',TagProp) > 0 then
                          ImgAli := (h - Canvas.TextHeight('gh')) / 2;
                      end;
                    end;

                    if (Pos('WRAP',TagProp) > 0) then
                    begin
                      h := Canvas.TextHeight('gh');
                      ImgAli := 0;
                    end;
                  end;
                end;
          'L':begin
                NewColor := claNull;
                LType := '';

                if (VarPos('>',s,TagPos)>0) then
                begin
                  TagProp := Uppercase(Copy(s,3,TagPos-1));

                  if VarPos('TYPE',TagProp,TagPos) > 0 then
                  begin
                    Prop := Copy(TagProp,TagPos+4,Length(TagProp));
                    Prop := Copy(Prop,Pos('"',prop)+1,Length(Prop));
                    Prop := Copy(Prop,1,Pos('"',prop)-1);
                    LType := Prop;
                  end;
                  if VarPos('COLOR',TagProp,TagPos) > 0 then
                  begin
                    Prop := Copy(TagProp,TagPos+5,Length(TagProp));
                    Prop := Copy(Prop,Pos('"',prop)+1,Length(Prop));
                    Prop := Copy(Prop,1,Pos('"',prop)-1);

                    if Length(Prop) > 0 then
                    begin
                      {$IFDEF DELPHI_LLVM}
                      if Prop[0] = '#' then
                      {$ELSE}
                      if Prop[1] = '#' then
                      {$ENDIF}
                        NewColor := Hex2Color(Prop)
                      else
                        NewColor := Text2Color(AnsiLowerCase(prop));
                    end;
                  end;
                end;

                w := w + 12 * ListIndex;
                if Linkbreak then
                  Imgbreak := True else Linkbreak := True;

                cr.left := cr.left + 12 * (ListIndex - 1);

                if not calc and not Invisible then
                begin
                  if LType = '' then
                  begin
                    if (NewColor <> claNull) then
                    begin
                      ColL := Canvas.Fill.Color;
                      FontColor := NewColor;
                    end;
                    Prop := Canvas.Font.Family;
                    Canvas.Font.Family := 'Symbol';

                    {$IFDEF DELPHIXE6_LVL}
                    if Odd(ListIndex) then
                      Canvas.FillText(cr,'·',false,Opacity,[],TTextAlign.Leading)
                    else
                      Canvas.FillText(cr,'o',false,Opacity,[],TTextAlign.Leading);
                    {$ELSE}
                    if Odd(ListIndex) then
                      Canvas.FillText(cr,'·',false,Opacity,[],TTextAlign.taLeading)
                    else
                      Canvas.FillText(cr,'o',false,Opacity,[],TTextAlign.taLeading);
                    {$ENDIF}

                    Canvas.Font.Family := prop;
                    if NewColor <> claNull then
                      FontColor := ColL;
                  end
                  else
                  begin
                    ColB := claNull;

                    if LTYPE = 'SQUARE' then
                    begin
                      ColB := Canvas.Fill.Color;
                      Canvas.Stroke.Color := NewColor;
                      Canvas.Fill.Color := NewColor;
                      Canvas.FillRect(RectF(cr.Left, cr.Top + 2, cr.Left + 8, cr.Top + 10),0,0,[],Opacity);
                      Canvas.Fill.Color := ColB;
                    end;

                    if LTYPE = 'CIRCLE' then
                    begin
                      ColB := Canvas.Fill.Color;
                      Canvas.Stroke.Color := NewColor;
                      Canvas.Fill.Color := NewColor;
                      Canvas.FillEllipse(RectF(cr.Left, cr.Top + 2, cr.Left + 8, cr.Top + 10),Opacity);
                      Canvas.Fill.Color := ColB;
                    end;
                  end;
                end;
                cr.Left := cr.Left + 12;
              end;
          'U':begin
                {$IFDEF DELPHI_LLVM}
                if s[2] <> '>' then
                {$ELSE}
                if s[3] <> '>' then
                {$ENDIF}
                begin
                  Inc(ListIndex);
                  LineBreak := true;
                end
                else
                  Canvas.Font.Style := Canvas.Font.Style + [TFontStyle.fsUnderline];
              end;
          'O':begin
                {$IFDEF DELPHI_LLVM}
                TagChar := Upcase(s[2]);
                {$ELSE}
                TagChar := Upcase(s[3]);
                {$ENDIF}
                if TagChar = 'F' then  // <OFS> tag
                begin
                  TagProp := Copy(s,3,pos('>',s) - 1);
                  Prop := Copy(TagProp,ipos('x',TagProp) + 2,Length(TagProp));
                  Prop := Copy(Prop,Pos('"',Prop) + 1,Length(prop));
                  Prop := Copy(Prop,1,Pos('"',Prop) - 1);
                  val(Prop,NewOffsetX,err);
                  cr.Left := NewOffsetX;
                  w := NewOffsetX;
                end
              end;
          'P':begin
                if (VarPos('>',s,TagPos)>0) then
                begin
                  TagProp := Uppercase(Copy(s,3,TagPos-1));

                  if VarPos('ALIGN',TagProp,TagPos) > 0 then
                  begin
                    Prop := Copy(TagProp,TagPos+5,Length(TagProp));
                    Prop := Copy(Prop,Pos('"',prop)+1,Length(Prop));
                    Prop := Copy(Prop,1,Pos('"',prop)-1);

                    if Pos('RIGHT',Prop) > 0 then Align := taRightJustify;
                    if Pos('LEFT',Prop) > 0 then Align := taLeftJustify;
                    if Pos('CENTER',Prop) > 0 then Align := taCenter;
                  end;

                  if VarPos('INDENT',TagProp,TagPos) > 0 then
                  begin
                    Prop := Copy(TagProp,TagPos+6,Length(TagProp));
                    Prop := Copy(Prop,Pos('"',prop)+1,Length(Prop));
                    Prop := Copy(Prop,1,Pos('"',prop)-1);
                    PIndent := IStrToInt(Prop);
                  end;

                  if VarPos('BGCOLOR',TagProp,TagPos) > 0 then
                  begin
                    Prop := Copy(TagProp,TagPos + 5,Length(TagProp));
                    Prop := Copy(Prop,Pos('"',Prop) + 1,Length(Prop));
                    Prop := Copy(Prop,1,Pos('"',Prop) - 1);

                    NewColor := claNull;

                    if Length(Prop) > 0 then
                    begin
                      {$IFDEF DELPHI_LLVM}
                      if Prop[0] = '#' then
                      {$ELSE}
                      if Prop[1] = '#' then
                      {$ENDIF}
                        NewColor := Hex2Color(Prop)
                      else
                        NewColor := Text2Color(AnsiLowerCase(prop));
                    end;

                    if VarPos('BGCOLORTO',TagProp,TagPos) > 0 then
                    begin
                      Prop := Copy(TagProp,TagPos + 5,Length(TagProp));
                      Prop := Copy(Prop,Pos('"',Prop) + 1,Length(Prop));
                      Prop := Copy(Prop,1,Pos('"',Prop) - 1);

                      if not Calc then
                      begin
                        isPara := True;
                        Canvas.Stroke.Color := Newcolor;

                        //DrawHTMLGradient(Canvas,NewColor,NewColorTo,clNone,64,Rect(fr.left,r.top,fr.right,r.bottom+2),true);
                        //Canvas.Brush.Style := bsClear
                      end;
                    end
                    else
                    begin
                      if not Calc then
                      begin
                        isPara := True;
                        paracolor := Canvas.Fill.Color;
                        //if Canvas.Brush.Style = bsClear then ParaColor := clNone;
                        Canvas.Fill.color := NewColor;
                        PenColor := Canvas.Stroke.Color;
                        Canvas.Stroke.Color := Newcolor;
                        Canvas.FillRect(RectF(fr.left,r.top,fr.right,r.bottom),0,0,[],Opacity);
                      end;
                    end;
                  end;
                end;
            end;
        'F':begin
              if (VarPos('>',s,TagPos)>0) then
              begin
                TagProp := UpperCase(Copy(s,6,TagPos-6));

                if (VarPos('FACE',TagProp,TagPos) > 0) then
                begin
                  Prop := Copy(TagProp,TagPos+4,Length(TagProp));
                  Prop := Copy(prop,pos('"',prop)+1,Length(prop));
                  Prop := Copy(prop,1,pos('"',prop)-1);
                  Canvas.Font.Family := Prop;
                end;

                if (VarPos(' COLOR',TagProp,TagPos) > 0) and not Selected then
                begin
                  Prop := Copy(TagProp,TagPos+6,Length(TagProp));
                  Prop := Copy(Prop,Pos('"',prop)+1,Length(prop));
                  Prop := Copy(Prop,1,Pos('"',prop)-1);
                  OldFontColor := Canvas.Fill.Color;

                  if Length(Prop) > 0 then
                  begin
                    {$IFDEF DELPHI_LLVM}
                    if Prop[0] = '#' then
                    {$ELSE}
                    if Prop[1] = '#' then
                    {$ENDIF}
                      FontColor := Hex2Color(Prop)
                    else
                      FontColor := Text2Color(AnsiLowerCase(Prop));
                  end;
                end;

                if (VarPos('BGCOLOR',TagProp,TagPos)>0) and not Calc and not Selected then
                begin
                  Prop := Copy(TagProp,TagPos+7,Length(TagProp));
                  Prop := Copy(prop,pos('"',prop)+1,Length(prop));
                  Prop := Copy(prop,1,pos('"',prop)-1);
                  OldBkColor := BkColor;

                  if Length(Prop) > 0 then
                  begin
                    {$IFDEF DELPHI_LLVM}
                    if Prop[0] = '#' then
                    {$ELSE}
                    if Prop[1] = '#' then
                    {$ENDIF}
                      BkColor := Hex2Color(Prop)
                    else
                      BkColor := Text2Color(AnsiLowerCase(prop));
                  end;

                end;

                if (VarPos('SIZE',TagProp,TagPos)>0) then
                begin
                  Prop := Copy(TagProp,TagPos+4,Length(TagProp));
                  Prop := Copy(Prop,Pos('=',Prop)+1,Length(Prop));
                  Prop := Copy(Prop,Pos('"',Prop)+1,Length(Prop));

                  case IStrToInt(Prop) of
                  1:Canvas.Font.Size := 8;
                  2:Canvas.Font.Size := 10;
                  3:Canvas.Font.Size := 12;
                  4:Canvas.Font.Size := 14;
                  5:Canvas.Font.Size := 16;
                  else
                    Canvas.Font.Size := IStrToInt(Prop);
                  end;

                end;
              end;
            end;
        'S':begin
              {$IFDEF DELPHI_LLVM}
              TagChar := Upcase(s[2]);
              {$ELSE}
              TagChar := Upcase(s[3]);
              {$ENDIF}

              if TagChar = '>' then
                Canvas.Font.Style := Canvas.font.Style + [TFontStyle.fsStrikeOut]
              else
              begin
                if TagChar = 'H' then
                  isShad := True
                else
                begin
                  if ipos('<SUB>',s)=1 then
                    isSub := True
                  else
                    if ipos('<SUP>',s)=1 then
                      isSup := True;
                end;
              end;
            end;
        'R':begin
              TagProp := Copy(s,3,pos('>',s)-1);
              prop := Copy(TagProp,ipos('a',TagProp)+2,Length(TagProp));
              prop := Copy(prop,pos('"',prop)+1,Length(prop));
              prop := Copy(prop,1,pos('"',prop)-1);
              Val(prop,Indent,err);
              //StartRotated(Canvas,indent);
            end;
        'Z':Invisible := True;
        end;
      end;

      if (VarPos('>',s,TagPos)>0) and not ImgBreak then
      begin
        Res := Res + Copy(s,1,TagPos);
        Delete(s,1,TagPos);
      end
      else
        if not Imgbreak then
          Delete(s,1,Length(s));
    end;
  end;

    w := w - sw;

    if w > xsize then
      xsize := w + 2;

    if (FocusLink = Hyperlinks - 1) and Anchor and not Calc then
    begin
      rr.Right := cr.Left;
      rr.Bottom := cr.Bottom;
      InflateRect(rr,1,0);
      //if not Calc then
      //  Canvas.DrawFocusRect(rr);
      rr.Left := r.Left + 1;
      rr.Top := rr.Bottom;
    end;

    Result := Res;
  end;

begin
  Anchor := False;
  Error := False;
  OldFont := TFont.Create;
  OldFont.Assign(Canvas.Font);
  OldFontColor := Canvas.Fill.Color;
  DrawFont := TFont.Create;
  DrawFont.Assign(Canvas.Font);
  DrawFontColor := Canvas.Fill.Color;
  CalcFont := TFont.Create;
  CalcFont.Assign(Canvas.Font);
  CalcFontColor := Canvas.Fill.Color;
  OldDrawfont := TFont.Create;
  OldDrawFont.Assign(Canvas.Font);
  OldDrawFontColor := Canvas.Fill.Color;
  OldCalcFont := TFont.Create;
  OldCalcFont.Assign(Canvas.Font);
  OldCalcFontColor := Canvas.Fill.Color;
  BlnkColor := Canvas.Fill.color;
  OldPenColor := Canvas.Stroke.Color;
  BGColor := claNull;
  BkColor := claNull;
  ParaColor := claNull;
  isPara := False;
  isShad := False;
  Invisible := False;

  OldFontColor := Canvas.Fill.Color;

  OfsX := 0;
  NewOfsX := 0;

  Result := False;

  r := fr;

  Align := taLeftJustify;
  PIndent := 0;

  XSize := 0;
  YSize := 0;
  HyperLinks := 0;
  HlCount := 0;
  ListIndex := 0;
  LiCount := 0;
  StripVal := '';
  FocusAnchor := '';
  MouseLink := -1;
  MouseInAnchor := False;

  ImgIdx := 0;
  AltImg := -1;

  if Pos('&',s) > 0 then
  begin
    repeat
      Foundtag := False;

      if TagReplacestring('&amp;','&&',s) then Foundtag := True;
      if TagReplacestring('&quot;','"',s) then Foundtag := True;

      if TagReplacestring('&sect;','§',s) then Foundtag := True;
      if TagReplacestring('&permil;','®‰',s) then Foundtag := True;
      if TagReplacestring('&reg;','®',s) then Foundtag := True;

      if TagReplacestring('&copy;','©',s) then Foundtag := True;
      if TagReplacestring('&para;','¶',s) then Foundtag := True;

      if TagReplacestring('&trade;','™',s) then Foundtag := True;
      if TagReplacestring('&euro;','€',s) then Foundtag := True;

    until not Foundtag;
  end;

  s := DBTagStrip(s);
  s := CRLFStrip(s,True);

  InsPoint := 0;

  while Length(s) > 0 do
  begin
    //calculate part of the HTML text fitting on the next line
    OldFont.Assign(OldCalcFont);
    OldFontColor := OldCalcFontColor;
    Canvas.Font.Assign(CalcFont);
    FontColor := CalcFontColor;
    OldAnchor := Anchor;
    OldAnchorVal := LastAnchor;
    suph := 0;
    subh := 0;
    imgali := 0;
    isSup := False;
    isSub := False;

    HtmlHeight := Canvas.TextHeight('gh');
    txtHeight := HtmlHeight;

    OldImgIdx := ImgIdx;

    s := Trim(s);

    su := HTMLDrawLine(Canvas,s,r,True,HtmlWidth,HtmlHeight,subh,suph,imgali,Align,PIndent,XPos,YPos,HotSpot,ImageHotSpot,ofsx,newofsx);

    Anchor := OldAnchor;
    LastAnchor := OldAnchorVal;

    CalcFont.Assign(Canvas.Font);
    CalcFontColor := Canvas.Fill.Color;
    OldCalcFont.Assign(OldFont);
    OldCalcFontColor := OldFontColor;

    HTMLHeight := HTMLHeight + LineSpacing;

    dr := r;

    case Align of
    taCenter:if (r.right - r.left - htmlwidth > 0) then
               dr.left := r.left+((r.right - r.left - htmlwidth) / 2);
    taRightJustify:if r.right - htmlwidth > r.left then
                       dr.left := r.right - htmlwidth;
    end;

    dr.Left := dr.Left + PIndent;

    dr.Bottom := dr.Top + HtmlHeight + Subh + Suph;

    if not CheckHeight then
    begin
      OldFont.Assign(OldDrawFont);
      OldFontColor := OldDrawFontColor;
      Canvas.Font.Assign(DrawFont);
      FontColor := DrawFontColor;

      HyperLinks := HlCount;
      ListIndex := LiCount;
      ImgIdx := OldImgIdx;

      HTMLDrawLine(Canvas,su,dr,CheckHotSpot,HtmlWidth,HtmlHeight,subh,suph,ImgAli,Align,PIndent,XPos,YPos,HotSpot,ImageHotspot,ofsx,newofsx);

      HlCount := HyperLinks;
      LiCount := ListIndex;

      if (HotSpot and
         (YPos > dr.Bottom - ImgAli - Canvas.TextHeight('gh')) and
         (YPos < dr.Bottom - ImgAli)) or ImageHotSpot then
      begin
        Result := True;
      end;

      ofsx := newofsx;

      DrawFont.Assign(Canvas.Font);
      DrawFontColor := FontColor;
      OldDrawFont.Assign(OldFont);
      OldDrawFontColor := OldFontColor;
    end;

    r.top := r.top + HtmlHeight + subh + suph;
    ysize := ysize + HtmlHeight + subh + suph;

    //do not draw below bottom
    if (r.top + TxtHeight > r.bottom + 1) and not CheckHeight then
      s := '';
  end;

  if (ysize = 0) then
    ysize := Canvas.TextHeight('gh');

  //ysize := ysize + 2;

  InsPoint := InsPoint shr 1;

  Canvas.Stroke.Color := OldPenColor;
  Canvas.Fill.Color := BlnkColor;
  Canvas.Font.Assign(OldFont);
  OldFont.Free;
  DrawFont.Free;
  CalcFont.Free;
  OldDrawfont.Free;
  OldCalcfont.Free;
end;

{$IFNDEF IOS}
{$IFNDEF ANDROID}
initialization
begin
  fcolorlookup := TStringList.Create;
  fcolorlookup.addobject('claaliceblue', tobject(claaliceblue));
  fcolorlookup.addobject('claantiquewhite', tobject(claantiquewhite));
  fcolorlookup.addobject('claaqua', tobject(claaqua));
  fcolorlookup.addobject('claaquamarine', tobject(claaquamarine));
  fcolorlookup.addobject('claazure', tobject(claazure));
  fcolorlookup.addobject('clabeige', tobject(clabeige));
  fcolorlookup.addobject('clabisque', tobject(clabisque));
  fcolorlookup.addobject('clablack', tobject(clablack));
  fcolorlookup.addobject('clablanchedalmond', tobject(clablanchedalmond));
  fcolorlookup.addobject('clablue', tobject(clablue));
  fcolorlookup.addobject('clablueviolet', tobject(clablueviolet));
  fcolorlookup.addobject('clabrown', tobject(clabrown));
  fcolorlookup.addobject('claburlywood', tobject(claburlywood));
  fcolorlookup.addobject('clacadetblue', tobject(clacadetblue));
  fcolorlookup.addobject('clachartreuse', tobject(clachartreuse));
  fcolorlookup.addobject('clachocolate', tobject(clachocolate));
  fcolorlookup.addobject('clacoral', tobject(clacoral));
  fcolorlookup.addobject('clacornflowerblue', tobject(clacornflowerblue));
  fcolorlookup.addobject('clacornsilk', tobject(clacornsilk));
  fcolorlookup.addobject('clacrimson', tobject(clacrimson));
  fcolorlookup.addobject('clacyan', tobject(clacyan));
  fcolorlookup.addobject('cladarkblue', tobject(cladarkblue));
  fcolorlookup.addobject('cladarkcyan', tobject(cladarkcyan));
  fcolorlookup.addobject('cladarkgoldenrod', tobject(cladarkgoldenrod));
  fcolorlookup.addobject('cladarkgray', tobject(cladarkgray));
  fcolorlookup.addobject('cladarkgreen', tobject(cladarkgreen));
  fcolorlookup.addobject('cladarkgrey', tobject(cladarkgrey));
  fcolorlookup.addobject('cladarkkhaki', tobject(cladarkkhaki));
  fcolorlookup.addobject('cladarkmagenta', tobject(cladarkmagenta));
  fcolorlookup.addobject('cladarkolivegreen', tobject(cladarkolivegreen));
  fcolorlookup.addobject('cladarkorange', tobject(cladarkorange));
  fcolorlookup.addobject('cladarkorchid', tobject(cladarkorchid));
  fcolorlookup.addobject('cladarkred', tobject(cladarkred));
  fcolorlookup.addobject('cladarksalmon', tobject(cladarksalmon));
  fcolorlookup.addobject('cladarkseagreen', tobject(cladarkseagreen));
  fcolorlookup.addobject('cladarkslateblue', tobject(cladarkslateblue));
  fcolorlookup.addobject('cladarkslategray', tobject(cladarkslategray));
  fcolorlookup.addobject('cladarkslategrey', tobject(cladarkslategrey));
  fcolorlookup.addobject('cladarkturquoise', tobject(cladarkturquoise));
  fcolorlookup.addobject('cladarkviolet', tobject(cladarkviolet));
  fcolorlookup.addobject('cladeeppink', tobject(cladeeppink));
  fcolorlookup.addobject('cladeepskyblue', tobject(cladeepskyblue));
  fcolorlookup.addobject('cladimgray', tobject(cladimgray));
  fcolorlookup.addobject('cladimgrey', tobject(cladimgrey));
  fcolorlookup.addobject('cladodgerblue', tobject(cladodgerblue));
  fcolorlookup.addobject('clafirebrick', tobject(clafirebrick));
  fcolorlookup.addobject('clafloralwhite', tobject(clafloralwhite));
  fcolorlookup.addobject('claforestgreen', tobject(claforestgreen));
  fcolorlookup.addobject('clafuchsia', tobject(clafuchsia));
  fcolorlookup.addobject('clagainsboro', tobject(clagainsboro));
  fcolorlookup.addobject('claghostwhite', tobject(claghostwhite));
  fcolorlookup.addobject('clagold', tobject(clagold));
  fcolorlookup.addobject('clagoldenrod', tobject(clagoldenrod));
  fcolorlookup.addobject('clagray', tobject(clagray));
  fcolorlookup.addobject('clagreen', tobject(clagreen));
  fcolorlookup.addobject('clagreenyellow', tobject(clagreenyellow));
  fcolorlookup.addobject('clagrey', tobject(clagrey));
  fcolorlookup.addobject('clahoneydew', tobject(clahoneydew));
  fcolorlookup.addobject('clahotpink', tobject(clahotpink));
  fcolorlookup.addobject('claindianred', tobject(claindianred));
  fcolorlookup.addobject('claindigo', tobject(claindigo));
  fcolorlookup.addobject('claivory', tobject(claivory));
  fcolorlookup.addobject('clakhaki', tobject(clakhaki));
  fcolorlookup.addobject('clalavender', tobject(clalavender));
  fcolorlookup.addobject('clalavenderblush', tobject(clalavenderblush));
  fcolorlookup.addobject('clalawngreen', tobject(clalawngreen));
  fcolorlookup.addobject('clalemonchiffon', tobject(clalemonchiffon));
  fcolorlookup.addobject('clalightblue', tobject(clalightblue));
  fcolorlookup.addobject('clalightcoral', tobject(clalightcoral));
  fcolorlookup.addobject('clalightcyan', tobject(clalightcyan));
  fcolorlookup.addobject('clalightgoldenrodyellow', tobject(clalightgoldenrodyellow));
  fcolorlookup.addobject('clalightgray', tobject(clalightgray));
  fcolorlookup.addobject('clalightgreen', tobject(clalightgreen));
  fcolorlookup.addobject('clalightgrey', tobject(clalightgrey));
  fcolorlookup.addobject('clalightpink', tobject(clalightpink));
  fcolorlookup.addobject('clalightsalmon', tobject(clalightsalmon));
  fcolorlookup.addobject('clalightseagreen', tobject(clalightseagreen));
  fcolorlookup.addobject('clalightskyblue', tobject(clalightskyblue));
  fcolorlookup.addobject('clalightslategray', tobject(clalightslategray));
  fcolorlookup.addobject('clalightslategrey', tobject(clalightslategrey));
  fcolorlookup.addobject('clalightsteelblue', tobject(clalightsteelblue));
  fcolorlookup.addobject('clalightyellow', tobject(clalightyellow));
  fcolorlookup.addobject('clalime', tobject(clalime));
  fcolorlookup.addobject('clalimegreen', tobject(clalimegreen));
  fcolorlookup.addobject('clalinen', tobject(clalinen));
  fcolorlookup.addobject('clamagenta', tobject(clamagenta));
  fcolorlookup.addobject('clamaroon', tobject(clamaroon));
  fcolorlookup.addobject('clamediumaquamarine', tobject(clamediumaquamarine));
  fcolorlookup.addobject('clamediumblue', tobject(clamediumblue));
  fcolorlookup.addobject('clamediumorchid', tobject(clamediumorchid));
  fcolorlookup.addobject('clamediumpurple', tobject(clamediumpurple));
  fcolorlookup.addobject('clamediumseagreen', tobject(clamediumseagreen));
  fcolorlookup.addobject('clamediumslateblue', tobject(clamediumslateblue));
  fcolorlookup.addobject('clamediumspringgreen', tobject(clamediumspringgreen));
  fcolorlookup.addobject('clamediumturquoise', tobject(clamediumturquoise));
  fcolorlookup.addobject('clamediumvioletred', tobject(clamediumvioletred));
  fcolorlookup.addobject('clamidnightblue', tobject(clamidnightblue));
  fcolorlookup.addobject('clamintcream', tobject(clamintcream));
  fcolorlookup.addobject('clamistyrose', tobject(clamistyrose));
  fcolorlookup.addobject('clamoccasin', tobject(clamoccasin));
  fcolorlookup.addobject('clanavajowhite', tobject(clanavajowhite));
  fcolorlookup.addobject('clanavy', tobject(clanavy));
  fcolorlookup.addobject('claoldlace', tobject(claoldlace));
  fcolorlookup.addobject('claolive', tobject(claolive));
  fcolorlookup.addobject('claolivedrab', tobject(claolivedrab));
  fcolorlookup.addobject('claorange', tobject(claorange));
  fcolorlookup.addobject('claorangered', tobject(claorangered));
  fcolorlookup.addobject('claorchid', tobject(claorchid));
  fcolorlookup.addobject('clapalegoldenrod', tobject(clapalegoldenrod));
  fcolorlookup.addobject('clapalegreen', tobject(clapalegreen));
  fcolorlookup.addobject('clapaleturquoise', tobject(clapaleturquoise));
  fcolorlookup.addobject('clapalevioletred', tobject(clapalevioletred));
  fcolorlookup.addobject('clapapayawhip', tobject(clapapayawhip));
  fcolorlookup.addobject('clapeachpuff', tobject(clapeachpuff));
  fcolorlookup.addobject('claperu', tobject(claperu));
  fcolorlookup.addobject('clapink', tobject(clapink));
  fcolorlookup.addobject('claplum', tobject(claplum));
  fcolorlookup.addobject('clapowderblue', tobject(clapowderblue));
  fcolorlookup.addobject('clapurple', tobject(clapurple));
  fcolorlookup.addobject('clared', tobject(clared));
  fcolorlookup.addobject('clarosybrown', tobject(clarosybrown));
  fcolorlookup.addobject('claroyalblue', tobject(claroyalblue));
  fcolorlookup.addobject('clasaddlebrown', tobject(clasaddlebrown));
  fcolorlookup.addobject('clasalmon', tobject(clasalmon));
  fcolorlookup.addobject('clasandybrown', tobject(clasandybrown));
  fcolorlookup.addobject('claseagreen', tobject(claseagreen));
  fcolorlookup.addobject('claseashell', tobject(claseashell));
  fcolorlookup.addobject('clasienna', tobject(clasienna));
  fcolorlookup.addobject('claskyblue', tobject(claskyblue));
  fcolorlookup.addobject('claslateblue', tobject(claslateblue));
  fcolorlookup.addobject('claslategray', tobject(claslategray));
  fcolorlookup.addobject('claslategrey', tobject(claslategrey));
  fcolorlookup.addobject('clasnow', tobject(clasnow));
  fcolorlookup.addobject('claspringgreen', tobject(claspringgreen));
  fcolorlookup.addobject('clasteelblue', tobject(clasteelblue));
  fcolorlookup.addobject('claviolet', tobject(claviolet));
  fcolorlookup.addobject('clathistle', tobject(clathistle));
  fcolorlookup.addobject('clatan', tobject(clatan));
  fcolorlookup.addobject('clatomato', tobject(clatomato));
  fcolorlookup.addobject('claturquoise', tobject(claturquoise));
  fcolorlookup.addobject('clawheat', tobject(clawheat));
  fcolorlookup.addobject('clawhitesmoke', tobject(clawhitesmoke));
  fcolorlookup.addobject('clayellowgreen', tobject(clayellowgreen));
  fcolorlookup.addobject('clared', tobject(clared));
  fcolorlookup.addobject('clablack', tobject(clablack));
  fcolorlookup.addobject('clablue', tobject(clablue));
  fcolorlookup.addobject('clagreen', tobject(clagreen));
  fcolorlookup.addobject('claaqua', tobject(claaqua));
  fcolorlookup.addobject('clayellow', tobject(clayellow));
  fcolorlookup.addobject('clafuchsia', tobject(clafuchsia));
  fcolorlookup.addobject('clawhite', tobject(clawhite));
  fcolorlookup.addobject('clalime', tobject(clalime));
  fcolorlookup.addobject('clasilver', tobject(clasilver));
  fcolorlookup.addobject('clagray', tobject(clagray));
  fcolorlookup.addobject('claolive', tobject(claolive));
  fcolorlookup.addobject('clanavy', tobject(clanavy));
  fcolorlookup.addobject('clapurple', tobject(clapurple));
  fcolorlookup.addobject('clateal', tobject(clateal));
  fcolorlookup.addobject('claorange', tobject(claorange));
  fcolorlookup.addobject('clamaroon', tobject(clamaroon));
  FColorLookup.Sort;
end;

finalization
begin
  FColorLookup.Free;
end;

{$ENDIF}
{$ENDIF}

end.
