delphi 文本布局类不适用于打印机

zi8p0yeb  于 2023-08-04  发布在  其他
关注(0)|答案(2)|浏览(117)

我用的是 Delphi 11火猴我尝试只使用TPrinter.Canvas方法进行一些打印(文本、形状、位图)。一切正常。
但是还有一个叫做TTextLayout的东西,它对格式化文本非常有用。然而,当我尝试使用这个类打印时,我看不到任何文本。另一方面,在常规画布上绘制,如使用TTextLayoutTPaintBox.CanvasTBitmap.Canvas,效果很好。
下面是我的代码(TButton(×3),TPaintBoxTPrintDialogTSaveDialog):

uses
  FMX.TextLayout;

procedure TForm1.Button1Click(Sender: TObject);
begin
  PaintBox1.Repaint;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  DoPrint;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  DrawToFile;
end;

procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
begin
  DrawSomething(Canvas);
end;

procedure TForm1.DoPrint;
begin
  if PrintDialog1.Execute then begin
    Printer.ActivePrinter.SelectDPI(1200, 1200);
    Printer.Orientation := TPrinterOrientation.poLandscape;
    Printer.BeginDoc;
    try
      if Printer.Canvas.BeginScene() then try
        DrawSomething(Printer.Canvas);
      finally
        Printer.Canvas.EndScene;
      end;
    finally
      Printer.EndDoc;
    end;
  end;
end;

procedure TForm1.DrawToFile;
var
  Bmp: TBitmap;
begin
  if SaveDialog1.Execute then begin
    Bmp := TBitmap.Create(300, 300);
    try
      Bmp.Clear(TAlphaColors.White);
      if Bmp.Canvas.BeginScene() then try
        DrawSomething(Bmp.Canvas);
      finally
        Bmp.Canvas.EndScene;
      end;
      Bmp.SaveToFile(SaveDialog1.FileName);
    finally
      Bmp.Free;
    end;
  end;
end;

procedure TForm1.DrawSomething(Canvas: TCanvas);
var
  tl: TTextLayout;
  BoldFont: TFont;
  StrokeBrush: TStrokeBrush;
begin
  tl := nil;
  BoldFont := nil;
  StrokeBrush := nil;
  try
    tl := TTextLayoutManager.DefaultTextLayout.Create();
    BoldFont := TFont.Create;
    StrokeBrush := TStrokeBrush.Create(TBrushKind.Solid, TAlphaColors.Blue);

    BoldFont.SetSettings('Arial', 96, TFontStyleExt.Create([TFontStyle.fsBold]));

    StrokeBrush.Thickness := 10.0;

    tl.BeginUpdate;
    try
      tl.TopLeft := TPointF.Create(0,0);
      tl.MaxSize := TPointF.Create(300, 300);
      tl.Text := 'Lorem'#$0A'ipsum';
      tl.WordWrap := True;
      tl.HorizontalAlign := TTextAlign.Leading;
      tl.VerticalAlign := TTextAlign.Leading;
      tl.Color := TAlphaColors.Black;
      tl.Font.SetSettings('Arial', 96, TFontStyleExt.Create([]));
      tl.Opacity := 1.0;
      tl.AddAttribute(TTextRange.Create(3, 5), TTextAttribute.Create(BoldFont, TAlphaColors.Black));
    finally
      tl.EndUpdate;
    end;
    tl.RenderLayout(Canvas);
    Canvas.DrawLine(TPointF.Create(10, 20), TPointF.Create(110, 120), 1.0, StrokeBrush);
  finally
    StrokeBrush.Free;
    BoldFont.Free;
    tl.Free;
  end;
end;

字符串
Button1重新绘制TPaintBoxButton2打印,Button3创建图像文件。以下是我的结果:
适用范围:Application screenshot
Microsoft Print to PDF(从实际打印机获得相同的结果):PDF screenshot
图像文件:Image file
正如您所看到的,TControl.CanvasTBitmap.Canvas在呈现文本布局方面都没有问题。只有打印机的画布不显示文本。我做错什么了吗?

lfapxunr

lfapxunr1#

它不起作用,因为TTextLayout.RenderLayout(Canvas)调用TTextLayoutD2D.DoDrawLayoutTTextLayoutD2D.DoDrawLayout需要TCanvasD2D画布,而打印机画布可能不是:

if (ACanvas = nil) or not (ACanvas is TCanvasD2D) or Text.IsEmpty or (FLayout = nil) then
    Exit;

字符串
这可能会导致Embarcadero的bug报告,但同时,一种解决方法是将文本绘制到TBitMap(就像您已经使用Button3一样),并将该位图绘制到打印机画布:

procedure TForm1.DoPrint;
var Bmp: TBitmap;
begin
  if PrintDialog1.Execute then begin
    Bmp:=TBitmap.Create(300,300);
    try
      Bmp.Clear(TAlphaColors.White);
      if Bmp.Canvas.BeginScene() then try
    DrawSomething(Bmp.Canvas);
      finally
    Bmp.Canvas.EndScene;
      end;
    finally
      Printer.ActivePrinter.SelectDPI(1200, 1200);
      Printer.Orientation := TPrinterOrientation.poLandscape;
      Printer.BeginDoc;
      try
        if Printer.Canvas.BeginScene() then try
          Printer.Canvas.DrawBitmap(Bmp,Bmp.Bounds,Bmp.Bounds,1)
        finally
          Printer.Canvas.EndScene;
        end;
      finally
        Printer.EndDoc;
      end;
      Bmp.Free;
    end;
  end;
end;


NB:不是try...finally的常规用户,我不知道我是否有最好的安排。

relj7zay

relj7zay2#

感谢@Philip J。Rayment的回答,我设法找到了另一个解决方案。
TTextLayout的创建有问题:DefaultTextLayout返回运行平台的默认文本布局类(documentation)。这通常与打印机所需的不同。在这种情况下,我们需要使用TTextLayoutManager.TextLayoutByCanvas()方法。

// ...
begin
  tl := nil;
  BoldFont := nil;
  StrokeBrush := nil;
  try
    // tl := TTextLayoutManager.DefaultTextLayout.Create(); -- wrong
    tl := TTextLayoutManager.TextLayoutByCanvas(Canvas.ClassType).Create(); // correct
    BoldFont := TFont.Create;
    StrokeBrush := TStrokeBrush.Create(TBrushKind.Solid, TAlphaColors.Blue);

    BoldFont.SetSettings('Arial', 96, TFontStyleExt.Create([TFontStyle.fsBold]));
// ...

字符串
但是,不同类型的画布的文本布局行为不同。例如,GDI+不支持文本属性(一个文本中的不同文本格式):
| 我的解决方案没有位图| My solution without bitmap |
| --| ------------ |
|

的|

|
布局也有不同的填充,因此,启用自动换行和太小的MaxSize,你可能会得到相当意想不到的结果(这次删除格式):
| 我的解决方案没有位图| My solution without bitmap |
| --| ------------ |
|

的|

|
使用相同的设置,但不同的TTextLayout类。
这有点尴尬,因为人们会期望后代仅仅充当“ Package 器”并产生相同的结果。
所以,我认为菲利普的解决方案比这个更好。尽管如此,DefaultTextLayout仍然可能在不同的平台上返回不同的文本布局。也许有一天,Embarcadero会修复TTextLayout的行为。

相关问题