Custom HTML canvas example

<< Click to Display Table of Contents >>

Navigation:  Font engine >

Custom HTML canvas example

Example of HTML canvas which use library font class for text measurement and rendering (only API used from DX is FillGeomatry).

 

type
  THtFontOTF = class(THtFont)
  private
    procedure FreePathCache;
  protected
    procedure GetMetrics;
  public
    Font: TOTFFont;
    PathCache: array of THtPath;
    constructor Create(const AOwner: THtFontCollection; const AFamily: stringconst AHeight: single; AStyle: THtFontStyles); override;
  end;
 
  THtCanvasOTF = class(THtCanvasDX)
  public
    class function FontClass: THtFontClass; override;
    class function MeasureString(const s: stringconst AFont: THtFont; const TextStyle: THtTextStyle;
      const Scale: single = 0; AFirstChar: integer = 1; ALength: integer = -1): single; override;
    procedure DrawString(const s: stringconst R: TRectF; {$IFDEF AUTOREFCOUNT}[unsafe]{$ENDIF} const Font: THtFont;
      const TextStyle: THtTextStyle;  AFirstChar: integer = 1; ALength: integer = -1); override;
    class function GetFlags: THtCanvasFlags; override;
  end;
 
constructor THtFontOTF.Create(const AOwner: THtFontCollection;
  const AFamily: stringconst AHeight: single; AStyle: THtFontStyles);
begin
  inherited;
  GetMetrics;
end;
 
procedure THtFontOTF.GetMetrics;
var B: VCL.Graphics.TBitmap;
    T: TBytes;
    n: integer;
    ST: TBytesStream;
begin
  B := VCL.Graphics.TBitmap.Create;
  try
    B.Width := 1;
    B.Height := 1;
    FreeandNil(Font);
    B.Canvas.Font.Name := Family;
    if hfsItalic in Style then
      B.Canvas.Font.Style := B.Canvas.Font.Style + [fsItalic];
    if hfsBold in Style then
      B.Canvas.Font.Style := B.Canvas.Font.Style + [fsBold];
    B.Canvas.Font.Size := 10;
    n := GetFontData(B.Canvas.Handle, 0, 0, nil, 0);
    if n = -1 then
      RaiseLastOsError;
    SetLength(T, n);
    n := GetFontData(B.Canvas.Handle, 0, 0, pointer(T), n);
    ST := TBytesStream.Create(T);
    try
      Font := TOTFFont.Create;
      Font.LoadfromStream(ST);
    finally
      ST.Free
    end;
    Self.MinCharWidth := Font.MeasureString('i', Height);
    Self.Ascent := Font.Ascent(Height);
    Self.Descent := Font.Descent(Height);
    Self.LineHeight := Self.Ascent + Self.Descent;
    Self.SpaceWidth := Font.MeasureString(' ', Height);
    FreePathCache;
    SetLength(PathCache, Font.GlyphTable.Count);
    FillChar(pointer(PathCache)^, Length(PathCache) * sizeof(THtPath), 0);
  finally
    B.Free
  end;
end;
 
procedure THtFontOTF.FreePathCache;
var i: integer;
begin
  for i := 0 to High(PathCache) do
    if PathCache[i] <> nil then
      PathCache[i].Free;
  SetLength(PathCache, 0);
end;
 
 
procedure THtCanvasOTF.DrawString(const s: stringconst R: TRectF;
  const Font: THtFont; const TextStyle: THtTextStyle; AFirstChar,
  ALength: integer);
var L: THtLayout;
    Scale: single;
    X, W: single;
    i, Offs: integer;
    B: THtBrush;
    F: THtFontOTF;
    M: THtMatrixData;
begin
  if ALength < 0 then
    ALength := Length(s);
  F := THtFontOTF(Font);
  F.Font.CreateLayout(copy(s, AFirstChar, ALength), Font.Height, L);
  F.Font.PrepareLayout(L);
  SaveState;
  M.Init(1, 0, 0, 1, R.Left, R.Top + round(F.Font.Ascent(F.Height) - F.Font.Ascent(F.Height)));
  FCurrentMatrix.Multiply(M);
  FContext.SetTransform(TD2D_MATRIX_3X2_F(FCurrentMatrix.Data));
  B := GetBrush(TextStyle.Color);
  Scale := Font.Height / F.Font.FontHeader.Header.UnitsPerEM;
  for i := 0 to High(L.Glyphs) do
  begin
    if F.PathCache[L.Glyphs[i].GID] = nil then
    begin
      F.PathCache[L.Glyphs[i].GID] := PathClass.Create;
      F.Font.GlyphTable.Glyphs[L.Glyphs[i].GID].AsPath(
       F.Font.GlyphTable, 0, 0, F.PathCache[L.Glyphs[i].GID], Scale);
    end;
  end;
 
  W := L.Width;
  if L.RTL then
  begin
    M.Init(1, 0, 0, 1, W, 0);
    FCurrentMatrix.Multiply(M);
    for i := 0 to High(L.Glyphs) do
    begin
      Offs := L.Glyphs[i].DX + L.Glyphs[i].Width;
      M.Init(1, 0, 0, 1, -Offs * Scale, 0);
      FCurrentMatrix.Multiply(M);
      FContext.SetTransform(TD2D_MATRIX_3X2_F(FCurrentMatrix.Data));
      FillPath(B, F.PathCache[L.Glyphs[i].GID]);
    end;
  end else
  begin
    for i := 0 to High(L.Glyphs) do
    begin
      Offs := L.Glyphs[i].DX;
      if i > 0 then
        Offs := Offs + L.Glyphs[i-1].Width;
      M.Init(1, 0, 0, 1, Offs * Scale, 0);
      FCurrentMatrix.Multiply(M);
      FContext.SetTransform(TD2D_MATRIX_3X2_F(FCurrentMatrix.Data));
      FillPath(B, F.PathCache[L.Glyphs[i].GID]);
      end;
  end;
  RestoreState;
  if hfsUnderline in F.Style then
    DrawLine(P, R.Left, R.Top + F.Font.Underline(F.Height), R.Right, R.Top + F.Font.Underline(F.Height));
end;
 
class function THtCanvasOTF.FontClass: THtFontClass;
begin
  Result := THtFontOTF;
end;
 
class function THtCanvasOTF.MeasureString(const s: stringconst AFont: THtFont;
  const TextStyle: THtTextStyle; const Scale: single; AFirstChar,
  ALength: integer): single;
begin
  if ALength < 0 then
    ALength := Length(s);
  Result := THtFontOTF(AFont).Font.MeasureString(copy(s, AFirstChar, ALength), AFont.Height);
end;
 
class function THtCanvasOTF.GetFlags: THtCanvasFlags;
begin
  Result := [hcfScaleCanvasDrawing, hcfCacheTextWidth, hcfSupportsSimpleLayouts, hcfFullRepaint];
end;