Link to home
Start Free TrialLog in
Avatar of VSmolensky
VSmolensky

asked on

Vertical shift in Unicode fonts

In my Delphi project for Windows, I often render Japanese text on TCanvas of an image. Unfortunately, the vertical position of the rendered characters depends on the font family. When the font is MS Mincho or MS Gothic, it's positioned perfectly well. When it's Tahoma, the text goes a bit down. When it's Yu Mincho or Kozuka Gothic, it goes down a lot. So it's really difficult to align the text for all possible fonts. I need to calculate some parameter for every font to move the position upwards.

Some experts from another forum recommended to use the parameters from TTextMetric. There are several: tmAscent, tmDescent, tmExternalLeading, tmInternalLeading, etc. Theoretically, a certain combination of them and the font size in pixels should give me the desired value. But whatever I tried didn't work so far.

There must be a way to do it. Any ideas?
Avatar of Sinisa Vuk
Sinisa Vuk
Flag of Croatia image

Ok. You want  to draw some text on Canvas. You surely needs TextMetric  structure. You did not post your function, but here are few articles wich might help you.
Draw multi fonts in single line or do
some calc with TextMetric. You did not write if you need some alignment or what?
Avatar of VSmolensky
VSmolensky

ASKER

My function is very simple (it's FireMonkey):

with MyImage.Bitmap.Canvas do
begin
 BeginScene;
 Font.Family := 'MS Mincho';
 Font.Style := [];
 Font.Size := 60;
 FillText (RectF (0, 0, Width-1, Height-1), '字' , False , 1 , [] , TTextAlign.Leading, TTextAlign.Leading);
 EndScene;
end;

I only need the characters to be drawn in the same area, not lower or higher, whatever font is chosen. At the moment this is what I have:

User generated image
Thanks for the links, anyway.
If it is for windows... I'm able to get TextMetic ...hee is my example:
procedure MyCanvasFillText(Canvas: TCanvas; const ARect: TRectF; const AText: string;
  const WordWrap: Boolean; const AOpacity: Single; const Flags: TFillTextFlags;
  const ATextAlign, AVTextAlign: TTextAlign);
var
  Layout: TTextLayout;
begin
  Layout := TTextLayoutManager.TextLayoutByCanvas(Canvas.ClassType).Create(Canvas);
  try
    Layout.BeginUpdate;
    Layout.TopLeft := ARect.TopLeft;
    Layout.MaxSize := PointF(ARect.Width, ARect.Height);
    Layout.Text := AText;
    Layout.WordWrap := WordWrap;
    Layout.Opacity := AOpacity;
    Layout.HorizontalAlign := ATextAlign;
    Layout.VerticalAlign := AVTextAlign;
    Layout.Font := Canvas.Font;
    Layout.Color := Canvas.Fill.Color;
    Layout.RightToLeft := TFillTextFlag.RightToLeft in Flags;
    //added line to original FillText method
    Layout.Padding.Top := - GetTextExternalLeading(Canvas.Font.Family, Canvas.Font.Size,
      Canvas.Font.Style, AText);
    Layout.EndUpdate;
    Layout.RenderLayout(Canvas);
  finally
    FreeAndNil(Layout);
  end;
end;

....

with dst.Canvas do
    begin
     BeginScene;
     Clear(TAlphaColorRec.White);
     Font.Family := 'Tahoma'; //'MS Mincho';
     Font.Style := [];
     Font.Size := 60;
     Fill.Color:=TAlphaColorRec.red;
     MyCanvasFillText(dst.Canvas, RectF (0, 0, Width-1, Height-1), '字' , False , 1 , [] , TTextAlign.Leading, TTextAlign.Leading);
     Label7.Text := r.Top.ToString;
     EndScene;
    end;

Open in new window


I'm using external unit for getting TextMetric info:
unit uTxtInfo;

interface

{$IFDEF MSWINDOWS}
uses Windows, VCL.Graphics, System.UITypes;
{$ELSE}
uses System.UITypes;
{$ENDIF}

function GetTextExternalLeading(FontName: String; FontSize: Single; FontStyle: System.UITypes.TFontStyles;
  const AText: String): Integer;

implementation

{$IFDEF MSWINDOWS}

function GetTextExternalLeading(FontName: String; FontSize: Single; FontStyle: System.UITypes.TFontStyles;
  const AText: String): Integer;
var
  bmp: TBitmap;
  tm: TEXTMETRIC;
begin
  bmp := TBitmap.Create;
  try
    bmp.Width := 100;
    bmp.Height := 100;
    bmp.Canvas.Font.Name := FontName;
    bmp.Canvas.Font.Size := Trunc(FontSize);
    bmp.Canvas.Font.Style := FontStyle;
    GetTextMetrics(bmp.Canvas.Handle, tm);
    //output
    Result := tm.tmExternalLeading + tm.tmInternalLeading;
  finally
    bmp.Free;
  end;
end;
{$ELSE}
function GetTextExternalLeading(FontName: String; FontSize: Single; FontStyle: System.UITypes.TFontStyles;
  const AText: String): Integer;
begin
  Result := 0;
end;
{$ENDIF}

end.

Open in new window

(uTxtInfo.pas)

Works for one character.. but for many - think that you should calculate each character separately and use Min of them as a padding value.

I follow this info about drawing text layout...
Thank you for your code, but I already know how to get tmExternalLeading and tmInternalLeading from TextMetric. I had tried it before asking my question, and I wrote about it. This didn't solve my problem, the characters are still not aligned properly. A solution, as far as I can see, is supposed to be much more sophisticated.
ASKER CERTIFIED SOLUTION
Avatar of VSmolensky
VSmolensky

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I've posted a solution recommended to me at another forum. Maybe it'll be helpful for somebody else here. Please let this question stay.
Thanks. surely will help someone. I'm thinking about that method but then seems to me that would not fit as a solution.