Delphi 将WinApi.Windows.TextOut与擒纵机构一起使用时出现意外偏移

Delphi 将WinApi.Windows.TextOut与擒纵机构一起使用时出现意外偏移,delphi,canvas,textout,Delphi,Canvas,Textout,我正在尝试使用winapi TextOut方法将文本绘制到画布。当擒纵比为0、900、1800或2700时,这种方法效果相当好,但对于所有其他值,我会得到一个偏移误差(“跳跃”) 请运行附加的代码以查看问题。正如你所看到的,水平和垂直文本的绘制正如人们所期望的那样,但是第三条线绘制在错误的位置 一些问题: 显而易见的一点是:为什么会发生这种情况,我应该怎么做才能解决它 为什么定向没有效果?我在示例中输入了1234,但结果与我使用的值相同 (我们正在使用的代码是一个旧的“类似cad”的库的一部分。

我正在尝试使用winapi TextOut方法将文本绘制到画布。当擒纵比为0、900、1800或2700时,这种方法效果相当好,但对于所有其他值,我会得到一个偏移误差(“跳跃”)

请运行附加的代码以查看问题。正如你所看到的,水平和垂直文本的绘制正如人们所期望的那样,但是第三条线绘制在错误的位置

一些问题:

  • 显而易见的一点是:为什么会发生这种情况,我应该怎么做才能解决它
  • 为什么定向没有效果?我在示例中输入了1234,但结果与我使用的值相同
  • (我们正在使用的代码是一个旧的“类似cad”的库的一部分。这个库的作者已经离开了地球,所以我们不能请他帮助我们,用一个新的更现代的库替换这个库将是一个很大的麻烦。我试着分离相关的代码)

    -

    -

  • 似乎取决于使用的字体。F.ex。使用
    Tahoma
    而不是
    Courier New
    字体大小为21或27时,问题不可见。对于如何更正
    Courier New
    ,我没有任何建议

  • 取决于
    图形模式

  • 默认情况下,图形模式与GM_兼容(值1)和says的文档(emphasis mine):

    LF擒纵机构-指定擒纵机构之间的角度(以十分之一度为单位) 擒纵机构矢量和设备的x轴。擒纵矢量 与一行文本的基线平行。 当图形模式设置为GM_兼容时,lfEscapement同时指定擒纵机构和方向。你应该设置 lfEscapement和lfOrientation的值相同

    我认为这是误导,因为
    lfOrientation
    的设置没有任何效果

    进一步:

    LFORTIONATION-指定两个方向之间的角度(以十分之一度为单位) 每个字符的基线和设备的x轴

    在我的测试中,在
    GM_COMPATIBLE
    的情况下,我发现
    lfOrientation
    是否设置没有区别(正如您所述)

    但是,在
    gmu-ADVANCED
    模式下,设置
    lfOrientation
    肯定会影响角色方向

    使用
    SetGraphicsMode(C.Handle,GraphicsMode)以更改图形模式

    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
    
    type
      TFaceName = string[LF_FACESIZE];
      TExtendedFont = class(TObject)
      private
        LogFont: TLOGFONTA;
        FHandle: HFONT;
      public
        constructor Create;
        destructor Destroy; override;
        procedure UpdateHandle;
    
        property Handle: HFONT read FHandle;
      end;
    
      TForm1 = class(TForm)
        Button1: TButton;
        Edit1: TEdit;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        procedure DrawText(X,Y,Escapement : integer; T : string);
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    procedure TExtendedFont.UpdateHandle;
    var
      TmpHandle: HFONT;
    begin
      TmpHandle := CreateFontIndirectA(LogFont);
      DeleteObject(FHandle);
      FHandle := TmpHandle;
    end;
    
    constructor TExtendedFont.Create;
    begin
      inherited Create;
      GetObject(GetStockObject(DEFAULT_GUI_FONT), SizeOf(LogFont), @LogFont);
      LogFont.lfFaceName := 'Courier New';
      FHandle := CreateFontIndirectA(LogFont);
    end;
    
    destructor TExtendedFont.Destroy;
    begin
      DeleteObject(FHandle);
      inherited Destroy;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Canvas.FillRect(ClientRect);
      DrawText(150,150,0,'No escapement (0°)');
      DrawText(150,150,1800,'180°');
      DrawText(150,150,2700,'270°');
      DrawText(150,150,StrToIntDef(Edit1.Text,0),'With escapement');
    end;
    
    procedure TForm1.DrawText(X,Y,Escapement : integer; T : string);
    var
      C : TCanvas;
      FLogFont : TExtendedFont;
    begin
      C := Canvas;
    
      FLogFont := TExtendedFont.Create;
      try
        FLogFont.LogFont.lfHeight := 21; //With a value of 20 or less, the problem disappears
        FLogFont.LogFont.lfEscapement := Escapement;
        FLogFont.LogFont.lfOrientation := 1234; //It doesn't seem to matter what value I use here
        FLogFont.UpdateHandle;
    
        SetTextAlign(C.Handle,TA_BOTTOM+TA_LEFT+TA_NOUPDATECP);
    
        C.Font.Handle := FLogFont.Handle;
        SetBkMode(C.Handle, TRANSPARENT);
    
        C.Pixels[X,Y] := clRed; //This SHOULD be the lower left corner of the text
        WinApi.Windows.TextOut(C.Handle,X,Y,PChar(T), Length(T));
      finally
        C.Font.Handle := 0;
        FLogFont.Free;
      end;  // try/finally
    end;
    
    end.
    
    object Form1: TForm1
      Left = 0
      Top = 0
      Caption = 'Form1'
      ClientHeight = 336
      ClientWidth = 635
      Color = clBtnFace
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clWindowText
      Font.Height = -11
      Font.Name = 'Tahoma'
      Font.Style = []
      OldCreateOrder = False
      PixelsPerInch = 96
      TextHeight = 13
      object Button1: TButton
        Left = 63
        Top = 8
        Width = 75
        Height = 25
        Caption = 'Draw text'
        TabOrder = 0
        OnClick = Button1Click
      end
      object Edit1: TEdit
        Left = 8
        Top = 8
        Width = 49
        Height = 21
        TabOrder = 1
        Text = '1'
      end
    end
    
    program Project1;
    
    uses
      Vcl.Forms,
      Unit1 in 'Unit1.pas' {Form2};
    
    {$R *.res}
    
    begin
      Application.Initialize;
      Application.MainFormOnTaskbar := True;
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end.