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?
VSmolenskyAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Sinisa VukCommented:
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?
0
VSmolenskyAuthor Commented:
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:

FourFonts.png
Thanks for the links, anyway.
0
Sinisa VukCommented:
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...
0
Cloud Class® Course: CompTIA Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

VSmolenskyAuthor Commented:
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.
0
VSmolenskyAuthor Commented:
Luckily, some expert in another forum gave me a great idea. In FireMonkey there's a component called TPathData which can deal with a text as with a kind of geometrical figure. Here's how it works:

procedure RenderKanji(TheImage: TImage; TheCharacter: string; X, Y: Single);
var PD: TPathData;
    DestRect: TRectF;
    W, H: Single;
    DoScene: Boolean;
begin
with TheImage.Bitmap.Canvas do
begin
 PD := TPathData.Create;
 DestRect := RectF (X, Y, Width-1, Height-1);
 try
  BeginScene;
  if TextToPath (PD, DestRect, TheCharacter, False, TTextAlign.Leading, TTextAlign.Leading)  then
    begin
     W := (Font.Size-PD.GetBounds.Width) / 2;
     H := (Font.Size-PD.GetBounds.Height) / 2;
     PD.Translate (X+W-PD.GetBounds.Left, Y+H-PD.GetBounds.Top);
     FillPath (PD, 1);
    end;
 finally
  EndScene;
  PD.Free;
 end;
 end;
end;
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
VSmolenskyAuthor Commented:
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.
0
Sinisa VukCommented:
Thanks. surely will help someone. I'm thinking about that method but then seems to me that would not fit as a solution.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.