I assume this is because the text will rotate around the rectangle's TopLeft position
No, the rotation is centered around the current origin of the canvas. By default, that is coordinate 0, 0
, but could be altered by the currently set deformation matrix. The typical way to go is: choose a rotation center, move the origin to that center, rotate, move back to the changed origin, and then draw. See TControl.MatrixChanged
for reference. But there are many other ways.
Hereby an example of how to paint text from the lower left to the upper right within a form:

procedure TForm1.FormPaint(Sender: TObject; Canvas: TCanvas;
const ARect: TRectF);
var
Angle: Single;
R: TRectF;
S: String;
H: Single;
Matrix: TMatrix;
begin
Canvas.Fill.Color := TAlphaColors.Black;
Angle := -ArcTan2(ClientHeight, ClientWidth);
R := ClientRect;
S := 'Text from bottom-left...';
H := Canvas.TextHeight(S);
Matrix := CreateRotationMatrix(Angle);
Matrix.m31 := Sin(Angle) * (ClientHeight - H);
Matrix.m32 := ClientHeight * (1 - Cos(Angle));
Canvas.SetMatrix(Matrix);
Canvas.FillText(R, S, False, 1, [], TTextAlign.taLeading,
TTextAlign.taTrailing);
S := '...to top-right';
Matrix.m31 := ClientWidth * (1 - Cos(Angle)) + Sin(Angle) * H;
Matrix.m32 := -Sin(Angle) * ClientWidth;
Canvas.SetMatrix(Matrix);
Canvas.FillText(R, S, False, 1, [], TTextAlign.taTrailing,
TTextAlign.taLeading);
end;
Update:
This code does not yet take an already shifted origin into account.
In response to your comment, the following code draws text from coordinate 50, 100
down, 90° rotated around that point, using the method explained above, on a PaintBox which is arbitrarily positioned on the form.
procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
const
S = 'Hello World';
var
R: TRectF;
OriginalMatrix: TMatrix;
ShiftMatrix: TMatrix;
RotationMatrix: TMatrix;
ShiftBackMatrix: TMatrix;
Matrix: TMatrix;
begin
PaintBox1.Canvas.Fill.Color := TAlphaColors.Black;
R.Right := 50;
R.Bottom := 100;
R.Left := R.Right - 5000;
R.Top := R.Bottom - 5000;
OriginalMatrix := PaintBox1.Canvas.Matrix;
ShiftMatrix := IdentityMatrix;
ShiftMatrix.m31 := -R.Right;
ShiftMatrix.m32 := -R.Bottom;
RotationMatrix := CreateRotationMatrix(DegToRad(-90));
ShiftBackMatrix := IdentityMatrix;
ShiftBackMatrix.m31 := R.Right;
ShiftBackMatrix.m32 := R.Bottom;
Matrix := MatrixMultiply(RotationMatrix, ShiftBackMatrix);
Matrix := MatrixMultiply(ShiftMatrix, Matrix);
Matrix := MatrixMultiply(Matrix, OriginalMatrix);
PaintBox1.Canvas.SetMatrix(Matrix);
PaintBox1.Canvas.FillText(R, S, False, 1, [], TTextAlign.taTrailing,
TTextAlign.taTrailing);
PaintBox1.Canvas.SetMatrix(OriginalMatrix);
end;
Which can be reduced to:
procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
const
S = 'Hello World';
var
R: TRectF;
SaveMatrix: TMatrix;
Matrix: TMatrix;
begin
PaintBox1.Canvas.Fill.Color := TAlphaColors.Black;
R := RectF(-Canvas.TextWidth(S), -Canvas.TextHeight(S), 0, 0);
SaveMatrix := PaintBox1.Canvas.Matrix;
Matrix := CreateRotationMatrix(DegToRad(-90));
Matrix.m31 := 50;
Matrix.m32 := 100;
PaintBox1.Canvas.MultyMatrix(Matrix);
PaintBox1.Canvas.FillText(R, S, False, 1, [], TTextAlign.taTrailing,
TTextAlign.taTrailing);
PaintBox1.Canvas.SetMatrix(SaveMatrix);
end;
Which in turn evolves into this general routine:
procedure DrawRotatedText(Canvas: TCanvas; const P: TPointF; RadAngle: Single;
const S: String; HTextAlign, VTextAlign: TTextAlign);
var
W: Single;
H: Single;
R: TRectF;
SaveMatrix: TMatrix;
Matrix: TMatrix;
begin
W := Canvas.TextWidth(S);
H := Canvas.TextHeight(S);
case HTextAlign of
TTextAlign.taCenter: R.Left := -W / 2;
TTextAlign.taLeading: R.Left := 0;
TTextAlign.taTrailing: R.Left := -W;
end;
R.Width := W;
case VTextAlign of
TTextAlign.taCenter: R.Top := -H / 2;
TTextAlign.taLeading: R.Top := 0;
TTextAlign.taTrailing: R.Top := -H;
end;
R.Height := H;
SaveMatrix := Canvas.Matrix;
Matrix := CreateRotationMatrix(RadAngle);
Matrix.m31 := P.X;
Matrix.m32 := P.Y;
Canvas.MultyMatrix(Matrix);
Canvas.FillText(R, S, False, 1, [], HTextAlign, VTextAlign);
Canvas.SetMatrix(SaveMatrix);
end;
procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
begin
PaintBox1.Canvas.Fill.Color := TAlphaColors.Black;
DrawRotatedText(PaintBox1.Canvas, PointF(50, 100), DegToRad(-90),
'Hello world', TTextAlign.taTrailing, TTextAlign.taTrailing);
end;